87 lines
85 KiB
Markdown
87 lines
85 KiB
Markdown
# Control-Loop Atlas
|
||
|
||
This atlas is the high-level map for RT3 1.06. It is intentionally broader than the function map:
|
||
each section explains who owns a loop, where it starts, what dispatches it, which state blocks
|
||
anchor it, and where control is handed to neighboring subsystems.
|
||
|
||
## CRT and Process Startup
|
||
|
||
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and `app_bootstrap_main` at `0x00484440`.
|
||
- Trigger/Cadence: single process startup path before shell or engine services exist.
|
||
- Key Dispatchers: `startup_init_tls_state`, `startup_init_file_handle_table`, `startup_run_init_callback_table`, `__setenvp`, `startup_build_argv`, `___crtGetEnvironmentStringsA`, then `app_bootstrap_main`.
|
||
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
|
||
- Subsystem Handoffs: exits the generic CRT path at `app_bootstrap_main`, which becomes the first RT3-owned coordinator.
|
||
- Evidence: `artifacts/exports/rt3-1.06/startup-call-chain.md`, `artifacts/exports/rt3-1.06/function-map.csv`.
|
||
- Open Questions: exact ownership boundary between compiler CRT shims and the first game-owned bootstrap helper; whether any nontrivial startup callbacks seed game globals before `app_bootstrap_main`.
|
||
|
||
## Bootstrap and Shell Service Bring-Up
|
||
|
||
- Roots: `app_bootstrap_main` at `0x00484440`, `bootstrap_init_shell_window_services` at `0x004840e0`, and `shell_install_global_controller` at `0x0051ff90`.
|
||
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
|
||
- Key Dispatchers: `app_bootstrap_main`, `bootstrap_init_shell_window_services`, `shell_install_global_controller`, `shell_service_pump_iteration`, graphics config load or default-init helpers, runtime capability probes, and early shell service initializers under the `0x004610..0x0053f0..` branch.
|
||
- State Anchors: global shell controller pointer `0x006d4024`, sibling display/runtime globals under `0x006d402c` and `0x006d4030`, and host capability flags gathered by `bootstrap_probe_system_profile`.
|
||
- Subsystem Handoffs: after the global shell controller has been installed the bootstrap path enters the repeating `shell_service_pump_iteration` loop, which services shell-state work and hands each iteration down into the controller frame path; when the shell lifetime ends this same bootstrap path tears the shell bundle down and returns to `app_bootstrap_main`.
|
||
- Evidence: `startup-call-chain.md`, function-map rows for `app_bootstrap_main`, `bootstrap_init_shell_window_services`, `shell_install_global_controller`, `shell_service_pump_iteration`, `shell_load_graphics_config_or_init_defaults`, `shell_reset_display_runtime_defaults`, and related graphics setup helpers.
|
||
- Open Questions: whether gameplay or in-engine world stepping later escapes this bootstrap-owned shell loop entirely, or whether gameplay entry still rendezvous with the same outer coordinator before the shell bundle is destroyed.
|
||
|
||
## Shell UI Command and Deferred Work Flow
|
||
|
||
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
|
||
- Trigger/Cadence: event-driven UI command dispatch plus deferred-message queue flushing during shell activity.
|
||
- Key Dispatchers: `shell_dispatch_ui_command`, `shell_enqueue_deferred_work_message`, `shell_post_deferred_message_type5`, `shell_post_deferred_message_type6`, `shell_enqueue_deferred_message_type4`, `shell_enqueue_deferred_message_type1`.
|
||
- State Anchors: shell object at `0x0062be68`, queue roots around `[this+0x11369d]`, `[this+0x1136a1]`, and `[this+0x1136a5]`, global routing gates at `0x006d4034`, and the separate detail-panel controller rooted at `0x006d0818`.
|
||
- Subsystem Handoffs: routes into graphics config, scenario-text export, overlay generation, multiplayer UI, shell detail windows such as `EditorPanel.win` and `TrainDetail.win`, and presentation-facing deferred work later drained by `shell_service_frame_cycle`.
|
||
- Evidence: function-map shell rows around `0x00464410`, `0x004d4500`, `0x004ddbd0`, and `0x0051f1d0..0x0051f460`.
|
||
- Open Questions: whether the shell command dispatcher is also used by hotkeys or only by UI callback tables; the current `0x006d0818` detail-panel path looks shell-only and does not yet explain gameplay world entry.
|
||
|
||
## Presentation, Overlay, and Frame Timing
|
||
|
||
- Roots: the bootstrap-owned `shell_service_pump_iteration` at `0x00483f70`, the shell-state service pass `shell_state_service_active_mode_frame` at `0x00482160`, the installed global shell controller at `0x006d4024`, the pending frame-cycle owner `shell_service_frame_cycle` at `0x00520620`, and the frame-time history path under `shell_update_frame_time_history` at `0x0051fd70`.
|
||
- Trigger/Cadence: recurring bootstrap-owned shell service work once the active mode, controller window, and display runtime are live; special modal or content-building paths can also force immediate frame servicing inside that broader cadence.
|
||
- Key Dispatchers: `shell_service_pump_iteration`, `shell_state_service_active_mode_frame`, `shell_service_frame_cycle`, `shell_refresh_presentation_frame`, `shell_update_frame_time_history`, `shell_get_smoothed_frame_scalar`, `shell_set_gamma_ramp_scalar`, and overlay builders such as `shell_queue_single_world_anchor_overlay`, `shell_queue_world_anchor_overlay_list`, and `shell_queue_indexed_world_anchor_marker`.
|
||
- State Anchors: shell state at `0x006cec74`, active mode pointer `0x006cec78`, shell frame history ring at `0x006d403c`, display/runtime state inside the controller block near `[this+0x114282]` and `[this+0x11428a]`, presentation service pointer at `[this+0x0c]`, and the native controller window handle at `[this+0x00]`.
|
||
- Subsystem Handoffs: the bootstrap-owned pump runs shell-bundle polling and shell-state maintenance before the shell-state pass synchronizes active-mode helpers and modal-status work and dispatches the controller frame cycle, which then consumes world/object state for overlays and presentation refresh, uses the controller window handle for one-time `ShowWindow` and related UI interaction, and drains the deferred work queues at the end of the cycle.
|
||
- Evidence: function-map rows for `shell_service_pump_iteration`, `shell_state_service_active_mode_frame`, `shell_service_frame_cycle`, `shell_flush_deferred_work_queues`, frame history, gamma ramp, world-anchor overlay builders, and graphics runtime helpers.
|
||
- Open Questions: how simulation cadence rendezvous with the shell presentation cadence once gameplay takes over and whether gameplay stepping exits this shell-owned controller path entirely.
|
||
|
||
## Map and Scenario Content Load
|
||
|
||
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the first grounded world-entry branch `world_entry_transition_and_runtime_bringup` at `0x00443a50`, `shell_map_file_world_bundle_coordinator` at `0x00445de0`, reference-database setup via `map_bundle_open_reference_databases` at `0x00444dd0`, and narrower loaders such as `map_load_geographic_label_database` and `map_load_city_database`.
|
||
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`, map-scenario open paths, and scenario-text export batch commands.
|
||
- Key Dispatchers: `shell_map_file_entry_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_load_city_database`, `scenario_text_export_build_language_file`, `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 globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state allocated through `0x00530c80`, and geography tables rooted at `0x0062b2fc` and `0x0062b268`.
|
||
- Subsystem Handoffs: the shared `fileopt.win` dialog rooted at `0x004dc670` now looks like the shell-side selector above the two broad file coordinators. Its message handler sets `0x006d07f8` for the load or restore side or `0x006d07ec` for the save or package side before the detail-panel transition manager routes into `shell_map_file_entry_coordinator` or `shell_map_file_world_bundle_coordinator`. The former unresolved third flag at `0x006d07f0` is now accounted for too: it escapes into the standalone `SettingsWindow.win` path through `shell_open_settings_window` rather than another map or save verb. The broad coordinators now hand their interactive work through the shared `filerqst.win` helper at `0x004dd010`, and that helper gives the extension split a firmer shape. The paired editor-map path is now grounded as `.gmp` through load mode `4` and save mode `3`. The remaining non-editor families are no longer anonymous either: `.gmc` is the campaign-scenario branch, backed both by the campaign-screen resignation prompt on `0x006cec7c+0xc5` and by the numbered `%s%02d.gmc` save helper at `0x00517c70`; `.gmx` is the sandbox branch, backed by the shell-side `The briefing is not available in sandbox games.` restriction on `0x006cec7c+0x82`; and the default `.gms` branch is therefore the standalone scenario family. When a live runtime world is already active the same helper bypasses those non-runtime extensions and forces the `.smp` runtime-state branch instead. The auxiliary save-side 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 by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal reference-bundle path when that dataset object is absent.
|
||
- Evidence: function-map map/scenario rows, analysis-context exports for `0x00445ac0`, `0x00445de0`, `0x00443a50`, `0x00434300`, and `0x00444dd0`, plus objdump string and mode-table evidence for `.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 `0x00469d30`, together with localized string evidence from ids `3018` and `3898`.
|
||
- 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 `SettingsWindow.win`. The old broad extension question is mostly resolved: `.gmp` is the editor-map pair, `.gms` is the standalone scenario family, `.gmc` is the campaign-scenario family, `.gmx` is the sandbox family, and `.gmt` is at least bounded as an auxiliary preview-surface branch rather than another gameplay save family. The higher-value global question is no longer whether `0x00443a50` is world entry. It is where the long-lived simulation cadence takes over after this bring-up and whether that cadence still rendezvous with the shell-owned frame path or escapes into a separate gameplay loop.
|
||
|
||
## Multiplayer Session and Transport Flow
|
||
|
||
- Roots: `multiplayer_window_init_globals` at `0x004efe80`, `multiplayer_window_service_loop` at `0x004f03f0`, the Multiplayer.win session-event callback table built by `multiplayer_register_session_event_callbacks` at `0x0046a900`, the active session-event transport object at `0x006cd970`, the Multiplayer preview dataset object at `0x006cd8d8`, and the lower pending-template and text-stream helpers around `0x00597880..0x0059caf0`.
|
||
- Trigger/Cadence: shell-owned Multiplayer.win frame service plus event-driven session callbacks and repeated transport pump steps.
|
||
- Key Dispatchers: session-event wrappers for actions `1`, `2`, `4`, `7`, `8`; `multiplayer_register_session_event_callbacks`; `multiplayer_dispatch_requested_action`; `multiplayer_reset_preview_dataset_and_request_action`; `multiplayer_preview_dataset_service_frame`; `multiplayer_load_selected_map_preview_surface`; `multiplayer_flush_session_event_transport`; `multiplayer_transport_service_frame`; `multiplayer_transport_service_worker_once`; `multiplayer_transport_service_route_callback_tables`; `multiplayer_transport_service_status_and_live_routes`; `multiplayer_transport_text_stream_service_io`; `multiplayer_transport_dispatch_pending_template_node`; `multiplayer_transport_service_pending_template_dispatch_store`.
|
||
- State Anchors: live session globals at `0x006d40d0`, active session-event transport object at `0x006cd970`, status latch at `0x006cd974`, session-event mode latch at `0x006cd978`, retry counter at `0x006cd984`, preview dataset object at `0x006cd8d8`, preview-valid flag at `0x006ce9bc`, staged preview strings at `0x006ce670` and `[0x006cd8d8+0x8f48]`, Multiplayer.win backing block at `0x006d1270`, selector-view store root at `[transport+0xab4]`, selector-view generation counters at `[transport+0xab8]..[transport+0xac0]`, selector-view backing arrays around `[transport+0xad0]..[transport+0xae4]`, selector-view entry probe-request id at `[entry+0x50]`, live-state gate at `[entry+0x58]`, third-slot flag word at `[entry+0x64]`, probe-schedule tick at `[entry+0x68]`, last-success tick at `[entry+0x6c]`, pending-probe latch at `[entry+0x74]`, success generation at `[entry+0x78]`, consecutive-failure counter at `[entry+0x7c]`, rolling average at `[entry+0x80]`, recent sample window at `[entry+0x84..]`, bounded sample-count at `[entry+0x94]`, total-success count at `[entry+0x98]`, pending-template list at `[transport+0x550]`, dispatch store at `[worker+0x54c]`, and text-stream buffers rooted under `[worker+0x1c]`.
|
||
- Subsystem Handoffs: the Multiplayer.win initializer seeds the backing block at `0x006d1270`, later reset paths construct the separate preview dataset at `0x006cd8d8`, and the shell-owned active-mode frame services that dataset every frame through `multiplayer_preview_dataset_service_frame`. That preview side publishes roster and status controls through the Multiplayer window control paths, loads `.gmt` preview surfaces through `multiplayer_load_selected_map_preview_surface`, and is even reused by the map/save coordinator’s mode-`11` `.gmt` path when the dataset already exists. In parallel, `multiplayer_register_session_event_callbacks` allocates and registers the separate session-event transport object at `0x006cd970`. The shell-side bridge into that deeper transport cadence is now tighter: `multiplayer_window_service_loop` and neighboring reset or initializer branches call `multiplayer_flush_session_event_transport`, which forces one status flush and then drops into `multiplayer_transport_flush_and_maybe_shutdown`. That wrapper in turn runs `multiplayer_transport_service_frame`, the recurring pump that services one worker step through `multiplayer_transport_service_worker_once`, sweeps the transport-owned callback tables and field caches through `multiplayer_transport_service_route_callback_tables`, services both the auxiliary status route and the current live route through `multiplayer_transport_service_status_and_live_routes`, and then descends one layer farther into the shared GameSpy route helper `multiplayer_gamespy_route_service_frame` at `0x58d040`. The transport also owns a separate selector-view sidecar beneath that route cadence. `multiplayer_transport_ensure_selector_view_store` allocates the keyed selector-view store at `[transport+0xab4]`, `multiplayer_transport_find_selector_view_entry_by_name` resolves entries from that store, `multiplayer_transport_upsert_selector_name_entry` marks per-slot activity and flags inside each entry, and `multiplayer_transport_mark_selector_slot_views_dirty` plus `multiplayer_transport_reset_selector_view_entry_runtime_state` manage the dirty or refresh fields at `+0xa0`, `+0xa4`, and `+0xa8`. That selector-view maintenance path is now split more cleanly too. The recurring owner is `multiplayer_transport_service_selector_view_refresh_cycle`, which first runs the fast deferred-probe lane: `multiplayer_transport_collect_refreshable_selector_view_entries` walks the store through `multiplayer_transport_filter_insert_refreshable_selector_view_entry`, which now shows that `[entry+0x64]` is not a generic flag bucket but the third selector-slot flag word, parallel to `[entry+0x5c]` and `[entry+0x60]`. In that collector, the `g` and `a` mode-letter bits produced by `multiplayer_transport_parse_selector_mode_letters` become mask `0x0c` in the slot-local flag words, and any third-slot entry carrying those bits at `[entry+0x64]` is excluded from the refreshable set. Eligible entries then pass slot-aware retry timing on `[entry+0x68]`, `[entry+0x6c]`, `[entry+0x78]`, and `[entry+0x7c]`, after which the service loop schedules refresh probes through `multiplayer_transport_schedule_selector_view_entry_refresh_probe`. That fast lane is narrower now too: the entry-side match key `[entry+0x50]` is no longer just an opaque request field. The profile-key callback lanes feed `multiplayer_transport_parse_selector_view_probe_marker`, which decodes one local `X%sX|%d` marker into a request id plus companion value, and `multiplayer_transport_arm_selector_view_probe_tracking` stores those into `[entry+0x50]` and `[entry+0x54]` before arming the live probe gate at `[entry+0x58]`. The current-selector callback root at `0x59f8b0` is now bounded as well: it resolves and upserts the active selector name, optionally reuses a cached `username` marker to arm probe tracking immediately, then submits the same profile-key bundle with selector context and forwards that selector through callback slot `17`, with the status-route side able to force route-mode transitions `2 -> 3 -> 4` afterward. One layer lower, `multiplayer_transport_handle_profile_key_query_result` at `0x596970` now bounds the per-key result path itself. It treats `username` as the probe-marker field, `b_flags` as the selector mode-letter field, and `(END)` as a real sentinel that publishes a zeroed slot-`22` payload instead of a marker pair. The same helper also hashes the selector-name, key-name, and resolved value text back into the caller table, so the profile-key bundle now looks like a real bounded handoff rather than an anonymous callback cloud. The deferred callback shim `multiplayer_transport_dispatch_selector_view_refresh_probe_result` then walks the keyed store through `multiplayer_transport_finish_selector_view_refresh_probe_if_matching`, which only completes entries whose pending latch `[entry+0x74]` is still armed and whose parsed marker request id `[entry+0x50]` matches the finished request. A failed result `-1` clears the pending latch and increments the consecutive-failure counter at `[entry+0x7c]`. A nonfailure result clears the pending latch, increments the success generation at `[entry+0x78]` and total-success count `[entry+0x98]`, clears `[entry+0x7c]`, stamps the last-success tick at `[entry+0x6c]`, appends the returned sample into the short rolling history at `[entry+0x84..]`, grows the bounded sample-count `[entry+0x94]` up to four, computes the current average into `[entry+0x80]`, and then publishes that averaged sample through `multiplayer_transport_enqueue_callback_slot24_record`. So the publication boundary is explicit and the request-id ownership is explicit, even though the exact user-facing meaning of the sample and the companion value at `[entry+0x54]` is still open. The same service loop also owns a slower sidecar lane keyed off `[entry+0xa4]`: `multiplayer_transport_select_stale_selector_view_progress_entry` walks the store through `multiplayer_transport_pick_stale_selector_view_progress_entry`, picks one stale entry whose progress latch `[entry+0x9c]` is clear and whose last progress tick `[entry+0x70]` is old enough, and then hands it to `multiplayer_transport_stage_selector_view_progress_snapshot`. That helper now looks more bounded too: it rebuilds the marker text from `[entry+0x50]` through `multiplayer_transport_format_selector_view_probe_marker`, formats one `PNG %s %d` line around that marker and the entry-local selector-view sample at `[entry+0x80]`, appends bounded selector-slot `PNG` fragments for live overlapping slots, marks progress-snapshot state in flight at `[entry+0x9c]`, and stamps both `[entry+0x70]` and the transport-wide throttle tick `[transport+0xaec]`. So the selector-view sidecar no longer looks like one undifferentiated refresh bucket: it has a faster deferred-probe lane plus a slower progress-snapshot lane, both still under the shell-owned multiplayer transport cadence. The two descriptor lanes installed by `multiplayer_transport_attach_callback_table_descriptor` are now tighter too. The first lane rooted at `0x59f5c0` can arm deferred-close state on the owner transport and then forward through callback slot `23`. The second lane is no longer just a loose selector-view bucket: `multiplayer_transport_callback_dispatch_selector_name_payload_lane` at `0x59f650` classifies selector payloads through `multiplayer_transport_is_selector_control_line`, routes `@@@NFO` control lines into `multiplayer_transport_sync_selector_view_nfo_r_flag`, and otherwise publishes either callback slot `13` or the split token-plus-tail callback slot `14` through `multiplayer_transport_split_selector_payload_token_and_tail`. That local `@@@NFO` helper is now bounded more tightly too: it only accepts lines ending in the literal `X\` tail, searches for the field marker `\$flags$\`, and then sets or clears bit `0x2` in the third selector-slot flag word at `[entry+0x64]` depending on whether that field contains the letter `r` before the next backslash. The sibling `multiplayer_transport_callback_dispatch_current_selector_payload_lane` at `0x59f720` first resolves the active selector through `0x5951a0`, then handles the current-selector variants of the same control vocabulary: `@@@NFO` continues into the same local `r`-flag sync helper, `@@@GML` plus mode-`3/4` payloads feed the shared control-token helper `multiplayer_transport_handle_gml_or_png_selector_control`, and the remaining non-control payloads publish callback slot `9`. That shared helper now narrows the `GML` and `PNG` split materially: when the token is `GML` and the tail is `Disconnected`, it requires the active selector-view entry to pass `multiplayer_transport_selector_view_entry_has_gml_disconnect_gate`, which currently means the entry participates in the third selector slot and has bit `0x20` set in `[entry+0x64]`, before it forces one status-pump pass, emits callback slot `16`, and re-enters route mode `5`. Its sibling `PNG` branch resolves a named selector-view entry from the tail text and, when that entry overlaps the active selector-slot ownership, refreshes selector-view runtime state through `0x5948f0` and republishes callback slot `25`. Alongside those dispatchers, one grounded branch at `0x59f560` still updates selector-view runtime state through `0x5948f0` and forwards the same selector-name pair through callback slot `25`, while `0x59f850` resets selector text state through `0x5954b0`, forwards through callback slot `19`, and when selector `2` is active in a nonterminal route mode re-enters `multiplayer_transport_set_route_mode` with mode `1`. The low-level route helper still looks like a two-part cycle: `multiplayer_gamespy_route_service_retry_and_keepalive_timers` handles challenge or retry pressure and periodic outbound control traffic around the `master.gamespy.com`, `PING`, `natneg`, `localport`, `localip%d`, and `statechanged` strings, while `multiplayer_gamespy_route_drain_inbound_packets` drains inbound datagrams and dispatches semicolon lines, backslash-delimited key bundles, and `0xfe 0xfd` GameSpy control packets. The transport-owned callback story is now narrower too. The shared route constructor `multiplayer_gamespy_route_construct_and_seed_callback_vector` seeds `[route+0x88]` through `[route+0x9c]` from the caller-supplied transport callback table, records the owner transport at `[route+0x104]`, and explicitly zeroes `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]` before any later patch-up. For the transport-owned status route, `multiplayer_transport_try_connect_status_route` then patches `[route+0xa0]` through `multiplayer_gamespy_route_set_extended_payload_callback` to point at `multiplayer_transport_forward_validated_extended_route_payload` `0x00597330`, which simply forwards the validated payload wrapper into the owner callback at `[transport+0x17f4]` with context `[transport+0x17f8]`. The grounded live-route connect path at `multiplayer_transport_try_connect_live_route` does not currently perform any matching post-construction patch for `[route+0xa0]`, `[route+0xa4]`, or `[route+0xd4]`, and the higher route-mode state machine now looks consistent with that: current grounded mode transitions switch by releasing route objects through `multiplayer_gamespy_route_release_and_free` and rebuilding them through `multiplayer_transport_try_connect_live_route`, 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]` is non-null. For the currently grounded transport-owned status and live routes, those two branches therefore remain optional and can cleanly no-op instead of implying a hidden mandatory callback path. Inside the packet parser, subtype `4` is no longer an unknown callback hop: after cookie validation it dispatches through `[route+0x9c]`, which the transport-owned status and live routes seed to `multiplayer_transport_handle_validated_route_cookie_event` `0x005972c0`. That helper either marks route progress and re-enters `multiplayer_transport_set_route_mode`, or forwards the event id plus payload into the owner callback at `[transport+0x17f0]` with context `[transport+0x17f8]`. Subtype `6` still validates the same cookie, dedupes one 32-bit cookie or packet id, and then dispatches the trailing payload through the natneg-or-raw callback layer rooted at `[route+0xa0]` and `[route+0xa4]`. This separates the shell-frame preview refresh at `0x006cd8d8` from the actual transport cadence at `0x006cd970`, and also separates that transport cadence from the lower GameSpy route-service and packet-parser layer beneath it.
|
||
- Evidence: `function-map.csv`, `pending-template-store-management.md`, `pending-template-store-functions.csv`, plus objdump caller traces showing `multiplayer_window_service_loop` reaching `multiplayer_flush_session_event_transport` and the transport pump chain `multiplayer_flush_session_event_transport -> multiplayer_transport_flush_and_maybe_shutdown -> multiplayer_transport_service_frame -> multiplayer_transport_service_worker_once -> multiplayer_transport_drain_request_text_queue`.
|
||
- Open Questions: unresolved request-id semantics for `1`, `2`, `4`, and `7`; whether any non-mode-path helper outside the currently grounded release-and-rebuild flow ever patches `[route+0xa4]` and `[route+0xd4]` for the transport-owned status/live routes, or whether those packet families are simply unused in this stack; and how far the Multiplayer preview-dataset machinery is reused outside `Multiplayer.win` beyond the currently grounded `.gmt` save-mode hook.
|
||
|
||
## Input, Save/Load, and Simulation
|
||
|
||
- Roots: the shell controller window-message ingress `shell_controller_window_message_dispatch` at `0x0054e3a0`, the shell input-state object initialized at `0x006d4018` through `shell_input_state_init` `0x0054e710`, the saved-world restore path `world_load_saved_runtime_state_bundle` at `0x00446d40`, the live-world save path `world_runtime_serialize_smp_bundle` at `0x00446240`, `world_entry_transition_and_runtime_bringup` at `0x00443a50`, the frame-owned cadence `simulation_frame_accumulate_and_step_world` at `0x00439140`, the recurring `GameMessage.win` service branch through `game_message_window_service_if_present` `0x004e0720`, the world-facing `GameUppermost.win` overlay branch ensured by `shell_ensure_game_uppermost_window` `0x004e0e40` and serviced through `game_uppermost_window_service_world_hotspot_band` `0x004e0780`, and the lower step family rooted at `simulation_advance_to_target_calendar_point` `0x0040ab50` with periodic branches through `simulation_service_periodic_boundary_work` `0x0040a590`.
|
||
- Trigger/Cadence: shell-side input is event-driven by controller-window `WM_*` traffic while save or load work is triggered either directly from shell commands or through the `fileopt.win` branch flags into the `.smp` runtime-state family; post-bring-up world service becomes recurring once a world root exists at `0x0062c120`, but the current grounded top-level cadence still remains the shell-owned `shell_service_pump_iteration` path, which calls `simulation_frame_accumulate_and_step_world` directly and lets it accumulate elapsed wall-clock time into one or more simulation-step quanta. A second ingress is now bounded too: `simulation_run_chunked_fast_forward_burst` at `0x00437b20` repeatedly calls the same lower stepper `simulation_advance_to_target_calendar_point`, but current grounded callers put that helper inside the larger post-load generation pipeline `world_run_post_load_generation_pipeline` at `0x004384d0` under the `Seeding Economy...` phase rather than under the ordinary player-facing speed buttons. That setup pipeline is now clearer at the progress-banner level too: localized id `318` `Computing Transportation and Pricing...` stays visible while the pipeline runs `world_compute_transport_and_pricing_grid` `0x0044fb70`, the early collection-owned staging pass `world_setup_building_collection_phase` `0x0041ea50`, and the conditional region pair `world_region_collection_seed_default_regions` `0x00421b60` plus `world_region_border_overlay_rebuild` `0x004882e0`; only then does the code post id `319` `Setting up Players and Companies...`. That `319` lane is no longer just a shell-state placeholder: its primary grounded work is still the chairman-profile pair `world_seed_default_chairman_profile_slots` `0x004377a0` plus `world_build_chairman_profile_slot_records` `0x00437220`, which seed the 16 selector bytes at `[0x006cec7c+0x87]`, materialize the per-slot chairman records from the scenario selectors, campaign override flag `[0x006cec7c+0xc5]`, and the static persona table at `0x005f2d28`, and then publish the selected chairman-profile and linked company summary pair through `[state+0x25]` and `[state+0x21]`. The local slot-record shape is tighter too because the shell editor panel around `0x004cc2d0` now surfaces it directly: `[slot+0x00]` is the staged chairman profile id, `[slot+0x01]` is the Optional-versus-Mandatory byte with nonzero=`Optional` and zero=`Mandatory`, `[slot+0x02]` combines with the separate per-slot gate at `[world+0x0bc3+slot*9]` to surface `Human`, `Computer`, and `Human or Computer`, `[slot+0x03]` is the special occupied-seat byte, and `[slot+0x04]` is the numeric tuning field. Both the selector seeder and the record materializer treat either `[slot+0x02]` or `[slot+0x03]` as enough to keep a slot live, but current grounded writes only seed `[slot+0x03]` on slot zero and later move it solely by whole-record compaction. That makes `[slot+0x03]` the strongest current anchor for the distinguished primary-human-seat flag rather than a generic role byte. The summary fields are tighter too: current direct accessors now show `[state+0x25]` as the selected chairman profile id in `0x006ceb9c`, while `[state+0x21]` is the linked owning company id copied from `[profile+0x1dd]` through `scenario_state_set_selected_chairman_profile` `0x00434890`. The editor-side scenario setup surface beside that chairman panel is clearer now too. `map_editor_scenario_metadata_panel_refresh_controls` `0x004ca790` republishes the scenario description from `[0x006cec78+0x672e]`, the start-year trio `[+0x66ca]`, `[+0x66d2]`, and `[+0x66ce]`, and the paired boolean toggles `[+0x66de]` plus inverse `[+0x66f3]` across control band `0x5b69..0x5b74`, while `map_editor_scenario_metadata_panel_refresh_briefing_mode` `0x004ca670` now bounds the single-player versus multiplayer briefing switch by flipping selector `0x621f50`, publishing localized headings `1491` and `3586`, and refreshing the two briefing texts from `[state+0x4f30]` and `[+0x5ae9]`. The companion dispatcher `map_editor_scenario_metadata_panel_handle_message` `0x004cb4a0` makes the year semantics tighter too: it commits the description and both briefing texts from edit-control payloads, toggles the same two booleans, and clamps the three year fields to `1829..2100` while maintaining `minimum <= default <= maximum`, which now lines up directly with the editor strings `Description:` `Minimum Start Year:` `Default Start Year:` `Maximum Start Year:` `Briefing` and `Multi-Player Briefing`. The neighboring special-conditions page is clearer as well: `map_editor_scenario_special_conditions_panel_construct` `0x004cb2b0` now grounds the live owner for the `Setup_Options_Buttons.imb` list rooted at `0xa7fa`, walks the 36-entry static rule table at `0x005f3ab0`, counts enabled flags from `[0x006cec78+0x4a7f]`, and publishes the `Special Conditions In Effect` summary from localized id `1053`. The same page now has a bounded live dispatcher too: `map_editor_scenario_special_conditions_panel_handle_message` `0x004cb8e0` handles both bulk selection controls and direct row-state changes, commits them back into `[0x006cec78+0x4a7f]`, and then re-enters the panel constructor to refresh the summary. That table is no longer just a raw id run; it now clearly includes the finance and construction restrictions `2535..2563`, plus later editor toggles such as `Use Bio-Accelerator Cars`, `Disable Cargo Economy`, `Disable Train Crashes`, `Disable Train Crashes AND Breakdowns`, and `AI Ignore Territories At Startup`. The neighboring available-chairman page is tighter too. `map_editor_available_chairman_panel_construct` `0x004ca540` now bounds the shell-side owner for the 40-entry persona-availability surface under `0x5aa0..0x5b03`: it walks the same persona table family at `0x005f2d28`, publishes one localized chairman-name row per profile, counts enabled availability bytes from `[0x006cec78+0x6987..]`, and emits the summary `%1 out of %2 are selected.` from localized id `1035`. The live state owner beside it is now grounded as well: `map_editor_available_chairman_panel_handle_message` `0x004cb6f0` handles three bulk-selection controls `0x5aa1..0x5aa3` by rewriting that same availability-byte array from preset bytes embedded in the persona table, and it also commits direct per-row toggle changes from `0x5aaa..0x5b03` back into `[state+0x6987..]` before refreshing the page. So the editor lane now has three distinct scenario-setup slices rather than one chairman-only blob: chairman slots, scenario metadata and briefings, plus both a rule-toggle matrix and an available-chairman pool. There is now one adjacent company-side lane too: current neighboring setup flow conditionally enters `world_conditionally_seed_named_starting_railroad_companies` `0x0047d440` when the Multiplayer preview owner `0x006cd8d8` is absent and either sandbox flag `[0x006cec7c+0x82]` is set or shell-state flag `[0x006cec74+0x14c]` is set while editor-map mode `[0x006cec74+0x68]` is clear. That helper no longer looks like a generic company refresh. It seeds exactly three fixed named railroad-company records from `RT3.lng` ids `575..577`: `Missouri Pacific`, `New York Central`, and `Grand Trunk Railroad`. The first seeded company is tied back to the selected chairman-profile summary and becomes the selected company id. The second railroad is tighter now too: it only receives a chairman link when `profile_collection_count_active_chairman_records` finds at least two live chairman records, and the helper then binds the zero-based second active chairman through `profile_collection_get_nth_active_chairman_record` with ordinal `1`. The third railroad currently gets no matching chairman-link branch in the grounded setup flow and therefore remains an unchaired named company in the live roster. The shell UI above that setup lane is now tighter as well: `shell_company_list_window_construct` `0x004c7200` builds a live company-list window over the company collection at `0x0062be10`, `shell_company_list_window_refresh_rows` `0x004c6c30` formats the active companies with localized strings `267..270`, and only then conditionally appends one synthetic row id `0x7fff` through `shell_company_list_format_company_or_start_row` `0x004c6b40` so `<<Start New Company>>` appears as a separate affordance rather than as part of the seeded trio itself. Current shell paging around that same roster is tighter too because the live company collection now has a grounded active-only ordinal helper family: `company_collection_count_active_companies` `0x00429a50`, `company_collection_count_active_companies_before_company_id` `0x004299f0`, and `company_collection_get_nth_active_company_id` `0x00429990`. Those helpers now anchor the “current company among active companies” math used by shell-side detail and picker flows rather than leaving it as anonymous collection glue. `shell_company_list_window_handle_message` `0x004c6f30` then routes the synthetic row into `start_new_company_dialog_open` `0x0047d080`, whose commit path now grounds through `start_new_company_dialog_commit_create_company` `0x0047d120`. That lower helper unlinks any current chairman-owned company, allocates a fresh company id from the live collection at `0x0062be10`, initializes it through `0x00428420`, and only then publishes the new selected company. The neighboring compact request helper `start_new_company_request_create_company` `0x0047d320` does the same fresh-company path from a request block and is already reached from the startup-company branch at `0x00470e48`. The immediate sibling shell branch below that roster is still station-oriented: current grounded resource names and handlers put one branch on `shell_station_detail_window_construct` `0x005068c0`, another on `shell_station_list_window_construct` `0x005074c0`, and the subordinate selector helper on `shell_station_pick_window_construct` `0x00507620`. But the broader company-detail ownership question is no longer open. There is now a separately grounded `CompanyDetail.win` family rooted at `shell_company_detail_window_construct` `0x004c5540`, with `shell_company_detail_window_handle_message` `0x004c56a0` as its main dispatcher and `shell_company_detail_window_refresh_controls` `0x004c2ca0` as the shared repopulation pass for the selected-company presentation and tabbed control bands around `0x9476..0x9490`. A grounded shell detail-manager caller reaches that constructor at `0x004dde24`, the first finance-action layer beneath it is now bounded through `shell_company_detail_issue_bond_offer_flow` `0x004c3890`, `shell_company_detail_issue_stock_offer_flow` `0x004c3f30`, `shell_company_detail_buyback_stock_flow` `0x004c46d0`, and `shell_company_detail_change_dividend_rate_flow` `0x004c5360`, and the first non-finance layer is now bounded too through `shell_company_detail_resign_chairmanship_flow` `0x004c5a0e`, `shell_company_detail_bankruptcy_flow` `0x004c5b99`, the territory-access family rooted at `shell_company_detail_refresh_selected_territory_access_summary` `0x004c1b60` plus `shell_company_detail_buy_territory_access_rights_flow` `0x004c5fc9`, backed by `company_clear_selected_chairman_if_current_profile` `0x00428a10`, `company_declare_bankruptcy_and_halve_bond_debt` `0x00425a90`, `company_has_territory_access_rights` `0x00424010`, `company_set_territory_access_rights_byte` `0x00424030`, and `company_can_purchase_territory_access_rights` `0x00426be0`, plus the two control-transfer lanes. `shell_company_detail_attempt_chairmanship_takeover_flow` `0x0050ccc0` now grounds the special chairman's election path: it checks the caller's current stock ownership in the target company, rejects insufficient holdings through localized id `623`, rejects recent failed attempts through id `624`, and then opens the confirmation under id `625` before seeding the local takeover-election state or packaging the same request through the multiplayer shell transport. The follow-on resolver `shell_resolve_chairmanship_takeover_vote_and_commit_outcome` `0x0050c940` now closes the single-player election loop too: it walks the active chairman profile collection, computes weighted votes for and against the takeover, compares the affirmative total against half the target-company value, presents the result through `shell_present_chairmanship_takeover_vote_outcome_dialog` `0x0050c500` in single-player or localized id `3082` in multiplayer, and then either transfers chairmanship through `0x00428a30` or stamps the current year into `[company+0x289]` as the grounded takeover-cooldown field. `shell_company_detail_attempt_merger_flow` `0x004ec640` now grounds the merger side too: it rejects empty worlds through id `727`, rejects recent failed attempts through id `728`, checks the proposed premium against company cash through localized id `3889`, and then commits into the resolver family. That merger resolver is now bounded too: `shell_resolve_merger_vote_and_commit_outcome` `0x004ebd10` walks the active chairman profile collection, computes weighted votes for and against the proposed merger, compares the affirmative total against half the target-company value, presents the result through `shell_present_merger_vote_outcome_dialog` `0x004eb890` in single-player or localized id `3059` in multiplayer, and then either commits the merger through `0x00427e20` or stamps the current year into `[company+0x15f]` as the grounded merger-cooldown field. The vote-bias side beneath that resolver is tighter now too: `scenario_state_compute_issue_opinion_multiplier` `0x00436590` is no longer just an abstract issue table lookup because its merger callsite uses issue id `0x3a`, which lines up directly with localized id `726` saying merger votes depend on public attitude toward the management of the two companies. By contrast the public-support helper `company_compute_public_support_vote_scalar` `0x00424fd0` uses the broader issue id `0x37`, which currently looks like a more general company-management or public-sentiment slot rather than the merger-only attitude term. The supporting stat layer is bounded more cleanly now too: the surrounding `0x0b` setup in the merger and takeover offer builders is formatter mode rather than a player-facing issue number, while the company-side stat wrapper `company_read_year_or_control_transfer_metric_value` `0x0042a5d0` now reads as a generic stat-family accessor over year-relative series or the bounded slot family in `company_read_control_transfer_metric_slot` `0x0042a2e0`. Its recurring family token `0x2329` is no longer treated as an issue id here either; it is the stat-family selector paired with localized company-stat label id `2329`. That means the paired raw and scaled helpers at `0x004241e0` and `0x00424200` now read as narrow control-transfer offer policy totals instead of direct issue-argument consumers, and the same broader support-and-governance metric family now also feeds the annual shareholder-revolt and creditor-liquidation lane surfaced by localized ids `300..304`. The editor-side help text cluster around ids `2433..2437` is no longer floating either: current grounded map-editor code now has a live economic tuning family beside the chairman-slot panel. `map_editor_economic_cost_slider_panel_construct` `0x004cadf0` binds six slider controls through `map_editor_economic_cost_slider_dispatch` `0x004ca980` into the scenario-state float block `[0x006cec78+0x0be2..0x0bf6]`, while the surrounding descriptor table at `0x00611c70..0x00612220` pairs that wider editor lane with localized fields `Prime Rate`, `Merger Premium`, and `Build Stations Cost` through `Steam Engine Cost` plus the comparison or help texts `2433..2437`. The broader shell-state master flag at `[0x006cec74+0x68]` still sits above the remaining post-load phases, plus two narrower per-phase gates: `[0x006cec74+0x174]` directly fronts id `320` `Setting Up Buildings...` and the later region-owned structure-demand and placement pass through `world_region_collection_run_building_population_pass` `0x00421c20` plus the deeper per-region worker `world_region_balance_structure_demand_and_place_candidates` `0x004235c0`, while `[0x006cec74+0x178]` directly fronts id `321` `Seeding Economy...` and the chunked burst helper `simulation_run_chunked_fast_forward_burst`; id `322` then fronts `Calculating Heights...`. The master `+0x68` flag is no longer just structural: the shell load/save coordinators now use the same flag to force the editor-map `.gmp` family, so current evidence treats it as the broader editor-map mode above those later setup branches rather than an unnamed generic toggle. That worker is no longer one opaque building bucket: current grounded categories split into `House`, a weighted region-profile family surfaced through the `Industry Weightings` stats path, and a third branch whose low-level fallback token is `Commercial` but whose aligned stats label is `City Support`. The same lower helper also reappears later on a slower simulation-side cadence with scale `1/12`, so it no longer looks like one-time setup glue only. The actual game-speed control family remains separate, rooted at `world_set_game_speed_mode` `0x00434680`, `world_adjust_game_speed_mode_delta` `0x00434850`, and `world_toggle_pause_or_restore_game_speed` `0x00437a60`.
|
||
- Key Dispatchers: `shell_controller_window_message_dispatch`, `shell_input_apply_window_key_transition`, `shell_input_snapshot_dispatch_state`, `shell_input_cursor_inside_active_view`, `world_load_saved_runtime_state_bundle`, `world_runtime_serialize_smp_bundle`, `simulation_frame_accumulate_and_step_world`, `game_message_window_service_if_present`, `game_message_window_service_frame`, `game_uppermost_window_handle_message`, `game_uppermost_window_service_world_hotspot_band`, `game_uppermost_window_refresh_controls`, `world_view_service_keyboard_turn_pan_and_zoom_bindings`, `world_view_step_heading_quadrant`, `world_view_step_zoom_bucket`, `world_seed_default_chairman_profile_slots`, `world_build_chairman_profile_slot_records`, `world_conditionally_seed_named_starting_railroad_companies`, `scenario_state_set_selected_chairman_profile`, `scenario_state_get_selected_chairman_profile_record`, `scenario_state_get_selected_chairman_company_record`, `shell_company_list_window_construct`, `shell_company_list_window_refresh_rows`, `shell_company_list_window_handle_message`, `start_new_company_dialog_open`, `start_new_company_dialog_commit_create_company`, `start_new_company_request_create_company`, `shell_station_detail_window_construct`, `shell_station_list_window_construct`, `shell_station_list_window_handle_message`, `shell_station_pick_window_construct`, `shell_station_pick_window_populate_station_rows`, `map_editor_chairman_slot_panel_construct`, `map_editor_chairman_slot_panel_handle_message`, `map_editor_chairman_slot_panel_refresh_selected_slot`, `map_editor_chairman_slot_panel_cycle_selected_slot_profile`, `map_editor_available_chairman_panel_construct`, `map_editor_available_chairman_panel_handle_message`, `map_editor_scenario_metadata_panel_refresh_briefing_mode`, `map_editor_scenario_metadata_panel_refresh_controls`, `map_editor_scenario_metadata_panel_handle_message`, `map_editor_scenario_special_conditions_panel_construct`, `map_editor_scenario_special_conditions_panel_handle_message`, `map_editor_economic_cost_slider_panel_construct`, `map_editor_economic_cost_slider_dispatch`, `station_place_world_surface_sync_and_dispatch`, `station_place_window_handle_message`, `track_lay_window_refresh_controls`, `track_lay_window_service_frame`, `track_lay_window_handle_message`, `simulation_advance_to_target_calendar_point`, the smaller single-step helper at `0x00409e80`, `0x0040a9c0`, `0x0040a910`, and `simulation_service_periodic_boundary_work`.
|
||
- State Anchors: shell input object `0x006d4018`, per-key state table starting at `[input+0x100]`, packed shell input flags at `[input+0xa8c]` now grounded as Right Shift `0x1`, Left Shift `0x2`, Control `0x4`, and Alt `0x20`, nested dispatch counter `[input+0xa90]`, global shell controller `0x006d4024`, active world root `0x0062c120`, `GameMessage.win` object `0x006d081c`, `GameUppermost.win` overlay object `0x006d0820`, `StationPlace.win` tool object `0x006d1720`, `TrackLay.win` tool object `0x006d1a8c`, accumulated leftover simulation time at `[this+0x4c80]`, shell and mode globals at `0x006cec74`, `0x006cec78`, and `0x006cec7c`, world manager collections at `0x0062be10`, `0x006ceb9c`, `0x006cfcbc`, `0x006cec20`, `0x0062bae0`, and `0x006acd34`, plus the packed calendar-like tuple fields around `[this+0x0d]`, `[this+0x0f]`, `[this+0x11]`, and `[this+0x14]`.
|
||
- Subsystem Handoffs: the controller window dispatcher now looks like the first grounded input ingress. It translates keyboard and mouse `WM_*` traffic into shell controller state and the separate input object at `0x006d4018`; read-side cursor and camera helpers later snapshot that object through `shell_input_snapshot_dispatch_state` and gate world-relative interaction through `shell_input_cursor_inside_active_view`. Current grounded consumers around `0x00478cb0`, `0x004e0780`, `0x0053f450`, and `0x0053fe90` still sit on the shell controller path and consult `0x006d4024` or the world owner at `0x0062be68`, so there is still no evidence for a distinct gameplay-only input object after world entry. Instead, the first deeper world-mode interaction branch now looks like a shared world-view coordinator layered on top of the same shell-fed input state. Shell_transition_mode ensures the `GameUppermost.win` object at `0x006d0820`; its message dispatcher `game_uppermost_window_handle_message` owns the narrow action band `0x7918` through `0x7921`; and the recurring service helper `game_uppermost_window_service_world_hotspot_band` rate-limits those hotspot actions, rechecks `shell_input_cursor_inside_active_view`, and then pans the live world view through `world_view_pan_relative_offset_in_camera_plane` `0x0043d130`. The same lower setter family is also reached from the cursor-drag path through `world_view_apply_screen_delta_to_focus_position` `0x0043d0c0`. Above both of those sits the larger recurring service `world_view_service_shell_input_pan_and_hover` `0x0043db00`, which now has one grounded keyboard branch beneath it: `world_view_service_keyboard_turn_pan_and_zoom_bindings` `0x0043d740`. That helper resolves four binding-pair families from the shell input table via `0x0054e7d0`, and the localized labels are now grounded from `Data/Language/RT3.lng`: `Camera Forward` and `Camera Backward` feed the first signed pan channel, `Camera Left` and `Camera Right` feed the second signed pan channel, `Camera Zoom In` and `Camera Zoom Out` feed the signed zoom-step channel that `0x0043db00` smooths into `world_view_step_zoom_bucket` `0x0043cc30`, and `Camera Rotate Left` plus `Camera Rotate Right` feed the continuous heading-turn branch through `0x0043c810`. The setup side is now better bounded too: `world_view_seed_keyboard_binding_slot_pairs` at `0x00439e40` seeds the eight slot pairs at `[this+0x0a6]` through `[this+0x0e2]` from the global action-binding registry through `0x0045f370` using the distinct registry keys `0x0043d2a0` through `0x0043d310`, and the registration block at `0x00460769` through `0x004608e7` shows those roots are defaulted to the expected Up Down Left and Right virtual keys before runtime polling begins. Beneath that camera stack, the enclosing frame path now has one grounded non-view sidecar: after `0x0043db00` it reads the active controller-view object pointer at `[0x006d4024+0x18]+0x366e`, compares it against the latched target at `[frame_owner+0x66a2]`, fires exit and enter-style vtable callbacks on pointer changes through slots `+0x64` and `+0x60`, and only latches the new object when the object passes its own slot `+0x1c` availability test and shell detail control id `0x07d6` on `0x006d0818` has flag bit `0x4`. That `0x07d6` gate is now more bounded than before: the dedicated `TrackLay.win` tool family rooted at `0x006d1a8c` special-cases the same control id in both `track_lay_window_service_frame` and `track_lay_window_handle_message`, uses world hit tests through `0x00448ac0` to arm and release a drag latch on that surface, and routes the resulting command work through the shared track-lay mode state at `0x00622b0c`. The surrounding `track_lay_window_refresh_controls` pass now shows that this is not just one isolated drag handler: the same tool family owns three mutually exclusive primary mode buttons at `0x985e` `0x985f` and `0x9860`, and current primary evidence now bounds those values as `Lay single track.` `0x1`, `Lay double track.` `0x4`, and `Bulldoze` `0x40` from the localized strings 2054 2055 and 1721 plus the matching control-routing branches in `track_lay_window_handle_message`. The same family also owns a bridge-type preference selector rooted at `0x006cec74+0x138`, two wrapped `Never` through `Common` frequency settings at `0x006cec74+0x140` and `0x006cec74+0x13c`, two boolean track-lay preference toggles, and the electrify-all-track action path. Those last two toggles are now tighter than before: current evidence strongly aligns control `0x986e` and state `0x006cec74+0x144` with `Auto-Hide Trees During Track Lay` from strings 1838 and 1839, while control `0x986d` and state `0x006cec78+0x4c74` align with `Auto-Show Grade During Track Lay` from strings 3904 and 3905. That mapping is still evidence-backed rather than absolutely direct from a recovered resource table, but the state ownership and control order now make it the strongest current fit. That makes `0x07d6` look like the shared main-world interaction surface inside a broader TrackLay world-command subsystem, not an unrelated detail button. The neighboring `StationPlace.win` family is now grounded on that same surface too: the shell detail-panel constructor family allocates it through `station_place_window_construct` at `0x00509d80`, publishes it at `0x006d1720`, services it each frame through `station_place_window_service_frame` at `0x0050a530`, and routes player-facing commands through `station_place_window_handle_message` at `0x005091b0`. That dispatcher special-cases the same `0x07d6` control, and the shared helper `station_place_world_surface_sync_and_dispatch` at `0x00508bb0` either accepts that direct surface traffic or falls back to the same detail-panel control looked up through `0x006d0818`, rechecks flag bit `0x4`, hit-tests the world through `0x00448ac0`, stages world coordinates into `0x006d1738` and `0x006d173c`, refreshes the selected-site summary through `station_place_format_selected_site_summary`, and updates the live station-placement selection state at `0x00622af0`, `0x00622aec`, and `0x006d1740`. Together with `station_place_select_category_and_refresh` `0x00508880`, `station_place_refresh_category_controls` `0x00507b90`, and `station_place_format_preview_panel` `0x00508730`, that makes StationPlace a second grounded consumer of the shared main-world interaction surface rather than only a sibling constructor plus frame hook. The StationPlace control semantics are now tighter too: the top category strip at `0x697c` through `0x6981` now grounds as small station, medium station, large station, service tower, maintenance facility, and non-station building from `RT3.lng` strings 2197 through 2202. For the three station categories only, the secondary strip at `0x6988` and `0x6989` plus display field `0x698c` no longer looks like another placement mode family; it is a building-style scroller. The click handlers on `0x6988` and `0x6989` cycle the style override in `0x00622aec`, and the display path builds the active style token from `StationSml`, `StationMed`, or `StationLrg` plus the localized architecture styles `Victorian`, `Tudor`, `Southwest`, `Persian`, `Kyoto`, and `Clapboard` from `RT3.lng` ids 2672 through 2667. One layer lower, the remaining opaque controls are now much tighter: `0x6985` and `0x6986` are no longer a generic assist toggle but the two station-rotation policy choices from strings 2207 and 2206, switching between auto-orienting the building to track or obstacles and strictly obeying the rotation specified by the circle above. The dedicated control at `0x6987` is the station-rotation circle itself, wired through callback `0x00507a90` and aligned with `Click to rotate the building. You can also use bracket keys [ and ] to rotate buildings.` string 2208. That matches the lower behavior too: when the strict-rotation choice is off, both preview and commit paths route staged coordinates through `0x00508040`, which performs the extra orientation search before validation; when strict rotation is on, that pass is skipped and the current angle in `0x006d172c` is used directly. The direct shell UI also exposes the same discrete view-step family through `world_view_step_heading_quadrant` `0x0043cb00` and `world_view_step_zoom_bucket` `0x0043cc30`. The neighboring gating predicates `world_view_should_drive_primary_pan_channel` and `world_view_should_drive_secondary_pan_channel` test packed shell input bits `0x3`, and `shell_input_apply_window_key_transition` now grounds those bits as the left and right Shift modifiers from scan codes `0x2a` and `0x36`. That means cursor drag, overlay hotspots, held Shift state, direct keyboard turn/pan/zoom bindings, the TrackLay and StationPlace world-command surfaces, and at least one frame-owned hover or focus-target branch all converge under the same shell-fed world-mode input path rather than a separate gameplay-input stack.
|
||
- Evidence: function-map rows for `shell_controller_window_message_dispatch`, `shell_drain_pending_window_messages`, `shell_input_state_init`, `shell_input_apply_window_key_transition`, `shell_input_snapshot_dispatch_state`, `shell_input_cursor_inside_active_view`, `world_load_saved_runtime_state_bundle`, `world_runtime_serialize_smp_bundle`, `world_entry_transition_and_runtime_bringup`, `simulation_frame_accumulate_and_step_world`, `game_message_window_service_if_present`, `game_message_window_service_frame`, `shell_ensure_game_uppermost_window`, `game_uppermost_window_construct`, `game_uppermost_window_handle_message`, `game_uppermost_window_service_world_hotspot_band`, `world_view_set_focus_position_xyz`, `world_view_apply_screen_delta_to_focus_position`, `world_view_pan_relative_offset_in_camera_plane`, `world_view_seed_keyboard_binding_slot_pairs`, `world_view_service_keyboard_turn_pan_and_zoom_bindings`, `world_view_step_heading_quadrant`, `world_view_step_zoom_bucket`, `world_view_should_drive_primary_pan_channel`, `world_view_should_drive_secondary_pan_channel`, `world_view_service_shell_input_pan_and_hover`, `world_seed_default_chairman_profile_slots`, `world_build_chairman_profile_slot_records`, `world_conditionally_seed_named_starting_railroad_companies`, `profile_collection_count_active_chairman_records`, `profile_collection_get_nth_active_chairman_record`, `scenario_state_set_selected_chairman_profile`, `scenario_state_get_selected_chairman_profile_record`, `scenario_state_get_selected_chairman_company_record`, `start_new_company_dialog_open`, `start_new_company_dialog_commit_create_company`, `start_new_company_request_create_company`, `shell_company_list_format_company_or_start_row`, `shell_company_list_activate_or_shift_center_company`, `shell_company_list_window_refresh_rows`, `shell_company_list_window_handle_message`, `shell_company_list_window_construct`, `shell_company_detail_window_refresh_controls`, `shell_company_detail_window_construct`, `shell_company_detail_window_handle_message`, `shell_station_detail_window_construct`, `shell_station_list_window_construct`, `shell_station_list_window_handle_message`, `shell_station_pick_window_construct`, `shell_station_pick_window_populate_station_rows`, `map_editor_chairman_slot_panel_construct`, `map_editor_chairman_slot_panel_refresh_slot_list`, `map_editor_chairman_slot_panel_refresh_selected_slot`, `map_editor_chairman_slot_panel_refresh_slot_counters`, `map_editor_chairman_slot_panel_cycle_selected_slot_profile`, `map_editor_chairman_slot_panel_handle_message`, `map_editor_available_chairman_panel_construct`, `map_editor_available_chairman_panel_handle_message`, `map_editor_scenario_metadata_panel_refresh_briefing_mode`, `map_editor_scenario_metadata_panel_refresh_controls`, `map_editor_scenario_metadata_panel_handle_message`, `map_editor_scenario_special_conditions_panel_construct`, `map_editor_scenario_special_conditions_panel_handle_message`, `map_editor_economic_cost_slider_panel_construct`, `map_editor_economic_cost_slider_dispatch`, `station_place_refresh_category_controls`, `station_place_format_selected_site_summary`, `station_place_format_preview_panel`, `station_place_select_category_and_refresh`, `station_place_world_surface_sync_and_dispatch`, `station_place_window_handle_message`, `station_place_window_construct`, `station_place_window_service_frame`, `track_lay_window_refresh_controls`, `track_lay_window_construct`, `track_lay_window_service_frame`, `track_lay_window_handle_message`, and `simulation_service_periodic_boundary_work`, plus the shell-input disassembly around `0x0054f290`, the world-view setup and service branches around `0x00439e40`, `0x0043d740`, `0x0043db00`, `0x0043cb00`, and `0x0043cc30`, the `319` setup-side branches around `0x004377a0`, `0x00437220`, `0x00477820`, `0x00477860`, `0x0047d440`, `0x004c6c30`, `0x004c6f30`, `0x0047d080`, `0x0047d120`, `0x0047d320`, `0x004c2ca0`, `0x004c5540`, `0x004c56a0`, `0x005068c0`, `0x005071e0`, `0x005074c0`, and `0x005076c0`, the direct summary-field helpers around `0x00434870` `0x00434890` and `0x004348c0`, the company-link writers at `0x00427c70` and `0x00427d74`, the company constructor at `0x00428420`, the startup-company branch around `0x00470e48`, the shell company-list strings `266` `<<Start New Company>>`, `267` `You are the chairman of the %1!`, `268` `The %1 has no chairman at the moment.`, `269` `%1 is the chairman of the %2.`, `270` `Double-click for details.`, and `2992` `Shift-Click to center on this company's primary station.`, the `CompanyDetail.imb` and `CompanyDetail.win` resource strings in `.rdata`, the `StationDetail.imb`, `StationDetail.win`, `StationList.win`, and `StationPick.win` resource strings in `.rdata`, the shell editor window branches around `0x004c9da0`, `0x004ca010`, `0x004ca1c0`, `0x004ca540`, `0x004ca670`, `0x004ca790`, `0x004ca980`, `0x004cb2b0`, `0x004cb4a0`, `0x004cb6f0`, `0x004cb8e0`, `0x004cc250`, `0x004cc2d0`, `0x004ceb90`, and `0x004cecc0`, the localized chairman-slot strings `2997` through `3001` `Optional` `Mandatory` `Human or Computer` `Computer` and `Human`, the localized scenario-editor strings `1483..1492` `Description:` through `Type the briefing for this map.`, the localized summary strings `1035` `%1 out of %2 are selected.` and `1053` `Special Conditions In Effect`, the 40-entry persona availability page under `0x5aa0..0x5b03`, the 36-entry special-condition table at `0x005f3ab0` covering ids `2535..2563`, `2874`, `3722`, `3835`, `3850`, `3852`, and `3920`, the static persona table at `0x005f2d28`, the selector array at `0x006cec7c+0x87`, the persona collection at `0x006ceb9c`, the localized persona strings in `Data/Language/RT3.lng` including `2730` `Unassigned`, the named-chairman range `2731+`, and the neighboring biography range `2495+`, plus the seeded railroad-name strings `575` `Missouri Pacific`, `576` `New York Central`, and `577` `Grand Trunk Railroad`, the StationPlace.win constructor plus category, dispatcher, rotation-circle, shared-world-surface, preview-build, and recurring service branches around `0x00507a90`, `0x00507b90`, `0x00508550`, `0x00508730`, `0x00508880`, `0x00508bb0`, `0x005091b0`, `0x00509d80`, and `0x0050a530`, the StationPlace string cluster `Place a small station` 2197 `Place a medium station` 2198 `Place a large station` 2199 `Place a service tower` 2200 `Place a maintenance facility` 2201 `Place a non-station building` 2202 `Scroll through building styles.` 2203 `When placing the building, it will strictly adhere to the rotation specified by the circle above.` 2206 `When placing the building, it will rotate itself as needed to orient to track or avoid obstacles.` 2207 `Click to rotate the building. You can also use bracket keys [ and ] to rotate buildings.` 2208 and the architecture-style labels `Clapboard` 2667 `Kyoto` 2668 `Persian` 2669 `Southwest` 2670 `Tudor` 2671 and `Victorian` 2672, the TrackLay.win constructor and dispatcher family around `0x0050d2d0`, `0x0050e400`, `0x0050e1e0`, and `0x0050e5c0`, the localized `Never` through `Common` strings 615 through 618, the track-lay strings `Bulldoze` 1721 `Lay single track.` 2054 `Lay double track.` 2055 and 3114, the TrackLay preference strings `Auto-Hide Trees During Track Lay` 1838 `If 'Auto-Hide Trees During Track Lay' is checked, trees will automatically be reduced to small stumps whenever you are in track laying mode.` 1839 `Auto-Show Grade During Track Lay` 3904 and `If 'Auto-Show Grade During Track Lay' is checked, you'll see the grade number over the track while laying track - useful for trying to keep your slopes to a minimum.` 3905, the electrify-all confirmation and failure strings 3083 3084 3837 and 3900, the binding-registry lookup path at `0x0045f370`, the registration block at `0x00460769` through `0x004608e7`, and the localized labels in `Data/Language/RT3.lng` ids `3466` through `3473`.
|
||
- Open Questions: no separate outer gameplay loop is grounded above `simulation_frame_accumulate_and_step_world` yet and no deeper gameplay-only input object is grounded either. The new setup-pipeline evidence narrows that question in one direction: the currently grounded chunked burst path at `0x00437b20` now looks subordinate to `world_run_post_load_generation_pipeline` rather than to the ordinary speed controls, and even there it still reuses the same lower stepper and pumps shell work between chunks instead of revealing a detached gameplay loop owner. The player-facing speed-control family is now cleaner too: `world_set_game_speed_mode` owns the bounded `Paused` through `Very Fast` ladder plus the hidden `Ultra Fast 6..9` extension, `world_toggle_pause_or_restore_game_speed` uses `[this+0x1d]` as the resume slot, and the multiplayer host restriction now looks attached to that same setter family rather than to the setup-side burst helper. The first deeper world-mode interaction branch is now better bounded: `GameUppermost.win` hotspots, cursor drag, held Shift state, discrete shell view-step commands, direct keyboard turn/pan/zoom bindings, the `TrackLay.win` and `StationPlace.win` world-command surfaces, and a frame-owned hover or focus-target transition branch all feed the same shell-controller-backed path. The remaining uncertainty has moved farther from basic ownership: the hover-target branch clearly exists, and `0x07d6` now looks like the shared main-world interaction surface rather than a generic detail button for one tool family only. The next unresolved layer is narrower and more semantic: the setup side now has one grounded owner, `world_run_post_load_generation_pipeline`, and its building-side branch is no longer just one opaque block. We now have a region family, a region-border overlay rebuild, a region-owned structure-demand and placement dispatcher, and a deeper per-region worker that computes category demand, subtracts existing coverage, and tries candidate placements. The category map is tighter too: category `0` falls back to `House`, category `2` is the year-gated weighted region-profile family that also feeds the localized `Industry Weightings` stats panel, and category `3` now reaches a separate pool-driven picker whose fallback label is `Commercial` but whose aligned player-facing stats bucket is `City Support`. The remaining setup-side uncertainty has therefore narrowed again: the region seed and border-overlay pair clearly complete before the `Setting up Players and Companies...` banner is posted; `[0x006cec74+0x174]` now looks like the direct building-population gate; `[0x006cec74+0x178]` now looks like the direct seeding-burst gate; and `[0x006cec74+0x68]` now aligns with editor-map mode because the same flag forces the `.gmp` family in the shell file coordinators while suppressing the later building and seeding branches and diverting the deeper region worker into alternate logic. The `319` lane itself is no longer the open structural gap; it now clearly owns chairman-profile slot seeding, profile-record materialization, a shell editor surface over the same local record family, and a separate live-company presentation path through the company-list window. The remaining gaps on this lane are therefore narrower than before: the local slot records are rooted at `[world+0x69d8]`, `[slot+0x01]` polarity and the external role gate at `[world+0x0bc3+slot*9]` are now grounded, and `[slot+0x03]` now looks like the distinguished primary-human-seat marker because current grounded writes seed it only on slot zero and later logic moves it solely by whole-record compaction. The open question is no longer whether the seeded trio lands in the visible shell company roster; current evidence says it does, and ordinary `Start New Company` now looks like a fresh-company allocator through `start_new_company_dialog_commit_create_company` and `start_new_company_request_create_company`, not like the path that claims one of the seeded named railroads. The immediate post-roster station branch is now clearly separate: current grounded resource names and handlers put mode `8` on `StationDetail.win`, mode `5` on `StationList.win`, and the subordinate selector helper on `StationPick.win`. The company-side ownership question has therefore moved down a layer rather than staying open. We now have a recovered `CompanyDetail.win` owner family through `shell_company_detail_window_refresh_controls`, `shell_company_detail_window_construct`, and `shell_company_detail_window_handle_message`; the first finance-action layer beneath it is bounded through the bond, stock-issue, stock-buyback, and dividend-rate helpers; the territory-access side is bounded too through `shell_company_detail_refresh_selected_territory_access_summary`, `shell_company_detail_buy_territory_access_rights_flow`, and the underlying company access-rights helpers; and the full takeover and merger vote-result lane is now grounded through `shell_resolve_chairmanship_takeover_vote_and_commit_outcome`, `shell_present_chairmanship_takeover_vote_outcome_dialog`, `shell_resolve_merger_vote_and_commit_outcome`, and `shell_present_merger_vote_outcome_dialog`. The remaining company-side uncertainty is therefore narrower than before: the public-support side is now tighter too because `company_compute_cached_recent_performance_support_score` and `company_compute_public_support_vote_scalar` bound the recent-performance and public-support blend beneath those vote resolvers, `scenario_state_compute_issue_opinion_multiplier` now bounds the next layer of optional company, chairman, and territory-specific opinion overrides on the active scenario state, and the broader stat-reader family around `company_read_control_transfer_metric_slot` and `company_read_year_or_control_transfer_metric_value` is no longer just a merger-premium helper. Current grounded callers show the same metric family feeding the annual shareholder-revolt and creditor-liquidation lane surfaced by localized ids `300..304`, so the remaining gap is now mostly semantic: the exact player-facing names of the support-and-governance metric slots behind issue ids `0x37` and `0x3a`, plus any later chairmanship-control side effects beyond the already grounded success or failure commit points. The packed simulation calendar tuple semantics also remain open. The `TrackLay.win` family now clearly owns `Lay single track.` `Lay double track.` and `Bulldoze` as its three primary modes, its bridge selector, its wrapped frequency preferences, and a strongly aligned pair of `Auto-Hide Trees During Track Lay` and `Auto-Show Grade During Track Lay` toggles; the `StationPlace.win` family now clearly owns its six top-level category buttons, the station-style scroller, and the station-rotation controls. The older `Building placement center` string 671 no longer looks like a live StationPlace control label in the current recovered flow, because the active constructor, preview, refresh, and dispatcher paths all use neighboring ids such as 669 and 2208 without a direct recovered lookup of 671. On save or load the broad serialize-versus-restore split is now grounded, the non-Quicksave `.gmp/.gmx/.gmc/.gms` families are separated, and the auxiliary `.gmt` path is at least bounded to the preview-surface side owner. The higher-value shell-facing gap has therefore shifted upward to the remaining semantics of the post-load generation phases, the later recurring structure-population cadence, the deeper vote-weight formulas inside takeover and merger resolution, and the still-open meaning of the packed simulation calendar tuple.
|
||
- Editor breadth: the broader map-editor page owner is now bounded through `map_editor_panel_select_active_section` `0x004ce070` and `map_editor_panel_dispatch_active_section_message` `0x004cf700`, which switch among the grounded setup pages, `Cities/Regions`, `Territories`, the `Players` and `Player Pool` setup slices, the now-grounded `Building Density` page, the locomotives-available and industry-availability pages, the economic and special-condition pages, the `Port/Warehouse Cargos` page, and the later report pages. The mid-editor ownership is materially clearer now too: the chairman-slot editor is the `Players` page, the available-chairman editor is the `Player Pool` page, and the former unnamed dual tri-state page now lines up with localized page text `997` `Building Density` plus help text `1017` and the direct field captions `1642` `Starting Building Density Level:` and `1644` `Building Density Growth:`. Both controls are now bounded as stored ordinal bytes `0/1/2` rather than loose labels: the first three-choice control is the map-wide starting-density selector, with its default middle state `1` matching the documented `100%` baseline from `1643`; the second is the paired overall growth selector whose effects later appear in the city-growth side of the 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 copy-industry-data flows; `map_editor_territory_panel_construct` and `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_handle_message` now bound the locomotive policy page over `0x006ada84`; `map_editor_industry_availability_panel_construct` plus `map_editor_industry_availability_panel_handle_message` do the same for the industry candidate pool at `0x0062b268`; and `map_editor_port_warehouse_cargo_panel_construct` plus `map_editor_port_warehouse_cargo_panel_handle_message` now ground the recipe-book page that edits port or warehouse cargo policies through twelve per-book state blocks at `[0x006cec78+0x0fe7+index*0x4e1]`. Each book now has a shared maximum annual production float at `book+0x3ed` and five fixed cargo-line entries starting at `book+0x3f1` with stride `0x30`; each line is bounded as a mode dword, annual amount, a supplied-cargo token at `+0x08`, and a demanded-cargo token at `+0x1c`. The constructor and handler now make those fields materially tighter too: the row pair shown in `Supply Only` and `Production Demand->Supply` is the `+0x08` supplied-cargo selector, the row pair shown in `Demand Only` and `Production Demand->Supply` is the `+0x1c` demanded-cargo selector, and the single amount field at `+0x04` is labeled `Annual Demand:` only in mode `1` but `Annual Supply:` in modes `2/3`. The stronger new runtime-side 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 lines into one repeated array of identical `0xbc`-byte runtime descriptors with no row-index special casing, and the candidate-side rebuild pass at `structure_candidate_collection_rebuild_runtime_records_from_scenario_state` `0x00412d70` then projects those descriptors into the live structure collection at `0x0062ba8c` before `structure_candidate_rebuild_cargo_membership_and_scaled_rate_tables` `0x00411ee0` rebuilds the per-cargo runtime summary tables. The player-facing line modes remain `Disabled`, `Demand Only`, `Supply Only`, and `Production Demand->Supply`, and the fourth mode is tighter now too: current wording around `1674`, `1675`, and `504` plus the downstream scaling path says it is the production-line mode governed by the shared annual production cap, where the entered annual amount stays on the supply side while the demanded side becomes the normalized input branch. The candidate-side accumulator pass reinforces that split by applying the shared production cap only 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` `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 channel, one demand or input channel, and one scaled production-output subrow channel for a requested cargo id; the sibling helper `structure_candidate_supports_or_references_cargo_id` `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 broader collection pass also now ties the editor rule side back into runtime filtering: `structure_candidate_collection_refresh_cargo_economy_filter_flags` `0x0041eac0` rebuilds per-candidate flag `[candidate+0x56]` across the live structure collection, and current grounded callers show it rerunning directly off the runtime cargo-economy latch at `[0x006cec74+0x25f]`, which aligns this lane with the editor's `Disable Cargo Economy` special condition rather than leaving it as a purely editor-owned recipe page. The first common live gate beneath that filter is now bounded too: `structure_candidate_is_enabled_for_current_year` `0x0041e220` is the shared year-and-filter check used by the collection refresh and later candidate-selection branches, while `structure_candidate_rebuild_local_service_metrics` `0x0041e2b0` is a setup-side local service scorer that already consumes the same enabled candidate records through world-grid sampling. One steady-state world-side consumer is now grounded as well: `placed_structure_rebuild_candidate_cargo_service_bitsets` `0x0042c690` walks linked placed structures, filters live category-`2` candidates through `structure_candidate_is_enabled_for_current_year`, and compacts the direct and scaled supply-side channels from `0x00412960` and `0x004129a0` into per-cargo bitsets on the placed-structure record. The next site-side owner layer is tighter now too: `placed_structure_refresh_linked_candidate_flag4` `0x0042c8f0` refreshes the sibling state bit at `[site+0x0e6]`, `placed_structure_refresh_candidate_service_state` `0x0042cdf0` ties that flag refresh to the cargo-service bitset rebuild, `placed_structure_rebuild_candidate_local_service_tables` `0x0042ce00` then performs the heavier per-site candidate score rebuild over the aligned float and word tables at `[site+0x107]`, `[site+0x02]`, and `[site+0x6c]`, and `placed_structure_refresh_local_service_score_bundle` `0x0042d580` is now the local wrapper that chains that rebuild into the neighboring post-passes before the world-grid owner continues. Those post-passes are tighter too: `placed_structure_apply_route_linked_service_caps` `0x0042cc50` is the first route-backed cap pass over the rebuilt local tables, `placed_structure_redistribute_local_service_pressure_from_neighbors` `0x0042c1b0` is the neighboring-site redistribution pass, and `placed_structure_clamp_candidate_service_age_table` `0x0042cb30` is the final recent-service clamp over the primary per-candidate word table. Above that refresh lane, `placed_structure_query_candidate_local_service_metrics` `0x0047e240` is the first higher-level query, `placed_structure_count_candidates_with_local_service_metrics` `0x0047e330` counts how many candidates currently produce that query, and `placed_structure_query_cached_express_service_class_score` `0x0047e390` caches one parallel class score for the express family now strongly aligned with `Passengers`, `Mail`, and `Troops`. Those site-side scores now have grounded shell read-side consumers too: `shell_station_detail_format_freight_and_express_summary` `0x00506be0` formats the visible `Freight: %1` and `Express: %1` lines in `StationDetail.win`, and the same station-detail family now has a deeper candidate-service lane: `shell_station_detail_set_active_candidate_service_preview` `0x00504ae0` stores the active `(station id, candidate id)` pair for the world-side preview scan, `shell_station_detail_clear_active_candidate_service_preview` `0x00504a90` tears that pair back down, `shell_station_detail_update_candidate_service_entry` `0x00504ba0` is the shell-side entry updater above that preview pair, `shell_station_detail_format_candidate_local_service_summary` `0x00504bea` uses `placed_structure_query_candidate_local_service_metrics` together with localized ids `681`, `682`, and `2813` to build the visible candidate service text, `shell_station_detail_build_to_from_haul_summary_widget` `0x00505150` now grounds the paired `To` and `From` hauled-traffic strip widgets, `shell_station_detail_present_to_from_haul_stats_popup` `0x00504770` owns their click-through annual and lifetime `hauled TO/FROM this station` popup, `shell_station_detail_refresh_nearby_structure_jump_rows` `0x00505470` owns the five-row nearby-structure jump lane, and `shell_station_detail_refresh_candidate_service_rows` `0x00505760` is now the larger row-list owner that enumerates active candidate-service entries and wires `shell_station_detail_update_candidate_service_entry` into the per-row click path. `shell_station_list_format_freight_and_express_availability_summary` `0x00506e50` feeds the station-list summary `%1 has %2 freight loads and %3 express loads available for hauling...`, and the paired modifier helper `shell_station_list_handle_center_or_rename_action` `0x00506d50` owns the visible Shift-center and Ctrl-rename row actions. The report side is clearer as well: `0x004d3060` is the dedicated `Stats - Trees` constructor over `map_editor_tree_stats_report`, `0x004d3080` is the actual `General Validation` constructor over `map_editor_general_validation_report`, and the same page table also now grounds `Stats - Cargo`, `Stats - City/Region`, `Stats - City Count`, `Event Variable Values`, and the neighboring event-validation page. The remaining open editor edge is therefore mostly the deeper gameplay meaning of those site-side service scores and flag bits, not page ownership.
|
||
|
||
- Station-detail overlay: the shell-side candidate preview pair now has a grounded world consumer too. `world_render_station_candidate_service_map_overlay` at `0x0043f640` reads the active `(station id, candidate id)` pair from `0x005ee4fc` and `0x005ee500`, scans the placed-structure collection, and then splits the legend by candidate mode. When the active candidate carries a nonzero route-style byte at `[candidate+0x46]`, the overlay uses the heavier helper `placed_structure_query_candidate_directional_route_overlay_summary` at `0x0047e690` in both directions between the preview station and each scanned site and formats the resulting directional rows as `3874` `Coming To %1` and `3875` `Going From %1`. When that byte is zero, the same overlay falls back to the direct local-service path through `placed_structure_query_candidate_local_service_metrics` and formats the two legend rows instead as `3876` `Current Supply @ < %1` and `3877` `Current Demand @ > %1`. Empty directional lanes collapse to `3878` `--None--`, and one title lane falls back to literal `All`. The nearby connection-state notes `3872` and `3873` are still tied to adjacent overlay-side ownership checks, but `3879` `Out of Sync` does not currently appear inside `0x0043f640` itself and remains a separate unresolved sibling owner.
|
||
- Station-detail overlay, corrected boundary: `3879` `Out of Sync` is no longer an unresolved overlay sibling. It is now grounded under the multiplayer preview dataset path instead: `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync` at `0x0046b780` checks global `0x006cd91c`, raises the `Out of Sync` shell status through `0x5386e0`, and then continues through the wider multiplayer preview launch-state service. So the station-detail overlay currently owns only the `Coming To`, `Going From`, `Current Supply`, `Current Demand`, `--None--`, and adjacent connection-state note lanes.
|
||
- Station-detail overlay, ownership side: one reusable site helper is grounded now too. `placed_structure_query_linked_company_id` at `0x0047efe0` resolves the current placed structure's linked instance through `0x0062b26c` and returns its company id from `[instance+0x276]`; the overlay compares that id against the active company selector before deciding whether a scanned site should carry the `Already Connected by Another Company` note. The remaining local gap on this branch is the exact branch that emits the neighboring `Not Connected` note.
|
||
- Station-detail overlay, route-list side: the neighboring helper `placed_structure_append_unique_route_entry` at `0x0047f010` is now grounded as the append-if-missing builder for the six-byte route-entry list rooted at `[site+0x462]` and `[site+0x466]`. That matters here because the directional overlay query at `0x0047e690` consumes the same list, so the remaining uncertainty is no longer list ownership. It is down to the exact semantics of each entry's `u32` payload and the final `Out of Sync` chooser.
|
||
|
||
## Next Mapping Passes
|
||
|
||
- Resolve the owner-side callback roles behind the validated GameSpy packet branches, especially `[route+0x9c]`, `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]`.
|
||
- Determine whether any later world-mode branch beneath the frame owner bypasses the shell controller input path for non-cursor gameplay input, since the current grounded overlay and cursor helpers still reuse `0x006d4018`.
|
||
- Keep detailed pending-template or transport work scoped to the specific atlas edges that remain unresolved.
|