2026-04-02 23:11:15 -07:00
|
|
|
|
# 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.
|
|
|
|
|
|
|
2026-04-06 01:11:06 -07:00
|
|
|
|
Subsystem notes now live under [docs/atlas](/home/jan/projects/rrt/docs/atlas/README.md) for
|
|
|
|
|
|
faster navigation. The longform atlas remains the canonical narrative reference during this
|
|
|
|
|
|
transition.
|
|
|
|
|
|
|
2026-04-02 23:11:15 -07:00
|
|
|
|
## CRT and Process Startup
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and
|
|
|
|
|
|
`app_bootstrap_main` at `0x00484440`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- Trigger/Cadence: single process startup path before shell or engine services exist.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Bootstrap and Shell Service Bring-Up
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- Roots: `app_bootstrap_main` at `0x00484440`, `bootstrap_init_shell_window_services` at
|
|
|
|
|
|
`0x004840e0`, and `shell_install_global_controller` at `0x0051ff90`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Shell UI Command and Deferred Work Flow
|
|
|
|
|
|
|
|
|
|
|
|
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Presentation, Overlay, and Frame Timing
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Map and Scenario Content Load
|
|
|
|
|
|
|
2026-04-08 16:31:33 -07:00
|
|
|
|
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the larger active-mode profile owner
|
|
|
|
|
|
`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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
- Key Dispatchers: `shell_map_file_entry_coordinator`,
|
|
|
|
|
|
`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`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
reference-bundle path when that dataset object is absent. The shell-side mode owner above those
|
|
|
|
|
|
file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode
|
2026-04-10 01:22:47 -07:00
|
|
|
|
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
|
|
|
|
|
|
load-screen call shape is `(4, 0)`, not a one-arg mode switch. The second stack argument is now
|
|
|
|
|
|
tighter too: current local evidence reads it as an old-active-mode teardown flag, because the
|
|
|
|
|
|
`0x482fc6..0x482fff` branch only runs when it is nonzero and then releases the prior active-mode
|
|
|
|
|
|
world through `0x434300`, `0x433730`, and the common free path before clearing `0x006cec78`.
|
|
|
|
|
|
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
|
|
|
|
|
|
hook-driven path `shell_transition_mode(4, 0)` returns cleanly, and the full old-mode teardown
|
|
|
|
|
|
stack under `0x5389c0` now returns too, including `0x5400c0`, the `0x53fe00 -> 0x53f860`
|
|
|
|
|
|
remove-node sweep over `[object+0x74]`, and the nearby mode-`2` teardown helper `0x00502720`.
|
|
|
|
|
|
The same live run now also reaches and returns from `shell_load_screen_window_construct`
|
|
|
|
|
|
`0x004ea620` and the immediate shell publish through `0x00538e50`.
|
|
|
|
|
|
Its constructor jump table is tighter now too: mode `1` is not the direct `Game.win`
|
|
|
|
|
|
constructor, but the startup-dispatch arm rooted at `0x483012`; mode `2` enters `Setup.win`,
|
|
|
|
|
|
mode `3` enters `Video.win`, mode `4` enters the plain `LoadScreen.win` branch at `0x4832e5`,
|
|
|
|
|
|
mode `5` enters `Multiplayer.win`, mode `6` enters `Credits.win`, and mode `7` enters
|
|
|
|
|
|
`Campaign.win`. The important static correction is that the startup-runtime slice
|
|
|
|
|
|
`0x004ea710 -> 0x0053b070(0x46c40) -> 0x004336d0 -> 0x00438890` belongs to mode `1`, not mode
|
|
|
|
|
|
`4`. Mode `4` only constructs and publishes the `LoadScreen.win` object through `0x004ea620`
|
|
|
|
|
|
and `0x00538e50`. The older hook-driven `(4, 0)` path therefore was not mysteriously skipping
|
|
|
|
|
|
the startup-runtime object; it was entering the wrong jump-table arm for that work. The caller
|
|
|
|
|
|
split above that owner is tighter now too:
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`world_entry_transition_and_runtime_bringup` reaches the same owner at `0x443b57` with `(0, 0)`
|
|
|
|
|
|
after dismissing the current shell detail panel and servicing `0x4834e0(0, 0)`, while the
|
|
|
|
|
|
saved-runtime path at `0x446d7f` does the same before immediately building the `.smp` bundle
|
|
|
|
|
|
payloads through `0x530c80/0x531150/0x531360`. That makes the `LoadScreen.win` startup lane the
|
|
|
|
|
|
only currently grounded caller that enters `0x438890` with `(1, 0)` instead of `(0, 0)`. The
|
2026-04-10 01:22:47 -07:00
|
|
|
|
remaining runtime uncertainty is narrower now too: the plain-run logs still show the plain
|
|
|
|
|
|
`LoadScreen.win` state under `(4, 0)`, and the corrected allocator-window run now reinforces the
|
|
|
|
|
|
same read because the post-construct allocator stream stays empty instead of showing the expected
|
|
|
|
|
|
`0x46c40` startup-runtime allocation. The first lower allocator probe on `0x005a125d` was not
|
|
|
|
|
|
trustworthy because that shared cdecl body sits behind the thunk and the initial hook used the
|
|
|
|
|
|
wrong entry shape, and the first direct thunk hook was also not trustworthy because a copied
|
|
|
|
|
|
relative-`jmp` thunk cannot be replayed through an ordinary trampoline. But the later corrected
|
|
|
|
|
|
thunk run plus the jump-table decode now agree: the next meaningful hook-driven test is mode
|
|
|
|
|
|
`1`, not mode `4`.
|
|
|
|
|
|
`mode_id = 2`, and it never advanced into `ready gate passed`, staging, or transition. So that
|
|
|
|
|
|
run did not actually exercise the `0x0053b070 -> 0x004336d0 -> 0x00438890` subchain at all.
|
|
|
|
|
|
The next runtime pass now lowers the ready-poll defaults to `1` and `0` and adds an explicit
|
|
|
|
|
|
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
|
|
|
|
|
|
`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
|
|
|
|
|
|
`0x46c40` allocator hit, no direct `0x004336d0` entry, and no direct `0x00438890` entry. So
|
|
|
|
|
|
the next clean runtime boundary is the tiny `LoadScreen.win` scalar setter at `0x004ea710`,
|
|
|
|
|
|
which sits immediately before the `0x0053b070` allocation in the static mode-`4` branch. The
|
|
|
|
|
|
immediate next runtime check is even more concrete than the helper hook, though: inspect the
|
|
|
|
|
|
state that `0x004ea710` should leave behind. The hook now logs the post-transition
|
|
|
|
|
|
`LoadScreen.win` singleton, its field `[+0x78]`, `0x006cec78`, the shell state's `[+0x0c]`
|
|
|
|
|
|
active-mode object field, and the startup selector. If `0x004ea710` really ran on the mode-`4`
|
|
|
|
|
|
branch, `[LoadScreen.win+0x78]` should no longer be zero after `shell_transition_mode` returns.
|
|
|
|
|
|
The latest run answered that directly: after transition return, `field_active_mode_object` is
|
|
|
|
|
|
still the `LoadScreen.win` singleton, `0x006cec78` is still null, `[LoadScreen.win+0x78]` is
|
|
|
|
|
|
still zero, and the startup selector is still `3`. So the current best read is that RT3 is
|
|
|
|
|
|
still parked in the plain `LoadScreen.win` state at transition return rather than having entered
|
|
|
|
|
|
the separate runtime-object path yet. That shifts the best next runtime boundary from “deeper
|
|
|
|
|
|
inside `shell_transition_mode`” to “what later active-mode service tick, if any, promotes the
|
|
|
|
|
|
load-screen object into the startup-dispatch path.” The next run now logs the first few
|
|
|
|
|
|
shell-state service ticks after auto-load is attempted with that same state tuple
|
|
|
|
|
|
(`0x006cec78`, `[shell_state+0x0c]`, `0x006d10b0`, `[LoadScreen.win+0x78]`, selector), so the
|
|
|
|
|
|
next question is very narrow: does one later service tick finally promote the plain
|
|
|
|
|
|
`LoadScreen.win` state into the startup-runtime object path, or does it stay frozen as-is? The
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
|
|
|
|
|
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`,
|
|
|
|
|
|
`Multiplayer.win` writes selector `3` on one pending-status path, and the larger `Setup.win`
|
|
|
|
|
|
dispatcher writes selectors `2`, `3`, `4`, and `5` on its validated launch branches. That makes
|
|
|
|
|
|
the file-load subfamily read less like one generic save-open branch and more like a shared
|
|
|
|
|
|
profile-file lane reused by setup, multiplayer, and campaign owners. The world-entry owner
|
|
|
|
|
|
boundary is tighter now too: `world_entry_transition_and_runtime_bringup` at `0x00443a50` no
|
|
|
|
|
|
longer stops at the initial shell transition and world allocation head. The same grounded
|
|
|
|
|
|
function continues through the larger post-load generation tail up to `0x00444dc2`, which means
|
|
|
|
|
|
the later `Setting up Players and Companies...` and neighboring post-load passes are not
|
|
|
|
|
|
floating raw callsites after all. That same owner now clearly covers the event-runtime refresh
|
|
|
|
|
|
through `0x433130`, chairman-profile materialization through `0x437220`, neighboring route and
|
|
|
|
|
|
tracker refresh families, and the one-shot kind-`8` runtime-effect service through `0x432f40`
|
|
|
|
|
|
before clearing shell-profile latch `[0x006cec7c+0x97]`. The `Setup.win` dispatcher
|
|
|
|
|
|
is less opaque now too: the early `0x0bc1..0x0c24` family is mostly fixed submode selection above
|
|
|
|
|
|
`0x00502c00`, except for the separate `0x0bc2/0x0bc5/0x0bc6/0x0bc7` shell-open quartet above
|
|
|
|
|
|
`0x00501f20`; `0x0c1f` is the settings-window escape; `0x0c1e/0x0c20/0x0c22` are direct shell
|
|
|
|
|
|
requests into `0x00482150`; the fixed submode buttons now have concrete lower targets such as
|
|
|
|
|
|
`0x0bc1/0x0bc8 -> 15`, `0x0bc3 -> 16`, `0x0bc4 -> 1`, `0x0c80 -> 13`, `0x0c1c -> 17`,
|
|
|
|
|
|
`0x0c1d -> 2`, `0x0c24 -> 14`, `0x0c81 -> 14`, `0x0c82 -> 6`, `0x0c83 -> 10`, and
|
|
|
|
|
|
`0x0c84 -> 12`; the
|
|
|
|
|
|
`0x0ce6/0x0ce7/0x0d49/0x0d4a/0x0e82/0x0e83` branches are bounded list or slider adjustments on
|
|
|
|
|
|
staged setup fields; and the later `0x0dca/0x0dcb/0x0de9/0x0df3/0x0e81/0x0f6f/0x0f70` controls
|
|
|
|
|
|
are the explicit selector-writing launch buttons rather than one anonymous validated-launch blob.
|
|
|
|
|
|
The constructor-side callbacks are tighter too: control `0x0ce8` is a table-driven payload-label
|
|
|
|
|
|
draw callback above `0x00502030`, not another launch root, and controls `0x0e86` and `0x0e87`
|
|
|
|
|
|
do not select more setup roots; they update the persisted shell-state selector pairs at
|
|
|
|
|
|
`[0x006cec74+0x233/+0x237]` and `[0x006cec74+0x23b/+0x23f]` through `0x00502160` and
|
|
|
|
|
|
`0x005021c0`, then immediately save config through `0x00484910(1)`. The constructor body is
|
|
|
|
|
|
tighter too: it seeds the initial `Setup.win` state by running `0x502910`, `0x502550`, and
|
|
|
|
|
|
`0x502c00(1)` before the user interacts with the window, installs `0x0c80..0x0c86` and
|
|
|
|
|
|
`0x0f6f..0x0f71` as homogeneous button bands, and treats `0x0e88` as one separate special
|
|
|
|
|
|
control with retuned float fields rather than another ordinary launch root. The remaining
|
|
|
|
|
|
optional constructor child `0x0bd0` is tighter now too: it is built from a separate template
|
|
|
|
|
|
block and optional owned heap object before registration, not another hidden setup-root button.
|
|
|
|
|
|
The generic shell helper layer beneath that constructor is tighter now too: `0x53fa50` is the
|
|
|
|
|
|
shared resource-bind and child-list initialization helper, `0x53f830` is the child-control
|
|
|
|
|
|
lookup-by-id helper over the intrusive list at `[this+0x70]`, `0x558130` is the child-control
|
|
|
|
|
|
finalizer that stamps the owner pointer and resolves localized captions before the control goes
|
|
|
|
|
|
live, and `0x53f9c0` is the ordered child-control registration helper used by the optional
|
|
|
|
|
|
`0x0bd0` branch and other shell dialogs.
|
|
|
|
|
|
The submode selector itself is tighter now too because its button-state lane is mostly decoded:
|
|
|
|
|
|
`0xbc5` tracks submode `15`, `0xbc6` tracks `16`, `0xbba` tracks `2`, `0xbbb` tracks `3`,
|
|
|
|
|
|
`0xbc3` tracks `13`, `0xbbc` groups `3/4/5`, `0xbbd` tracks `4`, `0xbbe` tracks `5`, `0xbbf`
|
|
|
|
|
|
groups `6/12/14`, `0xbc0` tracks `7`, `0xbc1` tracks `8`, `0xbc2` tracks `9`, `0xe75` tracks
|
|
|
|
|
|
`10`, and `0xf6e` tracks `17`. RT3.lng and the setup art families now also make several of those
|
|
|
|
|
|
top-level roots read less like anonymous ids and more like real menu panels: submode `1` is the
|
|
|
|
|
|
strongest current landing-panel fit, `2` is the `Single Player` root, `7/8/9` are the
|
|
|
|
|
|
`Editor` / `New Map` / `Load Map` family, `15` is `Extras`, and `17` is `Tutorial`. By
|
|
|
|
|
|
elimination against the separate shell `Credits.win` mode, the remaining top-level `Setup.win`
|
|
|
|
|
|
roots now most safely read as `13 = Multi Player` and `16 = High Scores`.
|
|
|
|
|
|
The file-backed side is tighter too: `0x502c00` now maps submodes exactly as `4 -> dataset 5`,
|
|
|
|
|
|
`9 -> 4`, `6 -> 8`, `10 -> 6`, `12 -> 10`, and `14 -> 9`, so the saved-game-backed family is
|
|
|
|
|
|
no longer one blurred list pane but a small set of stable dataset variants above
|
|
|
|
|
|
`0x4333f0/0x4336a0`.
|
|
|
|
|
|
The file-backed header split is tighter too: `0x5027b0` now maps the top header-control ids as
|
|
|
|
|
|
`4 -> 0xd4b`, `9 -> 0xde8`, `6/12/14 -> 0xdf2`, and `10 -> 0xe85`. RT3.lng closes one earlier
|
|
|
|
|
|
mistake here: those values are local setup control or resource ids, not localized text ids.
|
|
|
|
|
|
The setup art bundle now tightens that family split one step further too: under `rt3_2WIN.PK4`
|
|
|
|
|
|
the distinct file-backed setup art families are the shared `Setup_Load` lane and the separate
|
|
|
|
|
|
`Setup_Sandbox` lane, which matches the selector-side evidence that mode `10` is the
|
|
|
|
|
|
sandbox-backed `new` list while mode `12` is its `load` sibling. Combined with the builder at
|
|
|
|
|
|
`0x4333f0`, that shows only submodes `4` and `10` using the alternate localized-stem list-label
|
|
|
|
|
|
path; `6`, `9`, `12`, and `14` keep the direct filename-normalization lane.
|
2026-04-10 01:22:47 -07:00
|
|
|
|
The `rt3_2WIN.PK4` extraction path is grounded a bit more cleanly now too: current `pack4`
|
|
|
|
|
|
samples use a shared `0x03eb` header, a fixed `0x4a`-byte directory entry stride, and a payload
|
|
|
|
|
|
base at `8 + entry_count * 0x4a`. In the checked UI bundle the entry table is contiguous and
|
|
|
|
|
|
`Campaign.win` extracts cleanly at directory index `3` with payload length `0x306a`.
|
|
|
|
|
|
The extracted `.win` payload family now has one narrow shared shape too: `Campaign.win`,
|
|
|
|
|
|
`CompanyDetail.win`, and `setup.win` all share the same first `0x50` bytes at offsets
|
|
|
|
|
|
`0x00`, `0x0c`, `0x10`, `0x14`, `0x34`, `0x38`, `0x40`, and `0x48`, while
|
|
|
|
|
|
`CompanyDetail.win` and `setup.win` also carry an inline root `.imb` name immediately at
|
|
|
|
|
|
`0x51`. Current resource scans show `Campaign.win` embedding the `litCamp*/Ribbon*` family,
|
|
|
|
|
|
`CompanyDetail.win` embedding mainly `CompanyDetail.imb`, `GameWindow.imb`, and `Portrait.imb`,
|
|
|
|
|
|
and `setup.win` embedding the broader `Setup_Background/Buttons/New_Game/Load/Sandbox` art
|
|
|
|
|
|
families. The control records between those strings are still undecoded, but the resource-record
|
|
|
|
|
|
shell itself is tighter now: all checked `.win` samples use the same three-word prelude prefix
|
|
|
|
|
|
`0x0bb8, 0x0, 0x0bb9`, and the fourth prelude word matches `resource_name_len + 1`. The next
|
|
|
|
|
|
word after the terminating NUL then behaves like a per-record selector lane. In `setup.win`
|
|
|
|
|
|
the dominant `Setup_Buttons.imb` family alternates between `0x00040000` and the incrementing
|
|
|
|
|
|
`0x00010c1c..0x00010c86` series with dominant inter-record strides `0xb7/0xdb`; in
|
|
|
|
|
|
`Campaign.win` the `litCamp*/Ribbon*` family carries the incrementing `0x0004c372..0x0004c38e`
|
|
|
|
|
|
selector block with dominant `0x158/0x159` and `0xb2/0xb3` strides. That campaign lane is now
|
|
|
|
|
|
directly aligned to the executable-side control ids too: the low 16 bits of
|
|
|
|
|
|
`litCamp1..16` map exactly to `0xc372..0xc381`, and the low 16 bits of `Ribbon1..16` map
|
|
|
|
|
|
exactly to `0xc382..0xc391`, matching the `Campaign.win` progress and selector control bases
|
|
|
|
|
|
already grounded from `RT3.exe`. The fuller selector export now tightens the auxiliary families
|
|
|
|
|
|
too: `litArrows.imb` covers `0xc36c..0xc371` exactly and `litExits.imb` covers
|
|
|
|
|
|
`0xc397..0xc39a` exactly, matching the constructor-side six-control arrow strip and four-control
|
|
|
|
|
|
exit strip. The second post-name dword is tighter now too: its middle 16-bit lane groups those
|
|
|
|
|
|
same `Campaign.win` records under `0xc368`, `0xc369`, `0xc36a`, and `0xc36b`, which matches the
|
|
|
|
|
|
four page-strip controls and cleanly buckets the first five campaign entries, the next five, the
|
|
|
|
|
|
next three, and the final three under the same page families. There are still no `.imb`
|
|
|
|
|
|
selector records for `0xc393..0xc396` or `0xc39b`, which makes those message-side page-write
|
|
|
|
|
|
controls look more like structural buttons than art-backed repeated resource records.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
The grouped `3/4/5` family is narrower now too: `0x0ce5` is no longer part of it, because that
|
|
|
|
|
|
control writes selector `3` and then selects submode `9` as the `Load Map` sibling. The nearby
|
|
|
|
|
|
`0x0dcb` branch instead conditionally selects submode `5` or `3`, which keeps `3` as the
|
|
|
|
|
|
strongest current `New Game` / `Options` companion and `5` as the strongest current `Sandbox`
|
|
|
|
|
|
companion. The file-backed single-player side is tighter in the same way now: modes `4` and `10`
|
|
|
|
|
|
are the only siblings using the alternate localized-stem row-label path, so they now read most
|
|
|
|
|
|
safely as the two setup-local template or profile list variants rather than ordinary save lists.
|
|
|
|
|
|
Mode `10` is the stronger one of the pair because neighboring validated launch control `0x0e81`
|
|
|
|
|
|
both routes into selector `5` and sets sandbox byte `[0x006cec7c+0x82] = 1`, which makes it the
|
|
|
|
|
|
strongest current fit for the setup-local `New Sandbox` list. The distinct `Setup_Sandbox` art
|
|
|
|
|
|
family in `rt3_2WIN.PK4` now reinforces that same split one step further, which makes mode `12`
|
|
|
|
|
|
the strongest closed fit for the paired `Load Sandbox` lane; mode `4` is therefore the strongest
|
|
|
|
|
|
remaining non-sandbox peer in that same pair and now most safely reads as the setup-local `New
|
|
|
|
|
|
Scenario`-style chooser. Modes `6`, `12`, and `14` now tighten one step further as the three
|
|
|
|
|
|
selector-`3` direct-filename setup-local `load` siblings because they stay on the direct
|
|
|
|
|
|
filename-normalization lane, share the same `0xdf2` header family, and clear the same
|
|
|
|
|
|
presence-style latch `[0x006cec7c+0x97]`; mode `14` is the strongest current landing panel for
|
|
|
|
|
|
that cluster because `0x0c24` jumps to it directly while `0x0c82` and `0x0c84` only reach the
|
|
|
|
|
|
sibling modes `6` and `12` from inside the same load family. Current control-pairing and
|
|
|
|
|
|
setup-art evidence now make `12 = Load Sandbox` the strongest closed per-submode assignment. The
|
|
|
|
|
|
remaining non-sandbox pair is closed now too: the deeper bundle filter at `0x433260`
|
|
|
|
|
|
distinguishes dataset `9` from dataset `8` by one extra nonzero payload-flag family, and the
|
|
|
|
|
|
editor-side metadata path now grounds `[0x006cec78+0x66de]` as the direct `Campaign Scenario`
|
|
|
|
|
|
checkbox bit because `editorDetail.win` ties control `0x5b6e` to localized ids `3160/3161`.
|
|
|
|
|
|
That makes dataset `9` the campaign-designated load family and dataset `8` the ordinary scenario
|
|
|
|
|
|
load family, so `14 = Load Campaign` and `6 = Load Scenario` now read as grounded rather than
|
|
|
|
|
|
residual. `0x502910` is
|
|
|
|
|
|
tighter in a
|
|
|
|
|
|
corrective way too: it is not a mode-`3`-only helper after all, but the shared non-file-backed
|
|
|
|
|
|
payload panel that formats
|
|
|
|
|
|
`0xcf3/0xcf4/0xcf5/0xd4f`, mirrors option byte `[0x006cec7c+0x7d]` into `0x0ce9..0x0ced`,
|
|
|
|
|
|
rebuilds row host `0x0ce8` from payload bytes `[+0x31b/+0x31c]`, and mirrors live row markers
|
|
|
|
|
|
into `[0x006cec7c+0x87]`. That makes mode `3` just one user of the shared payload-driven panel,
|
|
|
|
|
|
not the sole owner of it.
|
|
|
|
|
|
The payload-helper cluster under that panel is tighter now too: `0x502220` does not just
|
|
|
|
|
|
republish labels and the preview surface. It first re-enters
|
|
|
|
|
|
`shell_setup_load_selected_profile_bundle_into_payload_record` `0x442400`, which clears one full
|
|
|
|
|
|
`0x100f2`-byte setup payload record, builds a rooted path from the staged profile stem, opens the
|
|
|
|
|
|
selected bundle through `0x530c80`, and then reads either the ordinary saved-profile chunk family
|
|
|
|
|
|
or the map-style chunk family through `0x531150/0x531360` depending on the selected extension
|
|
|
|
|
|
shape. Only after that does `0x502220` copy payload fields `+0x14/+0x3b2/+0x3ba/+0x20` into the
|
|
|
|
|
|
staged runtime profile through `0x47be50`, which in turn normalizes the payload category bytes at
|
|
|
|
|
|
`[payload+0x31a + row*9]` and the local marker-slot bytes at `[payload+0x2c9..]` through
|
2026-04-10 01:22:47 -07:00
|
|
|
|
`0x47bc80`. The copied-field consumer split is tighter now too: payload `+0x14` becomes staged
|
|
|
|
|
|
profile `[0x006cec7c+0x77]`, which is the visible setup scalar later formatted into controls
|
|
|
|
|
|
`0x0e84` and `0x0d4f` and adjusted by the `0x0d49/0x0d4a` pair; payload `+0x3b2` becomes
|
|
|
|
|
|
`[0x006cec7c+0x79]`, the live-row threshold that the payload-row draw callback uses to choose
|
|
|
|
|
|
style slot `0x18` versus `0x19`; and payload `+0x3ba` becomes `[0x006cec7c+0x7b]`, the current
|
|
|
|
|
|
scroll or row-index lane adjusted by the `0x0ce6/0x0ce7` pair. So the known setup-side payload
|
|
|
|
|
|
consumers still stop well before the later candidate table, but the early copied lanes are no
|
|
|
|
|
|
longer anonymous. The adjacent region-side worker family is tighter in a negative way too: the setup
|
|
|
|
|
|
payload path now gives one useful upper bound on the newer candidate-availability source block
|
|
|
|
|
|
too. The map-style setup loader is definitely pulling chunk families `0x0004/0x2ee0/0x2ee1`
|
|
|
|
|
|
into one large `0x100f2`-byte payload record, and the fixed `0x6a70..0x73c0` candidate table
|
|
|
|
|
|
clearly lives inside the same broad file family; but the grounded setup-side consumers we can
|
|
|
|
|
|
actually name after that load still only touch earlier payload offsets such as `+0x14`,
|
|
|
|
|
|
`+0x20`, `+0x2c9`, `+0x31a`, `+0x3ae`, `+0x3b2`, and `+0x3ba`. So the current evidence is
|
|
|
|
|
|
strong enough to say the candidate table is probably housed inside the setup payload family, but
|
|
|
|
|
|
not yet strong enough to name one setup-side consumer that reads the `0x6a70` header or the
|
|
|
|
|
|
fixed-width entries directly. The newer fixed-offset compare pass tightens that lower setup slice
|
|
|
|
|
|
too: across the checked map/save pairs `Alternate USA.gmp -> Autosave.gms`,
|
|
|
|
|
|
`Southern Pacific.gmp -> p.gms`, and `Spanish Mainline.gmp -> g.gms`, the known setup payload
|
|
|
|
|
|
lanes `+0x14` and `+0x3b2` are preserved map-to-save on the same scenario-family split as the
|
|
|
|
|
|
later candidate-table headers (`0x0771/0x0001`, `0x0746/0x0006`, `0x0754/0x0001`), while
|
|
|
|
|
|
`+0x3ae` stays fixed at `0x0186` and `+0x3ba` stays fixed at `1` across all six files. By
|
|
|
|
|
|
contrast, `+0x20` does not survive as one shared source value (`0xd3 -> 0xb4`,
|
|
|
|
|
|
`0x6f -> 0x65`, `0xe3 -> 0x78` across those same pairs). So the current best read is that the
|
|
|
|
|
|
setup payload mixes preserved scenario metadata and later save-variant state well before the
|
|
|
|
|
|
`0x6a70` candidate table, rather than acting as one uniformly copied prelude.
|
|
|
|
|
|
The adjacent region-side worker family is tighter in a negative way too: the setup
|
2026-04-08 16:31:33 -07:00
|
|
|
|
payload loader is now clearly separate from the broader region-building and placement cluster
|
|
|
|
|
|
around `0x422320..0x423d30`, whose current grounded helpers now include `0x422320`, `0x4228b0`,
|
|
|
|
|
|
`0x422900`, `0x422a70`, `0x422be0`, `0x422ee0`, `0x4234e0`, `0x4235c0`, and
|
|
|
|
|
|
`world_region_refresh_cached_category_totals_and_weight_slots` `0x423d30` rather than one hidden
|
|
|
|
|
|
setup-only panel. The leading region helper is no longer unnamed either: `0x422320` is now
|
|
|
|
|
|
bounded as the recurring cached-structure-scalar normalization pass that writes
|
|
|
|
|
|
`[region+0x2e2/+0x2e6/+0x2ea/+0x2ee]`, while `0x422a70` is the shared placement-validation and
|
|
|
|
|
|
commit gate beneath both the per-region worker and one second world-side placement loop. The
|
|
|
|
|
|
neighboring `0x4234e0` accessor is tighter too: it is the shared projected structure-count scalar
|
|
|
|
|
|
query by category that later feeds both the per-region placement family and the map-editor city
|
|
|
|
|
|
count stats report. So the remaining gap is no longer “what are these setup payload helpers
|
|
|
|
|
|
doing,” but only how aggressive we want to be when naming the last top-level setup roots from
|
|
|
|
|
|
mostly RT3.lng and asset-side evidence.
|
|
|
|
|
|
The adjacent summary helper is tighter too: `0x502550` is not another hidden submode owner. It
|
|
|
|
|
|
republishes the staged path tail in `0xe7f`, the scalar summary in `0xe84`, the two persisted
|
|
|
|
|
|
selector lists in `0xe86/0xe87`, and the config toggles in `0xe88/0xe89/0xe8a`.
|
|
|
|
|
|
The remaining top-level gap is cleaner now too: submode `16` still has no distinct downstream
|
|
|
|
|
|
helper or launch branch in the local selector/refresh family, but it is no longer a blank bucket.
|
|
|
|
|
|
Current evidence now bounds it as the `0x0bc3 -> 16` top-level button whose visual-state lane is
|
|
|
|
|
|
surfaced through control `0xbc6`, and the strongest residual fit is `High Scores` because
|
|
|
|
|
|
`Credits` already lives in separate shell mode `6`.
|
|
|
|
|
|
The validated launch lane is tighter now too: it no longer just writes selector `3` or `5` as
|
|
|
|
|
|
one undifferentiated blob, and it no longer includes `0x0ce5` or `0x0dcb`. `0x0ce5` is the
|
|
|
|
|
|
`Load Map` selector because it writes selector `3` and then selects submode `9`, while `0x0dcb`
|
|
|
|
|
|
is the later conditional `5/3` companion-selector sibling rather than a file launch. The actual
|
|
|
|
|
|
validated staged-profile lane is now bounded more narrowly: `0x0cf6` and `0x0e81` are the
|
|
|
|
|
|
selector-`5` siblings, while the neighboring selector-`3` validated lane is at least shared by
|
|
|
|
|
|
`0x0de9` and `0x0df3`; the shared bridge at `0x4425d0` then forces `[0x006cec7c+0xc5] = 1`,
|
|
|
|
|
|
mirrors payload bytes into `[profile+0xc4]`, `[profile+0x7d]`, and `[profile+0xc6..+0xd5]`, and
|
2026-04-10 01:22:47 -07:00
|
|
|
|
only then issues shell request `0x0cc`. The file-side staging bytes in that bridge are now
|
|
|
|
|
|
tighter too: across the checked `Alternate USA`, `Southern Pacific`, and `Spanish Mainline`
|
|
|
|
|
|
map/save pairs, payload `+0x33` stays `0`, but payload `+0x22` and token block `+0x23..+0x32`
|
|
|
|
|
|
do not preserve the earlier map-to-save pairing seen at `+0x14/+0x3b2`. Instead they split by
|
|
|
|
|
|
the finer file family, with examples `Alternate USA.gmp = 0x53 / 0131115401...` versus
|
|
|
|
|
|
`Autosave.gms = 0xae / 01439aae01...`, `Southern Pacific.gmp = 0xeb / 00edeeeb...` versus
|
|
|
|
|
|
`p.gms = 0x21 / 0100892101...`, and `Spanish Mainline.gmp = 0x5b / 0044f05b...` versus
|
|
|
|
|
|
`g.gms = 0x7a / 0022907a...`. So the validated launch bridge now looks more like a file-family
|
|
|
|
|
|
staging lane than a simple copy-forward of the earlier setup summary fields. The destination-side
|
|
|
|
|
|
consumers are tighter now too: `[profile+0xc4]` is no longer just an unnamed staged byte, but the
|
|
|
|
|
|
campaign-progress slot later consumed by the numbered `%s%02d.gmc` save helper `0x00517c70`;
|
|
|
|
|
|
`[profile+0x7d]` is the same compact option byte mirrored back into the `Setup.win` option
|
|
|
|
|
|
controls `0x0ce9..0x0ced`; and the campaign-side selector block is no longer only a one-byte
|
|
|
|
|
|
anchor. Direct `RT3.exe` disassembly of the campaign-side dispatcher at `0x004b89c0` now shows
|
|
|
|
|
|
the exact mirror shape: when the local campaign-page selector `[window+0x78] <= 4`, the helper
|
|
|
|
|
|
writes one highlighted progress control `0x0c372 + [profile+0xc4]` and then mirrors the full
|
|
|
|
|
|
sixteen-byte band `[profile+0xc6..+0xd5]` into controls `0x0c382..0x0c391` through repeated
|
|
|
|
|
|
`0x540120` calls. The same body also treats `[profile+0xc4]` as an index into the fixed
|
|
|
|
|
|
sixteen-entry scenario-name table at `0x00621cf0`, whose observed entries include `Go West!`,
|
|
|
|
|
|
`Germantown`, `Central Pacific`, `Texas Tea`, `War Effort`, `State of Germany`, `Britain`,
|
|
|
|
|
|
`Crossing the Alps`, `Third Republic`, `Orient Express`, `Argentina Opens Up`,
|
|
|
|
|
|
`Rhodes Unfinished`, `Japan Trembles`, `Greenland Growing`, `Dutchlantis`, and
|
|
|
|
|
|
`California Island`. On the launch-side branch the same helper writes startup selector `6`,
|
|
|
|
|
|
copies the resolved campaign filename into `[profile+0x11]`, forces `[profile+0xc5] = 1`, and
|
|
|
|
|
|
then issues shell request `0x0cc`. The lower refresh tail at `0x004b8d49..0x004b8d69` also
|
|
|
|
|
|
shows the observed page-band split over the same progress byte: values `< 5` pair with campaign
|
|
|
|
|
|
page `1`, values `5..9` with page `2`, values `10..12` with page `3`, and values `>= 13` with
|
|
|
|
|
|
page `4`. So `[profile+0xc6..+0xd5]` is no longer a generic opaque span at all, but the staged
|
|
|
|
|
|
sixteen-byte per-scenario campaign selector or unlock band consumed directly by `Campaign.win`.
|
|
|
|
|
|
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
|
|
|
|
|
|
the shared selector or launch branch, `0x0c362..0x0c367` force local page `1`,
|
|
|
|
|
|
`0x0c368..0x0c392` force local page `2`, `0x0c393..0x0c396` force local page `0`, and
|
|
|
|
|
|
`0x0c39b` alone forces local page `3`. That matches the constructor and refresh shape: the
|
|
|
|
|
|
sixteen scenario selector controls live at `0x0c352..0x0c361`, the four page-strip controls
|
|
|
|
|
|
live at `0x0c368..0x0c36b`, the six-arrow auxiliary strip lives at `0x0c36c..0x0c371`, the
|
|
|
|
|
|
progress band lives at `0x0c372..0x0c381`, the ribbon or selector band lives at
|
|
|
|
|
|
`0x0c382..0x0c391`, the string or voice target lane lives at `0x0c392`, the four exit controls
|
|
|
|
|
|
live at `0x0c397..0x0c39a`, and `0x0c39b` remains the one-off special action control that
|
|
|
|
|
|
spawns or dismisses the campaign-side companion object. The `Campaign.win` blob now exposes that
|
|
|
|
|
|
structural split directly too: alongside the art-backed `0x0004c3xx` records it carries
|
|
|
|
|
|
anonymous zero-name records with the same `0x0bb8 / 0 / 0x0bb9` prelude but selector words in
|
|
|
|
|
|
the `0x0010c3xx` family. Current local extraction shows exactly
|
|
|
|
|
|
`0x0010c352..0x0010c361`, `0x0010c362..0x0010c367`, `0x0010c393..0x0010c396`, and one
|
|
|
|
|
|
`0x0010c39b` record, which is the same non-`.imb` band the message dispatcher treats as the
|
|
|
|
|
|
structural selector and page-write family. Their second selector word then buckets those records
|
|
|
|
|
|
under the same page-strip ids as the art-backed records: `0xc368` for
|
|
|
|
|
|
`0xc352..0xc356`, `0xc362`, `0xc393`, and `0xc39b`; `0xc369` for `0xc357..0xc35b`,
|
|
|
|
|
|
`0xc363`, and `0xc394`; `0xc36a` for `0xc35c..0xc35e`, `0xc365..0xc366`, and `0xc395`; and
|
|
|
|
|
|
`0xc36b` for `0xc367`, `0xc35f..0xc361`, and `0xc396`. One final anonymous record at
|
|
|
|
|
|
`0x0002c392` has no page bucket at all, which fits the already-grounded read of `0xc392` as the
|
|
|
|
|
|
one-off string or voice target lane rather than a normal page-cell control. The anonymous body
|
|
|
|
|
|
layout is only partially named, but one file-side distinction is already stable: the ordinary
|
|
|
|
|
|
structural selector/page records all carry footer words `0xd3000000` and `0xd2000007` at
|
|
|
|
|
|
relative `+0x98/+0x9c`, while the lone `0xc392` record carries `0x00000000/0x00000000` there.
|
|
|
|
|
|
So `0xc392` is no longer just “outside the page buckets”; it is also the only current
|
|
|
|
|
|
anonymous-record outlier in that trailing structural footer pair. The structural selector side is
|
|
|
|
|
|
tighter now too: `0xc352..0xc361` partition exactly as `5 + 5 + 3 + 3` across page buckets
|
|
|
|
|
|
`0xc368..0xc36b`, matching the observed campaign page bands for scenario progress values `< 5`,
|
|
|
|
|
|
`5..9`, `10..12`, and `>= 13`. That makes the anonymous selector records the file-side mirror of
|
|
|
|
|
|
the same campaign page split, not a separate unrelated control family. The file-order adjacency
|
|
|
|
|
|
is tighter in the same way: `0xc352..0xc361` each sit immediately after one `RibbonN.imb`
|
|
|
|
|
|
record and before the next `litCamp` record, which makes them the strongest current file-side fit
|
|
|
|
|
|
for the structural selector or click-target siblings of the visible campaign ribbon controls.
|
|
|
|
|
|
The auxiliary anonymous bands line up the same way: `0xc362..0xc367` sit inside the local
|
|
|
|
|
|
`litArrows.imb` clusters, `0xc393..0xc396` sit inside the `litExits.imb` clusters, and
|
|
|
|
|
|
`0xc39b` sits once between the first arrow and first exit group. So the current best read is
|
|
|
|
|
|
that those anonymous records are the structural hitbox or action siblings of the visible arrow,
|
|
|
|
|
|
exit, and selector art families, not an unrelated hidden control layer. The generic record
|
|
|
|
|
|
cadence is tighter now too: in the current `Campaign.win` dump the ordinary anonymous structural
|
|
|
|
|
|
records all span `0x0a7` bytes, while the named art-backed records cluster at `0x0b1`, `0x0b2`,
|
|
|
|
|
|
and `0x0b3`. The only two visible outliers are the leading `0x0080c351` record at `0x0041`
|
|
|
|
|
|
with span `0x0a3`, and the trailing `0x0002c392` record at `0x2fb9` with span `0x0b1`. That
|
|
|
|
|
|
reinforces the current read that `0xc351` and `0xc392` are one-off structural controls flanking
|
|
|
|
|
|
the main selector or page family rather than ordinary repeated art-backed records.
|
|
|
|
|
|
The setup-launch corpus now tightens one corrective caveat there too: when the checked
|
|
|
|
|
|
`Alternate USA`, `Southern Pacific`, and `Spanish Mainline` `.gmp/.gms` files are decoded with
|
|
|
|
|
|
that same campaign-side interpretation, payload byte `+0x22` is always outside the known
|
|
|
|
|
|
campaign progress range `0..15` (`0x53`, `0xeb`, `0x5b`, `0xae`, `0x21`, `0x7a`), so the
|
|
|
|
|
|
ordinary validated setup lane is not simply staging a normal campaign-screen state. The paired
|
|
|
|
|
|
token block `+0x23..+0x32` is still structurally compatible with the campaign selector band, but
|
|
|
|
|
|
in the checked files it only populates a small recurring subset of the sixteen scenario lanes,
|
|
|
|
|
|
chiefly `Go West!`, `Germantown`, `Central Pacific`, `Texas Tea`, and `War Effort`, with
|
|
|
|
|
|
scenario-family-specific byte values rather than a simple `0/1` unlock bitmap. That makes the
|
|
|
|
|
|
setup-side staging bridge look more like one shared destination layout being reused for a broader
|
|
|
|
|
|
launch token family, not a proof that ordinary setup-launched `.gmp/.gms` content is already in
|
|
|
|
|
|
campaign-screen form.
|
|
|
|
|
|
Only `0x0e81` also sets `[0x006cec7c+0x82] = 1`, which is currently the strongest sandbox-side
|
|
|
|
|
|
anchor beneath the later `.gmx` load family.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
That launch band is slightly tighter now too: `0x0dca` is the grayscale-map picker branch above
|
|
|
|
|
|
the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` helper at `0x004eb0b0`, not another ordinary
|
|
|
|
|
|
save-file validator. That launch band is tighter in a second way too: `0x0ddf` is not a lobby
|
|
|
|
|
|
branch after all, but the separate windowed-mode-gated grayscale-heightmap generation path. It
|
|
|
|
|
|
reopens the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` picker through `0x0042a970`, validates
|
|
|
|
|
|
the chosen image through `0x005411c0`, and only then writes startup selector `2`. The fixed-
|
|
|
|
|
|
button side is tighter too: submode `15` now aligns with the `Extras` family because it sits
|
|
|
|
|
|
above the Readme/Weblinks shell-open quartet and the return-to-extras sibling, while submode
|
|
|
|
|
|
`17` now aligns with the tutorial chooser because it is the only branch that later exposes
|
|
|
|
|
|
startup selectors `1` and `7`, which RT3 uses for `Tutorial_2.gmp` and `Tutorial_1.gmp`. The
|
|
|
|
|
|
map-root family is tighter too: submodes `7/8/9` are the only setup branch that flips the file
|
|
|
|
|
|
root into
|
|
|
|
|
|
`maps\\*.gm*`, with mode `8` specifically the grayscale-heightmap picker and mode `9` the
|
|
|
|
|
|
file-backed map-list sibling.
|
|
|
|
|
|
Submode `13` is slightly tighter now too: current local evidence shows that it stays outside
|
|
|
|
|
|
both the dedicated file-backed pane and the mode-`3` option-heavy pane, so it is best read for
|
|
|
|
|
|
now as one top-level non-file-backed setup branch rather than another saved-game list variant.
|
|
|
|
|
|
The setup-side file roots are tighter now too: the shared file-list builder at `0x4333f0`
|
|
|
|
|
|
formats either `saved games\\*.smp` or `maps\\*.gm*` from the shell-state fields at
|
|
|
|
|
|
`[0x006cec74+0x68/+0x6c]`, with the separate `data\\tutorial` root only appearing on the
|
|
|
|
|
|
tutorial-side `[shell+0x6c] == 2` branch. That means the `Setup.win` submode families are no
|
|
|
|
|
|
longer one generic file pane: the ordinary setup-backed `.smp` family is broader than the
|
|
|
|
|
|
narrower file-list pane, because modes `3/4/5/6/10/12/14` stay on the ordinary saved-game side
|
|
|
|
|
|
while only `4/6/9/10/12/14` re-enter the dedicated file-list panel at `0x5027b0`; the `maps`
|
|
|
|
|
|
branch is the narrower setup or tutorial launch family above the same shared record builder.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Multiplayer Session and Transport Flow
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
into a probe request id plus displayed version/build integer, and
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
that averaged `ms` sample through `multiplayer_transport_enqueue_callback_slot24_record`. So the
|
|
|
|
|
|
publication boundary is explicit and the request-id ownership is explicit: `[entry+0x80]` now
|
|
|
|
|
|
reads as the averaged millisecond probe sample and `[entry+0x54]` as the displayed version/build
|
|
|
|
|
|
companion integer. The adjacent route-callback side is tighter too, but it is now kept separate:
|
|
|
|
|
|
the staged route-callback path at `0x5958e0` and the later compatibility gate at
|
|
|
|
|
|
`multiplayer_transport_route_binding_matches_route_callback_descriptor_tuple` `0x595d00` operate
|
|
|
|
|
|
on a compact GameSpy-style server or route descriptor family with a primary endpoint tuple at
|
|
|
|
|
|
`[descriptor+0x00]/[+0x04]`, an optional secondary endpoint tuple at `[descriptor+0x08]/[+0x0c]`,
|
|
|
|
|
|
string-key lookups such as `hostname` and `gamever`, and numeric-key lookups such as
|
|
|
|
|
|
`numplayers`, `numservers`, `numwaiting`, and `gsi_am_rating`. The route-binding side uses that
|
|
|
|
|
|
descriptor family's primary dword and host-order port plus the optional secondary tuple against
|
|
|
|
|
|
route-binding offsets `+0x54/+0x58` and route word `+0x30`. Current evidence still does not prove
|
|
|
|
|
|
that descriptor tuple is the same field family as the selector-view marker companion integer at
|
|
|
|
|
|
`[entry+0x54]`. The higher compact decode owners are tighter now too: `0x5907d0` is the
|
|
|
|
|
|
allocate-and-append lane for one self-consistent compact payload, while `0x590d00` is the keyed
|
|
|
|
|
|
upsert-by-primary-endpoint lane that reuses an existing descriptor when possible and then notifies
|
|
|
|
|
|
the owner callback. The route-callback-table runtime above that decode side is tighter now too:
|
|
|
|
|
|
`multiplayer_transport_route_callback_table_construct` `0x5905e0` seeds one transport-owned
|
|
|
|
|
|
table block, `multiplayer_transport_route_callback_table_release_decoded_schema_dictionary`
|
|
|
|
|
|
`0x5906f0` tears down the decoded schema dictionary rooted at `[this+0x08]`,
|
|
|
|
|
|
`multiplayer_route_callback_runtime_acquire_shared_string_copy` `0x590540` and
|
|
|
|
|
|
`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
|
|
|
|
|
|
splits between `[transport+0xba4]` with owner callback `0x595a40`, the local field-cache family
|
|
|
|
|
|
`[transport+0x1724]` seeded through `0x5a08f0/0x595b60`, and `[transport+0x1164]` with owner
|
|
|
|
|
|
callback `0x595bc0`, while
|
|
|
|
|
|
`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`.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
The callback-owner mode split above that runtime is now explicit too: append-notify `0x590370`
|
|
|
|
|
|
publishes mode `0`, compact upsert `0x590d00` publishes mode `1`, remove-notify `0x590430`
|
|
|
|
|
|
publishes mode `2`, and the live receive/decode path `0x5908c0` publishes modes `6`, `5`, and
|
|
|
|
|
|
`3`.
|
2026-04-06 20:34:16 -07:00
|
|
|
|
The route-handle lifecycle above that decode path is tighter now too: `0x590740` cleanly resets
|
|
|
|
|
|
one table's live route plus decode-side runtime without destroying the outer object; `0x5907a0`
|
|
|
|
|
|
is the broader destroy path that also releases the active descriptor collection; `0x590ed0`
|
|
|
|
|
|
opens the live route handle into `[this+0x4a0]`, stages the initial outbound request, and seeds
|
|
|
|
|
|
state `3` plus the staged receive buffer; `0x5911e0` is the state-`2/3` socket-service wrapper
|
|
|
|
|
|
that reads new bytes, grows the staged buffer, and re-enters `0x5908c0`; `0x5912c0` is the
|
|
|
|
|
|
one-shot send-with-reopen-retry helper; and `0x590ea0` is the shared disconnect publication and
|
|
|
|
|
|
reset tail. The recurring service helper `0x591290` is tighter too: it now first clears the
|
|
|
|
|
|
staged intrusive descriptor list through `0x590490` before entering the state-driven seed-or-
|
|
|
|
|
|
receive branch. The upstream owners are tighter too: `0x5962e0` is now the field-subscription
|
|
|
|
|
|
route-table opener above `[transport+0xba4]`, while `0x596530` is the `gsi_am_rating` reopen
|
|
|
|
|
|
path above `[transport+0x18bc]`. 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
|
|
|
|
|
|
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
|
|
|
|
|
|
byte-`0x15` bit `0x1` as a primary-endpoint-seed or endpoint-only marker. In the `gsi_am_rating`
|
|
|
|
|
|
dispatcher, clear-bit descriptors can take the richer direct transition lane, while set-bit
|
|
|
|
|
|
descriptors are staged through the queued enrichment path and still suppress that direct
|
|
|
|
|
|
transition even after the ready bit arrives.
|
|
|
|
|
|
The adjacent capacity-descriptor side is tighter too: `0x595bc0` now clearly publishes a
|
|
|
|
|
|
descriptor block from live descriptor properties `hostname`, `numwaiting`, `maxwaiting`,
|
|
|
|
|
|
`numservers`, and `numplayers` plus three carried sidecar scalars. 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
|
|
|
|
|
|
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
|
|
|
|
|
|
show no direct store to `[transport+0x1778]`, and a wider local sweep also failed to show any
|
2026-04-08 16:31:33 -07:00
|
|
|
|
obvious `lea`-based replay-band writer. The transient-request lifecycle tightens that further:
|
|
|
|
|
|
`0x593330/0x593370/0x593380/0x5934e0/0x5933a0` now fully bound `[transport+0x1780]`, `0x1784`,
|
|
|
|
|
|
and `0x1788` without ever touching `[transport+0x1778]`, and the neighboring active-opcode reset
|
|
|
|
|
|
helper `0x5929a0` is likewise scoped only to `[transport+0x17fc]`. A broader constructor and
|
|
|
|
|
|
teardown pass tightened that further too: `0x596090`, `0x5961b0`, and `0x5962e0` all touch the
|
|
|
|
|
|
neighboring replay-band fields without ever seeding `[transport+0x1778]`. A full-binary
|
|
|
|
|
|
literal-offset sweep tightens it further still: the only direct `0x1778` hit in `RT3.exe` is
|
|
|
|
|
|
the read in `0x595bc0`. One nearby ambiguity is now closed too: the
|
|
|
|
|
|
mode-`5` mirror path in `0x595a40` and `0x595e10` does not target `[transport+0x1778]`; it
|
|
|
|
|
|
writes `[transport+0x54]` and mirrors the same staged route companion dword only into queue-side
|
|
|
|
|
|
slot `[transport+0x1724+0x24]` through `0x005a0940`. So the sidecar writer remains upstream of this
|
|
|
|
|
|
leaf publisher. Mode `0` is now also tied more cleanly to the generic descriptor append-notify lane
|
2026-04-06 20:34:16 -07:00
|
|
|
|
at `0x590370`, while mode `2` stays outside this helper as the separate
|
|
|
|
|
|
remove-notify-and-stage path at `0x590430`. The opcode-`2` payload boundary is tighter too:
|
|
|
|
|
|
`0x592ae0` now grounds that payload
|
|
|
|
|
|
as a seven-dword block with an owned string slot at `+0x08`, so live mode supplies a populated
|
|
|
|
|
|
payload while modes `3` and `5` deliberately enqueue an all-zero payload and reuse only
|
|
|
|
|
|
the wrapper-side sidecar metadata. Those two modes are tighter now too: they are the live
|
|
|
|
|
|
receive-state owner callbacks emitted by `0x5911e0 -> 0x5908c0`, not loose generic replay
|
|
|
|
|
|
guesses. So those paths are better read as delayed metadata replays over one cached work record,
|
|
|
|
|
|
not over a separate anonymous cache blob. The neighboring
|
2026-04-08 16:31:33 -07:00
|
|
|
|
capacity-owner split is tighter now too: `0x595bc0` only stages descriptor records for modes
|
|
|
|
|
|
`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
|
|
|
|
|
|
longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring
|
2026-04-06 20:34:16 -07:00
|
|
|
|
work queue is tighter too: `0x593330/0x593370/0x593380` now bound `[transport+0x1780]` as the
|
|
|
|
|
|
construct/clear/destroy owner family, while `0x5933a0`, `0x5934e0`, and `0x593570` ground the
|
|
|
|
|
|
remove, allocate, and completion side over that same collection. The small sibling `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,
|
|
|
|
|
|
using `0x593400` for the queued work family at `[transport+0x1780]` and `0x592970` for the
|
|
|
|
|
|
active opcode-record collection at `[transport+0x17fc]`. The strongest current read is that
|
|
|
|
|
|
`0x5934c0` seeds that shared drain context id first, then the transport copies it into queued
|
|
|
|
|
|
work field `+0x0c` and active opcode-record field `+0x14` before the immediate-drain roots wait
|
|
|
|
|
|
on one shared disappearance test rather than on a vague settle loop. The currently grounded
|
|
|
|
|
|
roots are `0x58df20`, the neighboring formatted selector-text publish path at `0x58dfb0`, and
|
|
|
|
|
|
callback-table registration at `0x58e200`. The active-opcode side is tighter too: `0x5927b0`
|
|
|
|
|
|
now bounds the per-record service-and-retire path, `0x592800` the wider context-or-idle sweep,
|
|
|
|
|
|
`0x5929a0` the remove-by-opcode-type sweep, and `0x5929f0` the narrower opcode-`3`
|
|
|
|
|
|
field-snapshot removal keyed by the subscribed callback-pair payload. That also corrects
|
|
|
|
|
|
`0x595b80`, whose final cleanup is an active field-snapshot purge rather than a queued-work
|
|
|
|
|
|
drain. The adjacent route-callback descriptor-table lifecycle is tighter too: `0x590410` now
|
|
|
|
|
|
grounds `[table+0x5bc]` as the staged intrusive descriptor-list head, `0x590430` is the generic
|
|
|
|
|
|
remove-notify-and-stage lane, `0x590490` releases the staged list, and `0x5904d0` releases the
|
|
|
|
|
|
active descriptor collection before tearing that staged list down. That also makes the earlier
|
|
|
|
|
|
`0x5962e0` “release active descriptors” step explicit. The callback-table attach side now constrains the
|
2026-04-08 16:31:33 -07:00
|
|
|
|
same work-record metadata family a little further too: `0x593650` deliberately duplicates its
|
|
|
|
|
|
first caller metadata dword into both fields `+0x0c` and `+0x10`, while carrying the second
|
|
|
|
|
|
caller metadata dword in `+0x18`. The lower opcode wrappers are tighter now too: `0x592a40`
|
|
|
|
|
|
turned out to be the explicit opcode-`1` trigger wrapper whose constructor is a no-op and whose
|
|
|
|
|
|
active-side service is `0x5913c0`, while `0x592c40` is the real `0x08`-byte explicit opcode-`5`
|
|
|
|
|
|
binding leaf. The earlier opcode-`4` read was just the table-indexing mistake in `0x5928a0`:
|
|
|
|
|
|
selector `4` lands on the row at `0x5e2044`, not the row at `0x5e2034`. The producer side is tighter too:
|
2026-04-06 20:34:16 -07:00
|
|
|
|
bound-route requests, selector-text route requests, and the type-`9` text fastpath also stage
|
|
|
|
|
|
that same triplet through `0x5934e0`, and the fastpath shim `0x593d00` now gives the cleanest
|
|
|
|
|
|
proof of the callback split by only re-emitting the follow-on lane when `+0x10` is nonnull and
|
|
|
|
|
|
then forwarding `(+0x10, +0x18, +0x0c)` into `0x593170` as callback function, callback
|
|
|
|
|
|
companion, and trailing drain context. So the replay-side triplet is clearly a broader
|
|
|
|
|
|
transport callback-wrapper family, not one fixed route-only tuple. The nearby
|
|
|
|
|
|
field-subscription side is tighter too: `0x592b50` now clearly uses `[transport+0x1774]` as a
|
|
|
|
|
|
cached progress percentage under `[transport+0xba4]`, and `0x5962e0` seeds that percentage to
|
|
|
|
|
|
`1` just before the first immediate mode-`3` snapshot. The nearby route-callback-table
|
|
|
|
|
|
lifecycle is tighter now too: `0x596090` seeds `[transport+0xba0]` as the callback-plumbing
|
|
|
|
|
|
enable latch, clears staged payload slot `[transport+0xb50]`, and constructs the three owner
|
2026-04-08 16:31:33 -07:00
|
|
|
|
branches rooted at `[transport+0xba4]`, `[transport+0x1164]`, and `[transport+0x18bc]`;
|
|
|
|
|
|
`0x596210` is the recurring service sweep over those same three tables plus the local field
|
|
|
|
|
|
cache and queued-descriptor family; `0x596060` is the explicit `gsi_am_rating` reset; and
|
|
|
|
|
|
`0x596530` is the reopen-from-stored-label sibling above that same am-rating table. The
|
2026-04-06 20:34:16 -07:00
|
|
|
|
matching local cleanup is tighter too: `0x5962c0` is the explicit staged route-callback payload
|
|
|
|
|
|
clear on `[transport+0xb50]`, while `0x595ce0` now clearly resets only the capacity-descriptor
|
|
|
|
|
|
route callback table at `[transport+0x1164]`, not the field-subscription table at
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`[transport+0xba4]`. The remaining gap on the capacity side is therefore narrower: the carried
|
|
|
|
|
|
sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet reused
|
|
|
|
|
|
elsewhere (`drain context id +0x0c`, `callback fn +0x10`, `callback companion +0x18`), and the
|
|
|
|
|
|
negative result is stronger too: nearby replay-band fields `[transport+0x176c]`,
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`[transport+0x1770]`, `[transport+0x1774]`, `[transport+0x177c]`, `[transport+0x1780]`, and
|
|
|
|
|
|
`[transport+0x1784]` all have direct local owners while `[transport+0x1778]` still appears only
|
2026-04-08 16:31:33 -07:00
|
|
|
|
as the single read in `0x595bc0`; even the broader callback-owner lifecycle now skips it while
|
|
|
|
|
|
seeding, servicing, resetting, reopening, or tearing down those neighboring tables and caches.
|
|
|
|
|
|
The constructor now closes that local search further: `0x58dc50` bulk-zeroes the full transport
|
|
|
|
|
|
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:
|
|
|
|
|
|
`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
|
|
|
|
|
|
`[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
|
|
|
|
|
|
handoff rather than one missing ordinary field store in the local cluster. The adjacent staged-route
|
2026-04-06 20:34:16 -07:00
|
|
|
|
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
|
|
|
|
|
|
gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]`
|
|
|
|
|
|
together with target `[0xb48]` to decide whether staged route-callback traffic can push the
|
|
|
|
|
|
multiplayer route-mode ladder from `2` into `3` and later `4`. The selector-view counter beneath
|
|
|
|
|
|
that gate is tighter now too: `0x594e30` counts slot-`2` entries whose flag dword carries bit
|
|
|
|
|
|
`0x20`, optionally filtered by the current transport name buffer. The selector-view mutation
|
|
|
|
|
|
family under that same lane is tighter too: `0x594a30` is now the direct keyed-store remover,
|
|
|
|
|
|
`0x594fb0` clears one selector-slot ownership pointer plus its slot-local flag dword and drops
|
|
|
|
|
|
the whole entry when no slots remain, `0x595010` rekeys one selector-view entry under a new name
|
|
|
|
|
|
while preserving the `0x40..` runtime band, and callback root `0x59f9c0` now reads as the
|
|
|
|
|
|
sibling lane that clears one named selector-view slot, publishes callback slot `18`, and may
|
|
|
|
|
|
still re-enter the route-mode setter from the same slot-`2` status and generation gates. The
|
|
|
|
|
|
neighboring callback roots are tighter now too: `0x5950a0` clears one selector slot from every
|
|
|
|
|
|
selector-view entry in the keyed store, `0x59fab0` is the rename or relabel sibling above
|
|
|
|
|
|
`0x595010`, `0x59faf0` updates one selector slot's fixed sample-text buffer and refreshes the
|
|
|
|
|
|
active selector object when present, and `0x59fb60` replaces one selector slot's name set,
|
|
|
|
|
|
requests the default profile-key bundle for that slot, and publishes callback slot `20`. Slot
|
|
|
|
|
|
`16` is tighter now too: current grounded caller `0x59f440` forwards the staged route-callback
|
|
|
|
|
|
payload handle from `[transport+0xb50]` through `0x592ea0` just before route mode `5`. The
|
|
|
|
|
|
last adjacent callback root in that block is tighter now too: `0x59fbd0` is the built-in
|
|
|
|
|
|
per-slot profile-key query sibling. It resolves the caller selector name into one slot index,
|
|
|
|
|
|
forwards the caller trio into `0x596b90`, and then publishes callback slot `28`; that lower
|
|
|
|
|
|
helper indexes one slot-specific built-in string pair from `[transport+0x189c]` and
|
|
|
|
|
|
`[transport+0x18ac]`, reuses the generic per-key handler `0x596970`, and only republishes slot
|
|
|
|
|
|
`28` when that lower query path succeeds.
|
|
|
|
|
|
The compact-header side is tighter now too: `0x58fe20` and `0x58ff20` now show that compact
|
|
|
|
|
|
payloads always carry the primary IPv4 dword and that header bit `0x10` only gates whether the
|
|
|
|
|
|
primary port word is inline or inherited from the owner default port. `0x58fe90` now validates
|
|
|
|
|
|
the `0x40` inline keyed-property vector against the owner schema, and `0x58fe50` validates the
|
|
|
|
|
|
signed-`0x80` trailing string-pair tail before decode. `0x58ff60` then grounds bit `0x02` as
|
|
|
|
|
|
the inline secondary IPv4 dword branch, bit `0x20` as the paired secondary-port word branch with
|
|
|
|
|
|
owner-port fallback, bit `0x08` as one still-unresolved auxiliary dword stored at
|
|
|
|
|
|
`[descriptor+0x10]`, bit `0x40` as one inline keyed-property vector decoded through the
|
|
|
|
|
|
property-store writers, and signed bit `0x80` as one trailing string-pair tail. The
|
|
|
|
|
|
descriptor-state side is tighter now too: the shared queue helper at
|
|
|
|
|
|
`0x005a09a0` stamps pending state `0x4` for the local field-cache family `[transport+0x1724]`
|
|
|
|
|
|
and pending state `0x8` for the `gsi_am_rating` queued-descriptor family `[transport+0x1e7c]`,
|
|
|
|
|
|
while later service through `0x005a0c80` promotes those pending tags into ready bits `0x1` and
|
|
|
|
|
|
`0x2` in descriptor byte `[entry+0x14]`. That makes the current transport-side tests cleaner:
|
|
|
|
|
|
`0x58d1c0` is the field-cache ready gate, `0x58d1d0` is the `gsi_am_rating` queued-descriptor
|
|
|
|
|
|
ready gate, and `0x58d230` is the remaining flag-byte split between direct primary-endpoint
|
|
|
|
|
|
handling at `[transport+0x18bc]` and the queued path at `[transport+0x1e7c]`. That byte-`0x14`
|
|
|
|
|
|
story is no longer queue-only either: `0x58ff60` can also OR in bit `0x1` after the inline
|
|
|
|
|
|
keyed-property vector and bit `0x2` after the signed string-pair tail. The flag-byte split is no
|
|
|
|
|
|
longer purely behavioral either: current evidence now says byte `[descriptor+0x15]` bit `0x1` is
|
|
|
|
|
|
a source-side descriptor header bit, explicitly seeded during the primary-endpoint table refresh
|
|
|
|
|
|
around `0x590dc0` and preserved by the compact descriptor decode path at `0x58ff60`, rather than
|
|
|
|
|
|
a queue-generated runtime state. The `gsi_am_rating` dispatcher side is tighter too: that same
|
|
|
|
|
|
bit no longer just looks like a direct-versus-queued routing split, because `0x595e10` also uses
|
|
|
|
|
|
it to suppress the direct `0x595dc0` transition even after queued ready bit `0x2` is present.
|
|
|
|
|
|
The descriptor body is tighter too: `[descriptor+0x20]` is now the intrusive next-link used by
|
|
|
|
|
|
the transport-owned primary-endpoint list headed at `[table+0x5bc]`, and `[descriptor+0x1c]` is
|
|
|
|
|
|
now the special numeric scalar behind the current `queryid`/`ping` fallback pair. The compact-only
|
|
|
|
|
|
auxiliary dword at `[descriptor+0x10]` is tighter in a negative way too: local xref scans now
|
|
|
|
|
|
only show it being preserved by later generic helpers like
|
|
|
|
|
|
`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
|
|
|
|
|
|
accessor yet. The route-event dispatcher side is tighter too: the mode-`5` tails in both
|
|
|
|
|
|
callback families do not copy a descriptor-local field but instead mirror the transport-staged
|
2026-04-08 16:31:33 -07:00
|
|
|
|
companion dword at `[this+0x490]` into `[this+0x54]` and queue-side slot `[this+0x1724+0x24]`. The
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts
|
|
|
|
|
|
the surviving primary-endpoint table through `0x590310` in mode `1` with key `gsi_am_rating`,
|
|
|
|
|
|
then selects the new head through `0x590480` before re-entering the route-transition path. The
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
now looks more bounded too: it rebuilds the core `X%sX` marker text from `[entry+0x50]` through
|
|
|
|
|
|
`multiplayer_transport_format_selector_view_probe_marker_core`, formats one `PNG %s %d` line
|
|
|
|
|
|
around that marker and the entry-local averaged millisecond sample at `[entry+0x80]`, appends bounded
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 20:36:20 -07:00
|
|
|
|
route-mode state machine now looks consistent with that: `multiplayer_transport_set_route_mode`
|
|
|
|
|
|
latches the requested small mode at `[this+0x18b8]`, then uses mode `0` for the direct-versus
|
|
|
|
|
|
queued `gsi_am_rating` split, mode `1` for the ready-bit plus queued fallback, mode `2` for
|
|
|
|
|
|
pending-descriptor cleanup, mode `3` for the empty-table fallback, mode `4` for deferred
|
|
|
|
|
|
route-status recovery, and mode `5` for copying the staged route companion dword into `[this+0x54]`
|
2026-04-08 16:31:33 -07:00
|
|
|
|
and queue-side slot `[this+0x1724+0x24]`. The current grounded mode transitions still switch by releasing route
|
2026-04-06 20:36:20 -07:00
|
|
|
|
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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`[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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`[transport+0x17f8]`. The surrounding status-route callback vector is tighter now too:
|
|
|
|
|
|
`0x005970e0` publishes either the active selector text or the averaged probe sample at
|
|
|
|
|
|
`[entry+0x80]` and otherwise falls back to owner callback `[transport+0x17e0]`;
|
|
|
|
|
|
`0x00597180` is a straight owner-forwarding lane through `[transport+0x17e4]`;
|
|
|
|
|
|
`0x005971b0` seeds the local status-control id list and can then notify owner callback
|
|
|
|
|
|
`[transport+0x17e8]`; and `0x00597270` returns the third selector-slot generation counter
|
|
|
|
|
|
`[transport+0xac0]` on its bounded local branch before falling back to owner callback
|
|
|
|
|
|
`[transport+0x17ec]`. Subtype `6` still validates the same cookie, dedupes one 32-bit cookie or
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Input, Save/Load, and Simulation
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
speed buttons. That setup pipeline is now clearer at the progress-banner level too: on the fuller
|
|
|
|
|
|
setup path it first runs one preliminary named-candidate availability prepass through `0x00437743`
|
|
|
|
|
|
before any visible progress banner is posted. After that, localized id `318`
|
|
|
|
|
|
`Computing Transportation and Pricing...` stays visible while the pipeline runs
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`world_region_border_overlay_rebuild` `0x004882e0`; that border pass is now tighter too: the
|
|
|
|
|
|
outer owner refreshes the companion region set `0x006cfc9c` through the reset-and-assign wrapper
|
|
|
|
|
|
`0x00487650` above the heavier record initializer `0x00487540`, then re-enters `0x004881b0` to
|
|
|
|
|
|
refresh the raw per-region cell-count band from the live world raster, and the inner emitter
|
|
|
|
|
|
`0x00487de0` clears prior chunked segment queues through `0x00533cf0`, scans the live region
|
|
|
|
|
|
raster, and appends fresh border-segment records through `0x00536ea0`. The lower presentation
|
|
|
|
|
|
helper strip under the same owner is now explicit too: `0x00533970/0x00533980` query the cached
|
|
|
|
|
|
world-grid X/Y maxima, `0x00533ae0/0x00533af0/0x00533b00` expose the secondary-raster and
|
|
|
|
|
|
companion byte-raster roots, and `0x00533b20/0x00533b30/0x00533b70/0x00533b90` expose the
|
|
|
|
|
|
normalized coordinate, strip-offset, and sample-triplet tables used by shell-side overlay
|
|
|
|
|
|
staging. If shell-state gate
|
|
|
|
|
|
`[0x006cec74+0x174]` is set it then posts id `320` `Setting Up Buildings...` for
|
|
|
|
|
|
`world_region_collection_run_building_population_pass` `0x00421c20`; if `[0x006cec74+0x178] > 0`
|
|
|
|
|
|
it then posts id `321` `Seeding Economy...` for `simulation_run_chunked_fast_forward_burst`
|
|
|
|
|
|
`0x00437b20`; only after those setup-side gates does the code post id `319`
|
|
|
|
|
|
`Setting up Players and Companies...`. That `319` lane is no longer just a shell-state placeholder: its
|
|
|
|
|
|
earlier hidden prepass is tighter now too: `0x00437743` is the scenario-side named
|
|
|
|
|
|
candidate-availability seeding pass over the live candidate pool `0x0062b268`, feeding the
|
|
|
|
|
|
override collection at `[state+0x66b2]` through `0x00434f20` before any visible banner is posted.
|
2026-04-10 01:22:47 -07:00
|
|
|
|
That grounded write path is narrower than the static file evidence, though: the startup reset
|
|
|
|
|
|
helper `world_runtime_reset_startup_dispatch_state_bands` `0x004336d0` explicitly clears
|
|
|
|
|
|
`[state+0x66b2]` before the dispatch path begins, and the current exported write-side callers
|
|
|
|
|
|
we can name after that reset are still just the live-pool preseed `0x00437743`, the sibling
|
|
|
|
|
|
startup lane `0x00436ad7`, and the editor-side `Industry (Overall)` toggle handler
|
|
|
|
|
|
`0x004cf430` through `0x00434f20`. So while the fixed `0x6a70..0x73c0` candidate-availability
|
|
|
|
|
|
block is now well grounded as bundled map/save source data, a direct bulk-import path from that
|
|
|
|
|
|
block into the live runtime collection `[state+0x66b2]` is still not grounded by the current
|
|
|
|
|
|
disassembly notes.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
That candidate-side table now has a grounded fixed record layout too: each entry is a `0x22`-byte
|
|
|
|
|
|
blob with a zero-terminated candidate-name slot at `[entry+0x00..+0x1d]` and one trailing
|
|
|
|
|
|
availability dword at `[entry+0x1e]`, read through `0x00434ea0` and mirrored later into
|
|
|
|
|
|
`[candidate+0x7ac]`. The same scenario state keeps a locomotive-side sibling collection at
|
|
|
|
|
|
`[state+0x66b6]`, read through `0x00435030` and updated through `0x004350b0`, which is now the
|
|
|
|
|
|
shared named locomotive-availability lane beneath the `Locomotives` editor page and the
|
|
|
|
|
|
startup-side locomotive seeding branches. That locomotive-side table is the same pattern at a
|
|
|
|
|
|
larger width: each entry is a `0x41`-byte blob with a zero-terminated locomotive-name slot at
|
|
|
|
|
|
`[entry+0x00..+0x3c]` and one trailing availability dword at `[entry+0x3d]`, later mirrored into
|
|
|
|
|
|
`[loco+0x7b]`. Both override dwords now read most safely as simple availability bits rather than
|
|
|
|
|
|
wider mode enums. That `319` lane is no longer just a shell-state placeholder: its
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`[+0x66ce]`, the direct campaign-designated bit `[+0x66de]` through control `0x5b6e`, and the
|
|
|
|
|
|
inverse of the paired metadata byte `[+0x66f3]` through control `0x5b74`. The resource-side
|
|
|
|
|
|
anchor is now explicit too: `editorDetail.win` carries localized ids `3160/3161` `Campaign
|
|
|
|
|
|
Scenario` and `If checked, this map will be reserved as a campaign scenario.` inside the control
|
|
|
|
|
|
record rooted at `0x5b6e`, so `[+0x66de]` is now the grounded campaign-scenario flag rather than
|
|
|
|
|
|
an anonymous metadata boolean. `map_editor_scenario_metadata_panel_refresh_briefing_mode`
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
management of the two companies. By contrast the broader support-adjusted share-price helper
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`company_compute_public_support_adjusted_share_price_scalar` `0x00424fd0` uses issue id `0x37`,
|
|
|
|
|
|
while the finance-side debt helpers now bound `0x38` and `0x39` separately as the explicit
|
|
|
|
|
|
credit-rating and prime-rate lanes. That leaves `0x37` as the broader investor-confidence lane
|
|
|
|
|
|
behind equity support, share price, and adjacent governance pressure, with the strongest current
|
|
|
|
|
|
text anchors coming from the investor-attitude strings `1217` and `3048/3049` about company or
|
|
|
|
|
|
chairman performance rather than from the merger-only management-attitude term. One older local lead is now ruled out too: the
|
|
|
|
|
|
`0x460a90` / `0x473620` setup block is the camera-view hotkey family over localized ids
|
|
|
|
|
|
`3482..3489` (`Select/Assign Camera View 5..8`), not issue-opinion infrastructure, so it should
|
|
|
|
|
|
no longer be used as evidence for the player-facing meaning of issue `0x37`. The editor-side
|
|
|
|
|
|
`Stock Prices` label is also no longer being used as direct evidence here: the `0x4ca980` /
|
|
|
|
|
|
`0x4cadf0` panel owns a separate float-tuning block `[state+0x0bde..0x0bf6]`, with
|
|
|
|
|
|
`[state+0x0bde]` merely mirroring `[state+0x0be2]`, so that label belongs to a different
|
|
|
|
|
|
settings family than the issue table behind `0x37`. A direct shell-resource follow-up now narrows
|
|
|
|
|
|
the remaining caption question too: the extracted `CompanyDetail.win` blob from `rt3_2WIN.PK4`
|
|
|
|
|
|
contains no plain-English investor or finance caption for this lane, which matches the owner-side
|
|
|
|
|
|
read that section-0 is a dynamic `0x947f` text widget fed by
|
|
|
|
|
|
`shell_format_company_governance_and_economy_status_panel` `0x004e5cf0` rather than a separate
|
|
|
|
|
|
fixed label row. The supporting stat layer is bounded more cleanly now too: the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`simulation_run_chunked_fast_forward_burst`, whose grounded tail now also sweeps the live region
|
|
|
|
|
|
collection through `world_region_refresh_cached_category_totals_and_weight_slots` `0x00423d30`;
|
|
|
|
|
|
id `322` then fronts `Calculating Heights...`. The
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
Support`. The same placement-commit gate beneath it, `world_region_validate_and_commit_candidate_placement`
|
|
|
|
|
|
`0x00422a70`, is also corroborated by the separate world-side randomized batch helper
|
|
|
|
|
|
`world_try_place_random_structure_batch_from_compact_record` `0x00430270`, which retries
|
|
|
|
|
|
placements around one compact center/radius record rather than the ordinary per-region demand
|
|
|
|
|
|
deficits. That batch placer now sits under a wider compact runtime-effect dispatcher,
|
|
|
|
|
|
`world_apply_compact_runtime_effect_record_to_resolved_targets` `0x00431b20`, so the world-side
|
|
|
|
|
|
branch is no longer just “another caller near `0x43051e`” but one real effect family alongside
|
|
|
|
|
|
the ordinary region-demand worker. Above that, the live scenario event collection at `0x0062be18`
|
|
|
|
|
|
now has a tighter runtime-effect lane too: `scenario_runtime_effect_record_service_and_dispatch_linked_compact_effects`
|
|
|
|
|
|
`0x004323a0` services one live runtime-effect record, dispatches its linked compact effects
|
|
|
|
|
|
through `0x00431b20`, and can synthesize follow-on records through
|
|
|
|
|
|
`scenario_runtime_effect_record_build_followon_effect_from_compact_record_and_targets`
|
|
|
|
|
|
`0x00430b50`, which in turn allocates new live records through
|
|
|
|
|
|
`scenario_event_collection_allocate_runtime_effect_record_from_compact_payload` `0x00432ea0`.
|
|
|
|
|
|
Above that, `scenario_event_collection_service_runtime_effect_records_for_trigger_kind`
|
|
|
|
|
|
`0x00432f40` now bounds the collection-wide loop that services those live runtime-effect records
|
|
|
|
|
|
for one trigger kind. That trigger split is tighter now too: recurring simulation maintenance
|
|
|
|
|
|
drives kinds `1`, `0`, `3`, and `2`; the neighboring route-style follow-on drives `5` and `4`;
|
|
|
|
|
|
startup-company and named-railroad creation branches drive `7`; kind `6` is now bounded as a
|
|
|
|
|
|
mixed post-change family spanning the placed-structure post-create tail, one build-version-gated
|
|
|
|
|
|
company-startup or roster-refresh tail, and the route-entry post-change sweep on `0x006cfca8`;
|
|
|
|
|
|
one world-entry-side one-shot gate drives `8` and then clears shell-profile latch
|
|
|
|
|
|
`[0x006cec7c+0x97]`; the briefing-text query lane is kind `9`; and the collection dirty latch
|
|
|
|
|
|
still forces the internal rerun at kind `0x0a`.
|
|
|
|
|
|
That moves this branch out of the “isolated world-side placement oddity” bucket and into a real
|
|
|
|
|
|
scenario-runtime effect pipeline with a mostly bounded trigger-kind family.
|
|
|
|
|
|
The same lower helper also reappears later on a slower
|
|
|
|
|
|
simulation-side cadence with
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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`.
|
|
|
|
|
|
- CompanyDetail addendum: the shared helper `shell_company_detail_resolve_selected_company` at
|
|
|
|
|
|
`0x004c16f0` now bounds the common current-company accessor beneath the whole pane, and the
|
|
|
|
|
|
read-side owner also has a separate active-company navigation family through
|
|
|
|
|
|
`shell_company_detail_step_selected_active_company_delta` at `0x004c3470` plus the next or
|
|
|
|
|
|
previous wrappers at `0x004c3540` and `0x004c3550`. The section switch in
|
|
|
|
|
|
`shell_company_detail_window_refresh_controls` is now grounded too: section index `0x006cfe60`
|
|
|
|
|
|
selects a chairman/governance slice, a debt-and-capital slice, a per-share slice, or a
|
|
|
|
|
|
territory-access slice, published through the tab band around `0x9472..0x9479`, with adjacent
|
|
|
|
|
|
section-navigation controls around `0x947b..0x947c` and `0x948f..0x9490`.
|
|
|
|
|
|
- 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
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`track_lay_window_handle_message`. The same family also owns the downstream route-entry policy
|
|
|
|
|
|
bridge at `0x004955b0`: current caller evidence says that helper maps the live TrackLay primary
|
|
|
|
|
|
mode into endpoint-synthesis policy `1` or `4` before the tool re-enters `0x00493cf0`, which is
|
|
|
|
|
|
the strongest current bridge from the player-facing single-track versus double-track buttons into
|
|
|
|
|
|
the lower route-builder policy bytes. The same family also owns a bridge-type preference selector
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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`.
|
2026-04-06 21:36:19 -07:00
|
|
|
|
- Open Questions: current evidence grounds the shell-controller-backed input and frame path as the
|
|
|
|
|
|
only coordinator after world entry; no separate outer gameplay loop or gameplay-only input
|
|
|
|
|
|
object is grounded yet. The new setup-pipeline evidence also points the same way: the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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 frame-owned shell coupling is tighter now
|
|
|
|
|
|
too: inside `simulation_frame_accumulate_and_step_world` itself, one direct branch opens or
|
|
|
|
|
|
focuses `LoadScreen.win` through `shell_open_or_focus_load_screen_page`, and the post-step
|
|
|
|
|
|
shell-window ladder also probes several sibling shell windows by the same presence-plus-dirty
|
|
|
|
|
|
pattern, including the live `LoadScreen` singleton, the shared callback-driven custom modal, the
|
|
|
|
|
|
shared file-options dialog, `SettingsWindow.win`, the now-grounded `Overview.win` and
|
|
|
|
|
|
`BuildingDetail.win` singletons, and now the shell-side `Trainbuy.win` singleton too. That
|
|
|
|
|
|
train-buy family is no longer just an unnamed probe pair either: the opener path is now grounded
|
|
|
|
|
|
under the same shell-owned cadence through `shell_can_open_trainbuy_window_or_warn` and
|
|
|
|
|
|
`shell_open_or_focus_trainbuy_window`, and its current family semantics already extend beyond a
|
|
|
|
|
|
bare locomotive picker into selected-train upgrade summary and route-edit affordances. The
|
|
|
|
|
|
selected-train side is tighter now too: we have explicit query helpers for the selected train
|
|
|
|
|
|
record, id, validity, and company ownership, plus one explicit ownership-mismatch warning modal.
|
|
|
|
|
|
That same pass also clarified one boundary we should not conflate: the neighboring shell family
|
|
|
|
|
|
rooted at `0x006d3b20` is now grounded separately as `TrainDetail.win`. It reuses the same
|
|
|
|
|
|
selected-train context and some of the same helpers, but it is not the same `Trainbuy.win`
|
|
|
|
|
|
singleton family. The `TrainDetail.win` side now has its own constructor, opener, refresh path,
|
|
|
|
|
|
and message owner above the broader train-command strip. Its inner `0xcb` strip is tighter now
|
|
|
|
|
|
too: one bounded branch is the selected-train engine-replacement or trainbuy handoff lane using
|
|
|
|
|
|
warning ids `593/594`, and another is the selected-train retirement lane using `595/596/597`
|
|
|
|
|
|
with either a local teardown path or multiplayer opcode `0x37`. The remaining train-command
|
|
|
|
|
|
family is narrower too: the shared `0x33`-stride helper trio at `0x004b2f00`, `0x004b3000`, and
|
|
|
|
|
|
`0x004b3160` now looks like a real train route-stop or waypoint list rather than a generic row
|
|
|
|
|
|
buffer, and route-entry flag byte `+0x28` now has one grounded top-level split: sign bit clear
|
|
|
|
|
|
entries are the live placed-structure-backed family, while sign bit set entries use the direct
|
|
|
|
|
|
route-node payload side. The helper `train_route_list_count_live_site_reference_entries`
|
|
|
|
|
|
`0x004b2b80` now counts the first family explicitly. One deeper lower-bit result is grounded too:
|
|
|
|
|
|
both `train_route_list_insert_staged_entry_at_index` and the auxiliary finalize helper
|
|
|
|
|
|
`train_finalize_aux_route_entry_buffer_preserving_subflags` `0x004a94b0` explicitly preserve
|
|
|
|
|
|
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
|
|
|
|
|
|
`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
|
|
|
|
|
|
player-facing labels for those rows are still open, but the subflags now have one real shell-side
|
|
|
|
|
|
consumer instead of only preservation logic. The broader `BuildingDetail.win` refresh family is
|
|
|
|
|
|
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
|
|
|
|
|
|
ordinary ids through the live candidate collection and the special express-side ids through the
|
|
|
|
|
|
embedded `AnyCargo.imb`, `AnyFreight.imb`, and `PassMail.imb` paths. The first fixed triplet is
|
|
|
|
|
|
now table-grounded instead of only inferred: `0x00621df8` seeds the short-label controls
|
|
|
|
|
|
`0x7dc8..0x7dca` with RT3.lng `494..496` `Any Cargo`, `Any Freight`, and `Any Express`, while
|
|
|
|
|
|
`0x00621e10` seeds the adjacent `0x7e90..0x7e92` icon-name triplet with `AnyCargo`,
|
|
|
|
|
|
`AnyFreight`, and `PassMail` before `%1.imb` formatting. RT3.lng also tightens the longer popup
|
|
|
|
|
|
side now: `0x00621e04` feeds the first clickable selector triplet `0x7f58..0x7f5a` with
|
|
|
|
|
|
`494/497/498`, so the help text there is `Any Cargo`, `Any Freight\n(Freight is everything but
|
|
|
|
|
|
Passengers, Mail, and Troops)`, and `Any Express\n(Express is Passengers, Mail, and Troops)`.
|
|
|
|
|
|
The sibling special service rows still align to `Dining Car` and `Caboose`. The extracted
|
|
|
|
|
|
`BuildingDetail.win` blob now sharpens the resource boundary too: its embedded text table is
|
|
|
|
|
|
currently sparse rather than rich, exposing the help line for `0x7d01`, repeated
|
|
|
|
|
|
`BuildingDetail.imb` asset strings, and one standalone caption entry `Cargo`. That makes
|
|
|
|
|
|
`Cargo` the strongest current resource-side anchor for the row header around `0x7d06`. The
|
|
|
|
|
|
ordinary deeper rows are tighter now too: they do not look like hidden caption-table entries,
|
|
|
|
|
|
but like live candidate-derived rows. The current path validates each ordinary id through
|
|
|
|
|
|
`indexed_collection_entry_id_is_live` `0x00517d40`, resolves the concrete candidate record
|
|
|
|
|
|
through `indexed_collection_resolve_live_entry_by_id` `0x00518140`, and then reuses candidate
|
|
|
|
|
|
field `[record+0x04]` as one shared stem for both the row asset `%s.imb` path and the
|
|
|
|
|
|
neighboring display-label lookup through
|
|
|
|
|
|
`localization_lookup_display_label_by_stem_or_fallback` `0x0051c920`. That lookup is now
|
|
|
|
|
|
bounded too: it scans the static stem table at `0x006243c8`, already grounding entries such as
|
|
|
|
|
|
`Alcohol`, `Aluminum Mill`, `Automobiles`, `Bauxite`, and `Big Boy` against RT3.lng
|
|
|
|
|
|
`3202..3220`, and only falls back to localized id `3866` when no table entry matches before the
|
|
|
|
|
|
fixed `0x384..0x386` express-side triplet takes over. One neighboring candidate-side helper is
|
|
|
|
|
|
tighter now too: `structure_candidate_query_route_style_or_local_availability_metric`
|
|
|
|
|
|
`0x0041e650` shares the same route-style byte at `[candidate+0x46]`, returning the cached local
|
|
|
|
|
|
float at `[candidate+0x5a]` for ordinary candidates but switching route-style rows to one
|
|
|
|
|
|
normalized count over the world-side route-link collection `0x006ada90` keyed by candidate class
|
|
|
|
|
|
`[candidate+0x3e]`. That collection is tighter now too: it is constructed during world bring-up
|
|
|
|
|
|
by `placed_structure_route_link_collection_construct` `0x00468110`, and current grounded
|
|
|
|
|
|
creation flow through `placed_structure_route_link_allocate_site_pair_for_candidate_class`
|
|
|
|
|
|
`0x00467f50` seeds class byte `+0x10`, a masked initial state template, and the strongest
|
|
|
|
|
|
current creation-side site-pair fields at `+0x0c` and `+0x0e` before
|
|
|
|
|
|
`placed_structure_route_link_attach_site_owner` `0x00467eb0` links the new route-link record
|
|
|
|
|
|
into the placed-structure-owned chain at `[site+0x272]`. The owner-side split is tighter now
|
|
|
|
|
|
too: route-link field `+0x08` is the separate route-node-style owner anchor used by
|
|
|
|
|
|
`placed_structure_route_link_attach_route_node_owner` `0x00467f20`, while `+0x0a/+0x0c/+0x0e`
|
|
|
|
|
|
now read as the site-reference triple matched by
|
|
|
|
|
|
`placed_structure_route_link_collection_remove_links_touching_site_id` `0x004681f0`. Creation
|
|
|
|
|
|
flow now sharpens that further: `+0x0c` is the strongest current candidate for the stable
|
|
|
|
|
|
first-site field seeded before owner attachment, `+0x0a` is the mutable owner-site anchor
|
|
|
|
|
|
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`
|
|
|
|
|
|
currently uses `+0x0a` and `+0x0e` as the active endpoint site pair while recomputing state
|
|
|
|
|
|
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
|
|
|
|
|
|
the class counters and then tails into `placed_structure_route_link_detach_current_owner_chain`
|
|
|
|
|
|
`0x00467df0`, while
|
|
|
|
|
|
`placed_structure_route_link_collection_recompute_all_endpoint_pair_state` `0x004682c0`
|
|
|
|
|
|
explicitly reruns the per-record endpoint-pair reconciler across the whole live route-link
|
|
|
|
|
|
collection. One layer above that,
|
|
|
|
|
|
`placed_structure_route_link_rebuild_route_style_grid_counters_and_endpoint_state`
|
|
|
|
|
|
`0x00468300` now looks like the full-family refresh owner: it clears three route-style class
|
|
|
|
|
|
lanes in the world-grid tables rooted at `[0x0062c120+0x2129]`, then clears bit `0x2` across
|
|
|
|
|
|
the live route-link records and reruns the endpoint-pair reconciler. That now lines up with the
|
|
|
|
|
|
visible shell split: non-route candidates keep the richer local metric and price lane, while
|
|
|
|
|
|
route-style candidates use one world-side route-link family instead. The emission side is
|
|
|
|
|
|
tighter now too: `placed_structure_try_emit_best_route_style_peer_link_for_candidate_class`
|
|
|
|
|
|
`0x0040fef0` scans the live placed-structure collection for one best peer site by
|
|
|
|
|
|
class-specific weight, distance window, and type gate, then only creates a new route-link
|
|
|
|
|
|
through `placed_structure_route_link_allocate_site_pair_for_candidate_class` `0x00467f50` when
|
|
|
|
|
|
`placed_structure_endpoint_pair_has_shared_route_entry_key` `0x0040fbe0` says the chosen site
|
|
|
|
|
|
pair does not already share a route-entry key. One layer above that,
|
|
|
|
|
|
`placed_structure_rebuild_route_style_candidate_scores_and_peer_links` `0x004101e0` now reads
|
|
|
|
|
|
as the broader per-site owner of this lane: it computes per-class route-style score scalars from
|
|
|
|
|
|
local site state and scenario multipliers, drives the first three route-style emission attempts,
|
|
|
|
|
|
and then continues into a larger descriptor-driven scoring phase. Inside that larger pass,
|
|
|
|
|
|
`placed_structure_accumulate_candidate_metric_or_emit_route_style_peer_link` `0x0042cab0`
|
|
|
|
|
|
now cleanly shows the split between ordinary candidates, which add directly into the local
|
|
|
|
|
|
route-style grid lane at `[site+candidate*4+0x103]`, and remapped route-style candidates, which
|
|
|
|
|
|
re-enter the peer-link emitter. One layer above that, the broader post-create or post-edit site
|
|
|
|
|
|
rebuild `placed_structure_finalize_creation_or_rebuild_local_runtime_state` `0x0040ef10`
|
|
|
|
|
|
conditionally re-enters `0x004101e0` with stack flag `1` when its local latch at `[site+0x29e]`
|
|
|
|
|
|
stays clear, so the route-style lane is no longer floating under an unnamed single-site caller.
|
|
|
|
|
|
The placement side is tighter now too: the direct constructor
|
|
|
|
|
|
`placed_structure_collection_allocate_and_construct_entry` `0x004134d0` is the shared allocator
|
|
|
|
|
|
immediately beneath the current placement-side callers before they hand the new site into
|
|
|
|
|
|
`0x0040ef10`, and the lower constructor
|
|
|
|
|
|
`placed_structure_construct_entry_from_candidate_and_world_args` `0x0040f6d0` now bounds the
|
|
|
|
|
|
actual field-seeding layer beneath that allocator. The old unresolved higher placement chooser is
|
|
|
|
|
|
bounded more cleanly now too: the `0x00403ed5` and `0x0040446b` direct-placement commits sit
|
|
|
|
|
|
inside one larger helper,
|
|
|
|
|
|
`city_connection_try_build_route_with_optional_direct_site_placement` `0x00402cb0`. That shared
|
|
|
|
|
|
heavy builder is now the common target of the compact wrapper `0x00404640`, the peer-route
|
|
|
|
|
|
candidate builder `0x004046a0`, the region-entry pair wrapper
|
|
|
|
|
|
`city_connection_try_build_route_between_region_entry_pair` `0x00404c60`, and the direct retry
|
|
|
|
|
|
paths inside `simulation_try_select_and_publish_company_start_or_city_connection_news`
|
|
|
|
|
|
`0x00404ce0`. Internally it now has three bounded lanes:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
an early route-entry search or synthesis attempt through
|
|
|
|
|
|
`route_entry_collection_try_build_path_between_optional_endpoint_entries` `0x004a01a0`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
a single-endpoint direct-placement lane that scans `Maintenance` and `ServiceTower` candidates
|
|
|
|
|
|
and commits through `0x00403ed5 -> 0x004134d0 -> 0x0040ef10`,
|
|
|
|
|
|
and a later paired-endpoint fallback lane that seeds two endpoint candidates from the same stem
|
|
|
|
|
|
family, walks a temporary route-entry list, and commits through
|
|
|
|
|
|
`0x0040446b -> 0x004134d0 -> 0x0040ef10`.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
That early lane can now be described more concretely: it can reuse supplied route-entry
|
|
|
|
|
|
endpoints, synthesize a missing leading endpoint from the caller's coordinates, seed the
|
|
|
|
|
|
route-store builder-state block, and then hand off to the deeper path-search core without
|
|
|
|
|
|
placing a new site. The previously vague side family at `0x006cfcb4` is tighter too: current
|
|
|
|
|
|
evidence now bounds it as a small auxiliary route-entry tracker collection with an allocator at
|
|
|
|
|
|
`0x004a42b0`, a refcount or destroy path at `0x004a4340`, a direct route-entry group-id setter
|
2026-04-06 01:11:06 -07:00
|
|
|
|
at `0x00489f80`, an endpoint-membership probe at `0x00494ed0`, a boolean-latch refresh or
|
|
|
|
|
|
owner-notify path at `0x00494fb0`, and a two-endpoint merge or bind helper at `0x00494f00`.
|
|
|
|
|
|
That bind side is tighter now too: the trackers group route entries only when a route-key-like
|
|
|
|
|
|
value from `0x0048aa70`, the route-entry signature word `+0x22e`, and the boolean latch derived
|
|
|
|
|
|
from byte `+0x44` all agree. The deeper handoff under that lane is no longer anonymous either:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`route_entry_collection_search_path_between_entry_or_coord_endpoints` `0x0049d380` is now
|
|
|
|
|
|
bounded as the internal search core that runs the first candidate sweep, quality gates, and the
|
|
|
|
|
|
later route-extension fallbacks before returning one resolved route-entry id or `-1`. The first
|
|
|
|
|
|
sweep now has a bounded owner at `0x0049bd40`, and the two later fallback helpers are bounded as
|
|
|
|
|
|
the route-entry point-window coverage query `0x00494cb0` and the frontier-extension helper
|
|
|
|
|
|
`0x0049c900`. The math side is bounded more cleanly now too: the family reuses
|
|
|
|
|
|
`math_measure_float_xy_pair_distance` `0x0051db80` for its direct point-to-point scalar,
|
|
|
|
|
|
`math_compute_quadrant_adjusted_heading_angle_from_xy_pair` `0x004952f0` for heading-angle
|
|
|
|
|
|
construction, `math_normalize_subtracted_angle_delta_and_report_wrap` `0x004953c0` for wrapped
|
|
|
|
|
|
angle-delta checks, and `math_abs_double_with_crt_special_case_handling` `0x005a152e` for the
|
|
|
|
|
|
absolute-value side of the quality gates. Current evidence also bounds the threshold shape a
|
|
|
|
|
|
little more tightly: the initial sweep and later extension path both reuse one signed angle-bias
|
|
|
|
|
|
term `+/-0.181000038854627`, and the first sweep switches between a looser `1.4` and stricter
|
|
|
|
|
|
`1.8` quality multiplier depending on route-policy byte `4` or the broader display-runtime
|
|
|
|
|
|
preference `[0x006cec78+0x4c74]`, already grounded elsewhere as `Auto-Show Grade During Track
|
|
|
|
|
|
Lay`.
|
|
|
|
|
|
The small policy-byte semantics are still not fully closed, but current evidence is tighter than
|
|
|
|
|
|
before. Literal byte `1` is now the strongest current match for direct linked-site
|
|
|
|
|
|
endpoint-anchor creation or replacement, because the linked-site constructor and neighboring
|
|
|
|
|
|
repair branches around `0x00480463`, `0x00480a77`, and `0x00480b69` all feed that byte into
|
|
|
|
|
|
`0x00493cf0` before rebinding one chosen route-entry anchor through `0x0048abc0`. Literal byte
|
|
|
|
|
|
`2` now looks broader: the later world-side caller at `0x00480cd0` and the linked-site refresh
|
|
|
|
|
|
helper `placed_structure_refresh_linked_site_display_name_and_route_anchor` `0x00480bb0` both
|
|
|
|
|
|
reach `0x004a01a0` with both optional endpoint-entry ids unset and that byte, so the strongest
|
|
|
|
|
|
current read is a full linked-site route-anchor rebuild between optional endpoint entries rather
|
|
|
|
|
|
than the narrower direct creation lane. The TrackLay side now gives one tighter user-facing
|
|
|
|
|
|
anchor too: the live mode field `0x00622b0c` is already grounded as `Lay single track.` `0x1`
|
|
|
|
|
|
versus `Lay double track.` `0x4`, and the small mapper `0x004955b0` collapses that state into
|
|
|
|
|
|
the endpoint-policy bytes later passed to `0x00493cf0`, so the strongest current read is
|
|
|
|
|
|
`policy 1 = single-track endpoint synthesis` and `policy 4 = double-track endpoint synthesis`.
|
|
|
|
|
|
Bytes `1/2` are still the ones that enable the
|
|
|
|
|
|
auxiliary tracker lane in `0x004a01a0`, while byte `4` now reads more narrowly as the larger
|
|
|
|
|
|
company-side endpoint-synthesis charge inside `0x00493cf0`, because that helper feeds `-1`
|
|
|
|
|
|
versus `-2` into the saturating company counter helper `0x00423ec0` on `[company+0x7680]`
|
|
|
|
|
|
rather than skipping the company branch entirely. That counter is tighter now too: nearby
|
|
|
|
|
|
company initialization seeds it to `50` when scenario byte `0x4aaf` is enabled and to sentinel
|
|
|
|
|
|
`-1` otherwise, while the companion getter `0x004240a0` returns either the live counter or fixed
|
|
|
|
|
|
fallback `29999`. Current language-table correlation also gives `0x4aaf` a stronger
|
|
|
|
|
|
player-facing meaning: it is the live gate behind `Company track laying is limited...` and the
|
|
|
|
|
|
event variable label `Company Track Pieces Buildable`. So the current strongest read is
|
|
|
|
|
|
available track-laying capacity, not an abstract company route-budget. That site-side helper
|
|
|
|
|
|
also closes one adjacent ownership gap: beneath
|
|
|
|
|
|
`placed_structure_finalize_creation_or_rebuild_local_runtime_state` `0x0040ef10`, it rebuilds
|
|
|
|
|
|
one linked site's route-entry anchor and display-name buffer. That path is tighter now than just
|
|
|
|
|
|
“bind anchor then touch `0x006cfcb4`”: after the literal-policy-`2` rebuild succeeds it
|
|
|
|
|
|
re-enters `aux_route_entry_tracker_collection_refresh_route_entry_group_membership` `0x004a45f0`,
|
2026-04-06 01:11:06 -07:00
|
|
|
|
which can now be described more concretely. Its early prepass at `0x004a4380` can split one
|
|
|
|
|
|
mismatching adjacent subchain into a fresh tracker group; its later helper at `0x004a4ce0` can
|
|
|
|
|
|
transfer one compatible adjacent chain between neighboring groups; and the repair-side helper at
|
|
|
|
|
|
`0x004a4ff0` can reseed a whole route-entry component into a fresh tracker when the old group id
|
|
|
|
|
|
is missing or invalid. The regrouping pass also still rewrites compatible endpoint slots through
|
|
|
|
|
|
`0x004950f0` and refreshes one nearby cached-match payload band through `0x00495020` before the
|
|
|
|
|
|
helper resumes the name-buffer work. The remaining display-name side still matches the earlier
|
2026-04-05 20:11:47 -07:00
|
|
|
|
grounded text family: copy the resolved city name when available, append civic suffixes
|
|
|
|
|
|
`Township`, `New`, `Modern`, or `Renaissance`, append `Service Tower` or `Maintenance Facility`
|
|
|
|
|
|
on the linked-instance class branches, and otherwise fall back through the older
|
|
|
|
|
|
`Junction`..`Center` and `Anytown` text families.
|
2026-04-06 01:11:06 -07:00
|
|
|
|
The higher owner-refresh side of that same tracker family is tighter now too. The reusable gate
|
|
|
|
|
|
at `0x004a4c00` no longer looks like a stray predicate: it explicitly requires both route-entry
|
|
|
|
|
|
trackers to keep matching cached fields `+0x18/+0x1c/+0x1d/+0x1e` and both route entries to keep
|
|
|
|
|
|
their special side fields `+0x20e/+0x222` unset before adjacent-chain transfer is allowed. The
|
|
|
|
|
|
collection-wide traversal sweep at `0x004a5fc0` is bounded now as the connected-component refresh
|
|
|
|
|
|
owner over tracker field `+0x0c`, with a temporary queue at `[this+0x94]` and direct fallback
|
|
|
|
|
|
into the reseed helper `0x004a4ff0` whenever one neighboring route entry has lost valid tracker
|
|
|
|
|
|
ownership. Above both of those sits `aux_route_entry_tracker_collection_refresh_owner_adjacent_compatible_group_links`
|
|
|
|
|
|
`0x004a6360`, which now reads as the owner-side latch-change refresher invoked from
|
|
|
|
|
|
`0x00494fb0`: it starts from one owner route-entry id, confirms both bound endpoint entries agree
|
|
|
|
|
|
on the boolean class state implied by route-entry byte `+0x44`, and then probes both endpoint
|
|
|
|
|
|
sides for adjacent route-entry groups that can be absorbed through the `0x004a4c00 -> 0x004a4ce0`
|
|
|
|
|
|
gate-and-transfer pair. The sibling query side is bounded now too:
|
|
|
|
|
|
`aux_route_entry_tracker_collection_query_component_label_by_tracker_id` `0x004a6320` is the
|
|
|
|
|
|
dirty-aware accessor for tracker field `+0x0c`, while
|
|
|
|
|
|
`aux_route_entry_tracker_dispatch_route_entry_pair_metric_query` `0x004a65b0` is the remaining
|
|
|
|
|
|
mode-switched lower metric dispatcher beneath the heavier chooser at `0x004a6630`, forwarding
|
|
|
|
|
|
fixed route-entry-pair candidate sets into either `0x004a5280` or `0x004a5900` depending on the
|
|
|
|
|
|
shared hundredths-scaled build-version query
|
|
|
|
|
|
`runtime_query_hundredths_scaled_build_version` `0x00482e00` over `0x006cec74`. The current
|
|
|
|
|
|
caller thresholds `>= 0x67`, `>= 0x68`, `>= 0x69`, and `>= 0x6a` now line up with executable
|
2026-04-06 20:34:16 -07:00
|
|
|
|
build values `1.03`, `1.04`, `1.05`, and `1.06`, and the version source itself can come from
|
|
|
|
|
|
the multiplayer companion path as well as the local executable, so this split is no longer
|
|
|
|
|
|
best-read as a time- or era-side cutover at all. It now reads more cleanly as a pre-`1.03`
|
|
|
|
|
|
versus `1.03+` route-metric compatibility dispatcher. That lower split is tighter now too:
|
2026-04-06 01:11:06 -07:00
|
|
|
|
`0x004a5280` is the weighted recursive branch with heuristic ordering, per-tracker best-cost
|
|
|
|
|
|
cache `0x006cfcac`, and prune threshold `0x006cfcb0`, while `0x004a5900` is the alternate
|
|
|
|
|
|
recursive neighbor-walk branch that stays within compatible component labels through
|
|
|
|
|
|
`0x004a62c0` and accumulates step and mismatch penalties over route-entry links
|
|
|
|
|
|
`+0x206/+0x20a/+0x20e`. Above both of them,
|
|
|
|
|
|
`aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
|
|
|
|
|
`0x004a6630` is now bounded as the real chooser: direct fixed-pair query when both entries pass
|
|
|
|
|
|
`0x0048b870`, otherwise endpoint-pair fallback across the two tracker groups.
|
|
|
|
|
|
The adjacent route-style rebuild side is tighter now too: the same build-version family reaches
|
|
|
|
|
|
`placed_structure_rebuild_route_style_candidate_scores_and_peer_links` `0x004101e0`, where build
|
|
|
|
|
|
`1.04+` keeps one `Recycling Plant` stem-specific compatibility branch alive and build `1.05+`
|
|
|
|
|
|
skips one older descriptor-side attenuation fallback. There is also one neighboring world-side
|
|
|
|
|
|
compatibility lane now bounded below the same family: `placed_structure_collect_connected_component_tile_bounds_with_version_gate`
|
|
|
|
|
|
`0x004160c0` skips one older recursive component-bounds walk through `0x00415f20` on build
|
|
|
|
|
|
`1.03+` when scenario field `[0x006cec78+0x46c38]` is already active. The wrapper above that
|
|
|
|
|
|
path is bounded now too: `placed_structure_map_tile_range_to_connected_component_records_with_optional_bounds_refresh`
|
|
|
|
|
|
`0x00416170` walks the caller rectangle, maps tile keys into the component table rooted at
|
|
|
|
|
|
`0x0062ba7c`, and only re-enters `0x004160c0` when the current owner still has no cached bounds
|
|
|
|
|
|
and the local world or scenario suppressors stay clear. The higher neighboring raster side is
|
|
|
|
|
|
tighter too. `world_grid_refresh_projected_rect_sample_band_and_flag_mask` `0x00418610` is the
|
|
|
|
|
|
shared projected-rectangle helper above `world_grid_refresh_flagged_region_float_extrema_and_mean`
|
|
|
|
|
|
`0x00415020`: it refreshes the temporary sample band at `0x0062b7d0`, publishes the surviving
|
|
|
|
|
|
rectangle through `0x0044d410`, and on the single-sample path re-enters
|
|
|
|
|
|
`world_grid_toggle_flagged_mask_bit0_for_nonsentinel_rect_samples` `0x004185a0` to flip mask bit
|
|
|
|
|
|
`0x01` for the corresponding non-sentinel cells. Current grounded callers for `0x00418610` are
|
|
|
|
|
|
the neighboring placed-structure local-runtime helper `0x00418be0` and the heavier placement
|
|
|
|
|
|
validator `0x004197e0`, so this adjacent family now reads more like projected placement or local
|
|
|
|
|
|
raster prep than an unowned generic world-grid scan. The higher owner split is tighter now too.
|
|
|
|
|
|
`placed_structure_build_local_runtime_record_from_candidate_stem_and_projected_scratch`
|
|
|
|
|
|
`0x00418be0` is the broader construction or rebuild lane: resolve one candidate id from a stem,
|
|
|
|
|
|
build projected scratch through `0x00416ec0`, then publish the projected rectangle and validate
|
|
|
|
|
|
its side windows through `placed_structure_publish_projected_runtime_rect_globals_and_validate_side_windows`
|
|
|
|
|
|
`0x00416620`. That publish pass is bounded now too: it stages `0x0062b308/0x0062b30c/0x0062b310`,
|
|
|
|
|
|
validates the rectangle against route-entry coverage through
|
|
|
|
|
|
`route_entry_collection_query_rect_window_passes_entry_type_gate` `0x00494240`, can branch into
|
|
|
|
|
|
the special projected-slot picker `placed_structure_try_select_projected_rect_profile_slot`
|
|
|
|
|
|
`0x00415570`, and refreshes the compact per-cell side tables through
|
|
|
|
|
|
`world_grid_refresh_projected_rect_surface_and_region_byte_tables` `0x00414e10` before the
|
|
|
|
|
|
finished scratch is copied into one queued runtime record and the new record re-enters
|
|
|
|
|
|
`0x00416170` when its rectangle is valid. The smaller sibling
|
|
|
|
|
|
`placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds`
|
|
|
|
|
|
`0x00418a60` now reads as the current-subject clone path above the same connected-component and
|
|
|
|
|
|
mask-refresh helpers, returning the cloned local runtime record later stored into `[site+0x24e]`
|
|
|
|
|
|
by `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
|
|
|
|
|
`0x0040e450`. The higher wrapper above that clone path is bounded now too:
|
|
|
|
|
|
`placed_structure_collection_refresh_local_runtime_records_and_position_scalars` `0x004133b0`
|
|
|
|
|
|
first drains the temporary site-id queue rooted at `0x0062ba64/0x0062ba6c/0x0062ba70` through
|
|
|
|
|
|
`placed_structure_local_runtime_site_id_queue_count` `0x00414480` and
|
|
|
|
|
|
`placed_structure_local_runtime_site_id_queue_pop_next` `0x00413f50`, rebuilding one cloned
|
|
|
|
|
|
local-runtime record per queued placed structure through `0x0040e450`, and only then sweeps all
|
|
|
|
|
|
live placed structures through the side refresh helper
|
|
|
|
|
|
`placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon` `0x0040ee10`.
|
|
|
|
|
|
The last lower side reads are tighter now too. `0x0040e450` first seeds the projected-slot cache
|
|
|
|
|
|
through `placed_structure_cache_projected_rect_profile_slot_id` `0x00414470` before it re-enters
|
|
|
|
|
|
`0x00418a60`, and the broader stem-based builder at `0x00418be0` now has one named optional side
|
|
|
|
|
|
renderer instead of an anonymous callsite:
|
|
|
|
|
|
`placed_structure_render_local_runtime_overlay_payload_from_projected_bounds` `0x00418040`.
|
|
|
|
|
|
The side refresh split is bounded now too. `0x0040ee10` publishes one local position or scalar
|
|
|
|
|
|
triplet through the shared setter `0x00530720`, then tails into
|
|
|
|
|
|
`placed_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime` `0x0040e360`;
|
|
|
|
|
|
that smaller follow-on only runs on the current subtype-`1`, class-`3`, linked-site branch, and
|
|
|
|
|
|
recomputes one local-runtime triplet from the linked peer's route-entry anchor when that anchor
|
|
|
|
|
|
is still live. The heavier sibling above that side refresh is bounded now too:
|
|
|
|
|
|
`placed_structure_set_world_coords_and_refresh_local_runtime_side_state` `0x0040eba0` is the
|
|
|
|
|
|
world-coordinate mutation helper that recomputes `[site+0x388]` and `[site+0x38c]`, rewrites the
|
|
|
|
|
|
world-grid owner mapping through `0x0042c9f0` and `0x0042c9a0`, updates the subtype-`4`
|
|
|
|
|
|
proximity-bucket family when needed, rebuilds the same local position or scalar triplet, and
|
|
|
|
|
|
then tails into the linked-site anchor follow-on at `0x0040e360`.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The sibling policy-`1` side is tighter now too. The constructor lane no longer stops at “one
|
|
|
|
|
|
linked site id at `[site+0x2a8]`”: the subtype-`1` branch in
|
|
|
|
|
|
`placed_structure_construct_entry_from_candidate_and_world_args` `0x0040f6d0` now clearly
|
|
|
|
|
|
allocates that linked record through
|
|
|
|
|
|
`placed_structure_collection_allocate_and_construct_linked_site_record` `0x00481390`, whose lower
|
|
|
|
|
|
constructor is `placed_structure_construct_linked_site_record_from_anchor_and_coords`
|
|
|
|
|
|
`0x00480210`. That lower constructor seeds the linked record's own id and anchor-site id, clears
|
|
|
|
|
|
the local route-anchor and display-name fields, projects the anchor footprint into world space,
|
|
|
|
|
|
and then either binds an already-covered route entry through `0x00417b40` or falls through into
|
|
|
|
|
|
the neighboring policy-`1` route-entry synthesis family around `0x00493cf0` before rebinding the
|
|
|
|
|
|
chosen route entry through `0x0048abc0`.
|
|
|
|
|
|
The cleanup side is tighter now too. Linked-site removal now has a bounded owner at
|
|
|
|
|
|
`placed_structure_collection_remove_linked_site_record` `0x004813d0`: it resolves the linked
|
|
|
|
|
|
record through `0x006cec20`, runs the per-record teardown
|
|
|
|
|
|
`placed_structure_teardown_linked_site_runtime_state_before_removal` `0x00480590`, removes the
|
|
|
|
|
|
live entry from the collection, and only then re-enters the still-bounded company-wide follow-on
|
|
|
|
|
|
at `0x00429c10` when the removed record passed the narrower transit-like latch. That per-record
|
|
|
|
|
|
teardown is no longer just “clear some scratch fields.” It now clearly clears the route-style
|
|
|
|
|
|
scratch lane, clears the five proximity buckets at `[site+0x590..0x5b8]`, detaches or invalidates
|
|
|
|
|
|
the current route-entry anchor, frees the three per-site byte arrays at `[site+0x24..0x2c]`,
|
|
|
|
|
|
clears this site's indexed byte from the corresponding arrays of later placed-structure records,
|
|
|
|
|
|
and then re-enters `0x00436040` with the current site id.
|
|
|
|
|
|
That company-side follow-on is no longer just one opaque callback either. It is now bounded as
|
|
|
|
|
|
`company_collection_refresh_active_company_linked_transit_site_peer_caches` `0x00429c10`, which
|
|
|
|
|
|
walks the active company roster and re-enters
|
|
|
|
|
|
`company_rebuild_linked_transit_site_peer_cache` `0x004093d0` on each company. That per-company
|
|
|
|
|
|
fast pass stamps a refresh tick at `[company+0x0d3e]`, clears and repopulates the
|
|
|
|
|
|
placed-structure-side cache cells addressed through `[site+0x5bd][company_id]`, marks the
|
|
|
|
|
|
eligible linked transit sites for that company, allocates one `0x0d`-stride peer table for each
|
2026-04-06 01:11:06 -07:00
|
|
|
|
eligible site, and fills those peer rows from
|
|
|
|
|
|
`aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
|
|
|
|
|
`0x004a6630`. That helper no longer reads as one anonymous route sweep: it either uses the fixed
|
|
|
|
|
|
pair directly or falls back across tracker endpoint combinations before returning the winning
|
|
|
|
|
|
route-entry id, one route-step count, and one companion mismatch count. The fast cache now reads
|
|
|
|
|
|
more cleanly too: peer-record dword `+0x05` stores that step count, while float `+0x09` stores
|
|
|
|
|
|
the normalized continuity share derived from `(steps - mismatches) / max(steps, 1)`, not a raw
|
|
|
|
|
|
delta-per-step ratio. The
|
2026-04-05 20:11:47 -07:00
|
|
|
|
adjacent timed wrapper `company_service_linked_transit_site_caches` `0x00409720` now shows the
|
|
|
|
|
|
cadence too: `0x004093d0` is the shorter-interval refresh, while the older heavier sibling at
|
|
|
|
|
|
`0x00407bd0` was only revisited on the longer interval.
|
|
|
|
|
|
That heavier sibling is now bounded too:
|
|
|
|
|
|
`company_rebuild_linked_transit_autoroute_site_score_cache` `0x00407bd0` no longer looks like a
|
2026-04-06 20:34:16 -07:00
|
|
|
|
generic tail refresh. It reuses the fast peer tables, rebuilds candidate-local amount bands plus
|
|
|
|
|
|
normalized issue-opinion scales, and then folds the peer-side route metrics back into three
|
|
|
|
|
|
per-site cache floats with a cleaner split:
|
|
|
|
|
|
`+0x12` is the raw surviving site-score total,
|
|
|
|
|
|
`+0x0e` is the continuity-and-step-weighted companion total,
|
|
|
|
|
|
and `+0x16` is the promoted final site-ranking lane chosen from the strongest grouped candidate
|
|
|
|
|
|
bands.
|
|
|
|
|
|
That also closes most of the cross-version impact question:
|
|
|
|
|
|
the pre-`1.03` versus `1.03+` tracker metric split now looks like it mainly perturbs the weighted
|
|
|
|
|
|
`+0x0e` lane and the promoted `+0x16` lane, not the raw `+0x12` total.
|
|
|
|
|
|
That final lane then feeds the neighboring selectors
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_select_best_owned_linked_transit_site_by_autoroute_score` `0x00408280` and
|
|
|
|
|
|
`company_build_linked_transit_autoroute_entry` `0x00408380`. That makes the company-side follow-on
|
|
|
|
|
|
read more like a linked-transit autoroute cache family than a generic company maintenance pass.
|
|
|
|
|
|
The neighboring reachability gate is tighter now too:
|
|
|
|
|
|
`company_query_cached_linked_transit_route_anchor_entry_id` `0x00401860` caches one company-side
|
|
|
|
|
|
route-entry anchor, and
|
|
|
|
|
|
`placed_structure_is_linked_transit_site_reachable_from_company_route_anchor` `0x004801a0`
|
|
|
|
|
|
uses that anchor to decide whether a foreign linked transit site can still participate in the
|
|
|
|
|
|
current company's fast peer cache.
|
|
|
|
|
|
The first direct train-side consumer above that cache family is bounded now too:
|
|
|
|
|
|
`train_try_append_linked_transit_autoroute_entry` `0x00409770`. After servicing the owning
|
|
|
|
|
|
company's caches, it asks `0x00408380` for one staged `0x33`-byte route entry using the train's
|
|
|
|
|
|
current anchor site, then either appends that entry through `0x004b3160` and refreshes the new
|
|
|
|
|
|
trailing selection through `0x004b2f00`, or rotates one existing slot in place when the local
|
|
|
|
|
|
two-entry cap has already been reached. So this edge now reaches an actual train-side autoroute
|
2026-04-08 16:31:33 -07:00
|
|
|
|
append lane rather than stopping at anonymous company-side cache cells. The weighted cache lanes
|
|
|
|
|
|
still do not escape that route-choice family directly: this train-side append path only inherits
|
|
|
|
|
|
the weighted site and peer choice by calling `0x00408380`, not by reading cache `+0x0e/+0x16`
|
|
|
|
|
|
itself.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The train-side follow-on above that seed path is bounded now too. The owning company can count
|
|
|
|
|
|
its live roster through `company_count_owned_trains` `0x004264c0`, measure one aggregate linked
|
|
|
|
|
|
transit site pressure through `company_compute_owned_linked_transit_site_score_total`
|
|
|
|
|
|
`0x00408f70`, and then rebalance that roster through
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`company_balance_linked_transit_train_roster` `0x00409950`.
|
|
|
|
|
|
The aggregate helper no longer reads as a raw sum either:
|
|
|
|
|
|
it starts from the site-cache `+0x12` totals, converts that into one tentative roster target
|
|
|
|
|
|
through year and site-count ladders, and on build `1.03+` adds one special distance-side scaling
|
|
|
|
|
|
branch when exactly two eligible linked transit sites survive.
|
|
|
|
|
|
Because it consumes `+0x12` rather than `+0x0e` or `+0x16`, current evidence now says the tracker
|
|
|
|
|
|
compatibility split is more important for seeded route choice and ranked site choice than for the
|
2026-04-08 16:31:33 -07:00
|
|
|
|
final company train-count target itself. The local linked-transit chain is now bounded enough to
|
|
|
|
|
|
say that directly: weighted cache lanes feed `0x00408280 -> 0x00408380 -> 0x00409770/0x00409830`,
|
|
|
|
|
|
while the separate pressure or roster lane feeds `0x00408f70 -> 0x00409950` from raw `+0x12`.
|
2026-04-06 20:34:16 -07:00
|
|
|
|
The balancer then applies two age bands to company-owned trains:
|
|
|
|
|
|
very old trains are removed when the roster already exceeds target or upgraded when it still
|
|
|
|
|
|
needs capacity, while the mid-age band can trigger one narrower upgrade pass.
|
|
|
|
|
|
After that it fills any remaining deficit by either packaging multiplayer opcode `0x75` or
|
|
|
|
|
|
locally re-entering
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_try_add_linked_transit_train_and_publish_news` `0x00409830`. The two visible news
|
|
|
|
|
|
helpers under it are bounded too: `0x00409830` emits RT3.lng `2896` for a newly added train,
|
|
|
|
|
|
while `company_publish_train_upgrade_news` `0x00409300` emits RT3.lng `2897` for the upgrade
|
|
|
|
|
|
branch.
|
|
|
|
|
|
The subtype-`4` sibling side is bounded now too. The nearby-site bucket family now has:
|
|
|
|
|
|
`placed_structure_append_nearby_transit_site_distance_bucket_entry` `0x0047fdb0`,
|
|
|
|
|
|
`placed_structure_remove_site_id_from_proximity_bucket_lists` `0x0047dd10`, and
|
|
|
|
|
|
`placed_structure_clear_proximity_bucket_lists` `0x0047dcd0`, plus the two collection sweeps
|
|
|
|
|
|
`placed_structure_collection_append_site_into_all_proximity_bucket_lists` `0x00481480` and
|
|
|
|
|
|
`placed_structure_collection_remove_site_id_from_all_proximity_bucket_lists` `0x004814c0`.
|
|
|
|
|
|
Current evidence says those five buckets store `(peer site id, distance)` pairs for nearby
|
|
|
|
|
|
station-or-transit peers, grouped through the five-way classifier at `0x0040d350`. The older
|
|
|
|
|
|
stream-backed side is bounded too: `placed_structure_collection_load_dynamic_side_buffers_from_stream`
|
|
|
|
|
|
`0x00481430` walks the live placed-structure collection and re-enters
|
|
|
|
|
|
`placed_structure_load_dynamic_side_buffers_from_stream` `0x0047d8e0`, which repopulates the
|
|
|
|
|
|
route-entry list, the three per-site byte arrays, the five proximity buckets, and the trailing
|
|
|
|
|
|
scratch band from the caller-supplied persistence stream.
|
|
|
|
|
|
The linked-site route-entry list itself is tighter now too. The refresh or teardown branch at
|
|
|
|
|
|
`0x0040e102` re-enters `placed_structure_remove_route_entry_key_and_compact` `0x0047d810`, which
|
|
|
|
|
|
removes one matching `u16` key from the six-byte list rooted at `[site+0x462]/[site+0x466]`,
|
|
|
|
|
|
compacts the surviving entries into a replacement buffer, and decrements the stored route-entry
|
|
|
|
|
|
count.
|
|
|
|
|
|
The broader company-side owner above these pieces is tighter now too. The periodic service pass
|
|
|
|
|
|
`company_service_periodic_city_connection_finance_and_linked_transit_lanes` `0x004019e0` now
|
|
|
|
|
|
reads as the current outer owner for this branch: it sequences the city-connection announcement
|
|
|
|
|
|
lanes, the linked-transit train-roster balancer, the acquisition-side sibling
|
|
|
|
|
|
`company_try_buy_unowned_industry_near_city_and_publish_news` `0x004014b0`, the annual finance-policy helper
|
|
|
|
|
|
`company_evaluate_annual_finance_policy_and_publish_news` `0x00401c50`, and the shorter-versus-
|
|
|
|
|
|
longer linked-transit cache refresh tail through `0x004093d0` and `0x00407bd0`. That outer pass
|
|
|
|
|
|
also has one tighter temporary-state role now: it clears company bytes `0x0d17/0x0d18/0x0d56`,
|
|
|
|
|
|
mirrors `0x0d17` into scenario field `0x006cec78+0x4c74` only while the earlier route-building
|
|
|
|
|
|
side runs, and restores the original scenario value on exit. Current evidence now ties `0x0d17`
|
|
|
|
|
|
to the shared preferred-locomotive chooser `0x004078a0`: the override is only armed when that
|
|
|
|
|
|
helper picks one locomotive whose engine-type dword is `2`. That engine-type lane now best reads
|
|
|
|
|
|
as electric rather than an unnamed class slot, because the linked approval helper around
|
|
|
|
|
|
`0x0041d550` now reads as a real locomotive-era and engine-type policy gate, dispatching the same
|
|
|
|
|
|
`0/1/2` field across three scenario-opinion lanes while the local player-facing engine-type
|
|
|
|
|
|
family is explicitly `Steam / Diesel / Electric`. That same gate also now ties the later scenario
|
|
|
|
|
|
bytes `0x4c97..0x4c99` back to the editor-side `Locomotives` page rather than treating them as
|
|
|
|
|
|
anonymous availability flags: they are the live `All Steam Locos Avail.`, `All Diesel Locos
|
|
|
|
|
|
Avail.`, and `All Electric Locos Avail.` policy bytes behind `0x004cd680` / `0x004cf0d0`. The
|
|
|
|
|
|
current logic is also tighter than a plain override tier: a positive engine-family opinion result
|
|
|
|
|
|
can carry the gate by itself only while none of those three editor bytes is enabled; once any of
|
|
|
|
|
|
them is nonzero, `0x0041d550` instead requires one matching intersection between the record-local
|
|
|
|
|
|
family bytes and the enabled editor family bytes. The `WhaleL` carveout is narrower now too:
|
|
|
|
|
|
current data-file correlation ties that stem to the `Orca NX462` / `WhaleL_NE` locomotive family,
|
|
|
|
|
|
and it explicitly zeros that positive-opinion result before the later family-availability checks,
|
|
|
|
|
|
so it loses the shortcut rather than gaining a special approval path. The same
|
|
|
|
|
|
chooser now also has a bounded fallback below it, `locomotive_collection_select_best_era_matched_non_electric_fallback_id`
|
|
|
|
|
|
`0x00461cd0`, which explicitly skips engine-type `2` and chooses the lowest-penalty remaining
|
|
|
|
|
|
locomotive by era mismatch and approval gates. The route-search side is tighter too: this
|
|
|
|
|
|
electric-only override now clearly feeds the first path-sweep branch in `0x0049bd40`, forcing the
|
|
|
|
|
|
larger `1.8` initial quality multiplier instead of `1.4`, which is the same branch otherwise
|
|
|
|
|
|
chosen by explicit route-policy byte `4`. So the later annual finance helper is reading
|
|
|
|
|
|
same-cycle side-channel state from the earlier city-connection and linked-transit branches, not
|
|
|
|
|
|
unrelated long-lived finance fields. The inner
|
|
|
|
|
|
finance helper is not debt-only either. Current grounded outcomes include bankruptcy news
|
|
|
|
|
|
`2881`, the debt restructure family `2882..2886`, a later share-repurchase headline `2887`, and
|
|
|
|
|
|
the dividend-side adjustment branch. The main commit verbs under that helper are now grounded too:
|
|
|
|
|
|
`company_repay_bond_slot_and_compact_debt_table` `0x00423d70`,
|
|
|
|
|
|
`company_issue_bond_and_record_terms` `0x004275c0`,
|
|
|
|
|
|
`company_repurchase_public_shares_and_reduce_capital` `0x004273c0`, and
|
|
|
|
|
|
`company_issue_public_shares_and_raise_capital` `0x00427450`. The threshold side is tighter now
|
|
|
|
|
|
too. The earliest creditor-pressure lane requires scenario mode `0x0c`, the bankruptcy toggle
|
|
|
|
|
|
`[0x006cec78+0x4a8f]` to be clear, at least `13` years since `[company+0x163]`, and at least
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`4` years since founding year `[company+0x157]`; it then scans the last three years of the
|
|
|
|
|
|
derived net-profits and revenue lanes `0x2b` and `0x2c`, chooses one negative pressure ladder
|
|
|
|
|
|
`-600000 / -1100000 / -1600000 /
|
|
|
|
|
|
-2000000` from the current slot-`0x2c` bands around `120000 / 230000 / 340000`, requires the
|
|
|
|
|
|
broader support-adjusted share-price or public-support scalar at least `15` or `20` depending on
|
|
|
|
|
|
whether all three sampled years failed, checks the current fuel-cost lane in slot `0x09` against
|
|
|
|
|
|
`0.08` times that ladder, and requires the
|
|
|
|
|
|
three-year slot-`0x2b` total to clear one final `-60000` gate before it falls into the
|
|
|
|
|
|
bankruptcy commit and RT3.lng `2881` headline.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The middle debt-capital layer is split more clearly now too. With `[+0x4a8b]` clear, one annual
|
|
|
|
|
|
bond lane first simulates full repayment through `company_repay_bond_slot_and_compact_debt_table`
|
|
|
|
|
|
and then uses the post-repayment cash window with fixed `-250000` and `-30000` thresholds plus
|
|
|
|
|
|
the broader linked-transit train-service latch `[company+0x0d56]` to decide whether to append one
|
|
|
|
|
|
or more `500000` principal, `30`-year bonds. The repurchase lane is separate again: when the
|
|
|
|
|
|
city-connection announcement-side latch `[company+0x0d18]` is set, growth setting `2` does not
|
|
|
|
|
|
suppress it, and `[+0x4a87]` is clear, it starts from one `1000`-share batch, can replace its
|
|
|
|
|
|
default `1.0` factor with one linked-chairman personality scalar, scales that by `1.6` when
|
|
|
|
|
|
growth setting `1` is active, and then runs one `800000` stock-value gate plus one
|
2026-04-06 20:34:16 -07:00
|
|
|
|
support-adjusted-share-price-times-factor-times-`1000`-times-`1.2` affordability gate before the
|
|
|
|
|
|
repeated `1000`-share buyback commits behind RT3.lng `2887`. The ordering above this helper is
|
|
|
|
|
|
tighter now too:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_service_periodic_city_connection_finance_and_linked_transit_lanes` clears those latches
|
|
|
|
|
|
first, runs the city-connection and linked-transit branches, and only then enters the annual
|
|
|
|
|
|
finance helper, so these look like same-cycle reaction gates rather than long-lived balance-sheet
|
|
|
|
|
|
flags.
|
|
|
|
|
|
After the earlier debt or bankruptcy outcomes stay inactive, the later stock-capital lane also
|
2026-04-06 20:34:16 -07:00
|
|
|
|
has a tighter bounded shape now too: it only opens on build `1.03+`, only after the earlier
|
|
|
|
|
|
bankruptcy, bond, and repurchase outcomes stay inactive, and with the bond and stock toggles
|
|
|
|
|
|
`[+0x4a8b]` and `[+0x4a87]` clear, at least two bond slots live, and at least one year since
|
|
|
|
|
|
founding. It derives one issue batch from outstanding shares rounded down to `1000`-share lots
|
|
|
|
|
|
with floor `2000`, trims that batch until the broader support-adjusted share-price scalar times
|
2026-04-08 16:31:33 -07:00
|
|
|
|
batch no longer exceeds the `55000` gate, recomputes the pressured support-adjusted share-price
|
|
|
|
|
|
scalar and the paired `price/book` ratio, then tests the remaining gates in a fixed order:
|
|
|
|
|
|
share-price floor `22`, proceeds floor `55000`, current cash from `0x2329/0x0d` against the
|
|
|
|
|
|
chosen highest-coupon bond principal plus `5000`, one later stock-issue cooldown gate that
|
|
|
|
|
|
converts the current issue mixed-radix calendar tuple at `[company+0x16b/+0x16f]` through
|
|
|
|
|
|
`calendar_point_pack_tuple_to_absolute_counter` `0x0051d3c0` and compares the result against the
|
|
|
|
|
|
active world absolute calendar counter `[world+0x15]`, and only then the coupon-versus-price-to-book
|
|
|
|
|
|
approval ladder `0.07/1.3 -> 0.14/0.35`. The issue mutator preserves the previous tuple in
|
|
|
|
|
|
`[company+0x173/+0x177]` and refreshes the current tuple from `[world+0x0d/+0x11]` on the
|
|
|
|
|
|
derived-pricing lane. On success it issues two
|
2026-04-06 20:34:16 -07:00
|
|
|
|
same-sized tranches through repeated `company_issue_public_shares_and_raise_capital` calls and
|
|
|
|
|
|
publishes a separate equity-offering news family rooted at localized id `4053`, not the earlier
|
|
|
|
|
|
debt or buyback headline family.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The dividend side is bounded too: it requires the dividend toggle `[0x006cec78+0x4a93]` to be
|
|
|
|
|
|
clear, mode `0x0c`, at least `1` year since `[company+0x0d2d]`, and at least `2` years since
|
2026-04-06 20:34:16 -07:00
|
|
|
|
founding, then averages the last three years of the net-profits lane `0x2b`, folds in the
|
|
|
|
|
|
unassigned-share pool and the current `0x0d` band, applies the map-editor building-growth
|
|
|
|
|
|
setting `[0x006cec78+0x4c7c]`, treats growth setting `1` as a `0.66` scale-down and setting `2`
|
|
|
|
|
|
as a zeroing pass on the current dividend, quantizes surviving adjustments in tenths, and
|
|
|
|
|
|
finally clamps against
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_compute_board_approved_dividend_rate_ceiling` `0x00426260`.
|
|
|
|
|
|
The linked-transit route-seeding side has one tighter sibling now too:
|
|
|
|
|
|
`company_reset_linked_transit_caches_and_reseed_empty_train_routes` `0x00401940`. It clears the
|
|
|
|
|
|
two company-side linked-transit cache timestamps, forces one immediate cache rebuild through
|
|
|
|
|
|
`0x00409720`, strips route lists from company-owned trains in modes `0x0a/0x13`, and then
|
|
|
|
|
|
re-enters `train_try_append_linked_transit_autoroute_entry` `0x00409770` only when a train's
|
|
|
|
|
|
route list has become empty.
|
|
|
|
|
|
On the wider chooser question, the current evidence is also tighter than before: every recovered
|
|
|
|
|
|
external owner of `0x00402cb0` is still in the city-connection family, so the two later direct
|
|
|
|
|
|
placement lanes currently read as city-connection fallback behavior rather than a broadly shared
|
|
|
|
|
|
world placement service.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
It can still unwind through route-state cleanup without committing new placed structures, so the
|
|
|
|
|
|
exact lower helper semantics are not fully closed, but the broader chooser is no longer
|
|
|
|
|
|
anonymous and its main policy split is now visible. The two lower helpers directly under those
|
|
|
|
|
|
commit lanes are bounded now too:
|
|
|
|
|
|
`placed_structure_project_candidate_grid_extent_offset_by_rotation` `0x00417840` is the shared
|
|
|
|
|
|
projected-footprint offset helper over candidate bytes `[candidate+0xb8]` and `[candidate+0xb9]`,
|
|
|
|
|
|
and `placed_structure_validate_projected_candidate_placement` `0x004197e0` is the shared
|
|
|
|
|
|
go-or-no-go validator that checks company, territory, world-tile, and footprint occupancy state
|
2026-04-05 20:11:47 -07:00
|
|
|
|
before either direct-placement commit is allowed to fire. Its strongest current subtype branch is
|
|
|
|
|
|
a station-attachment or upgrade-style lane keyed by `[candidate+0x32] == 1`, which now has the
|
|
|
|
|
|
concrete failure family `2901..2906`: blocked upgrade footprint, ground not flat enough, not
|
|
|
|
|
|
your track, insufficient track-laying capacity, cannot connect to existing track, and ground too
|
|
|
|
|
|
steep. That tightens the chooser materially without yet proving that the validator is
|
|
|
|
|
|
station-only for every caller.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
The recurring outer owner is tighter now too:
|
|
|
|
|
|
`placed_structure_collection_refresh_quarter_subset_route_style_state` `0x00413580` walks every
|
|
|
|
|
|
fourth live placed structure from a scenario-time-derived offset and re-enters the per-site
|
|
|
|
|
|
rebuild with stack flag `0`, giving the route-style lane a concrete recurring maintenance sweep
|
|
|
|
|
|
under `simulation_service_periodic_boundary_work` rather than only a floating caller note. One
|
|
|
|
|
|
neighboring helper is now bounded on the message side too:
|
|
|
|
|
|
`shell_building_detail_handle_subject_value_row_band_action` `0x004ba270` switches over the
|
|
|
|
|
|
clicked row family `0x7d07..0x7d14`, treats subject bytes `+0x21/+0x22/+0x23` as one current
|
|
|
|
|
|
selection plus one bounded low/high pair, increments the family dirty latch at `0x006cfe0c` on
|
|
|
|
|
|
change, and republishes the refreshed value through the shared shell control helper on code
|
|
|
|
|
|
`0x66`. One neighboring helper is now bounded on the side-list path too:
|
|
|
|
|
|
`shell_building_detail_propagate_selected_subject_state_into_side_list` `0x004b9ec0` walks the
|
|
|
|
|
|
sibling list owner at `0x006cfe08`, copies the active subject state into each side-list record,
|
|
|
|
|
|
mirrors the current service or capability id at `+0x24`, and preserves the `0x40/0x20/0x10`
|
|
|
|
|
|
subflags from the active subject. That means the value-row actions and later selector rows now
|
|
|
|
|
|
read as propagating one shared building-detail state across the sibling list rather than only
|
|
|
|
|
|
mutating one isolated subject record. One neighboring helper is
|
|
|
|
|
|
now bounded separately too: `shell_building_detail_refresh_subject_pair_value_rows` `0x004bad20`
|
|
|
|
|
|
owns the mutually exclusive value-row pairs `0x7d07/0x7d08`, `0x7d11/0x7d12`, and
|
|
|
|
|
|
`0x7d13/0x7d14`, choosing among them from the same selected-subject flag byte and payload fields
|
|
|
|
|
|
at `+0x22/+0x23`, while the larger cargo-or-service row owner also gates the extra visual lanes
|
|
|
|
|
|
`0x7d6a`, `0x7d6b`, and `0x7d9d` from that same subflag family. The asset-string block is tighter
|
|
|
|
|
|
too: that branch family now explicitly uses `AnyCargo.imb`, `AnyFreight.imb`, `PassMail.imb`,
|
|
|
|
|
|
`Caboose.imb`, and `Dining.imb`, with the bit-`0x20` special lane already aligned to
|
|
|
|
|
|
`Caboose.imb` and the sibling bit-`0x10` special lane now aligned to `Dining.imb`. The larger
|
|
|
|
|
|
`0x7f58..0x801f` band is no longer just a styled row family either:
|
|
|
|
|
|
`shell_building_detail_present_flagged_service_capability_popup` `0x004b9fd0` is now grounded as
|
|
|
|
|
|
its explanatory popup callback. It resolves either one fixed express-side row from `0x00621e04`
|
|
|
|
|
|
or one active candidate or service id from `[subject+0x24]`, then formats one popup through the
|
|
|
|
|
|
shell message-box path. The neighboring refresh helper `shell_building_detail_refresh_flagged_service_capability_rows`
|
|
|
|
|
|
`0x004b9a20` is tighter now too: `0x7d07..0x7d1c` is not one anonymous block, but a real mask
|
|
|
|
|
|
partition over subject bits `0x20` and `0x10`, with one zero-mask pair, one bit-`0x20`-only
|
|
|
|
|
|
pair, one exclusive-or pair, and later one-bit and two-bit indicator rows. RT3.lng now closes
|
|
|
|
|
|
the fixed popup text family too: the single-line branch uses
|
|
|
|
|
|
`3922` `%1\nLoads available at %2: %3`, while the richer ordinary-candidate branch uses
|
|
|
|
|
|
`2981` `%1\nLoads available at %2: %3 Current Price: %4` and can append
|
|
|
|
|
|
`2982` `Price at next station, %1: %2 (%3%4)`. The selector band is narrower too: current
|
|
|
|
|
|
refresh-side evidence shows `0x7f58..0x801f` as one selected-ordinal highlight family over the
|
|
|
|
|
|
three fixed express rows plus the ordinary active-candidate rows, not as a generic unstructured
|
|
|
|
|
|
list. The neighboring `0x8020..0x8051` band is tighter too: it is primarily the matching
|
|
|
|
|
|
remove-entry family for the selected subject's live id list, with two special row indices
|
|
|
|
|
|
re-routing into the `0x7d0f/0x7d10` subflag-clearing path instead of ordinary list compaction
|
|
|
|
|
|
when the current `0x20/0x10` service-bit combination demands it. That means the exact captions
|
|
|
|
|
|
are no longer broadly open across the whole selector family: the fixed express-side rows, the
|
|
|
|
|
|
mask partitions, and the add/remove structure are grounded, and the remaining caption gap is
|
|
|
|
|
|
mostly the ordinary candidate rows further down the same band. The top-level toggles are
|
|
|
|
|
|
tighter now too: the paired
|
|
|
|
|
|
`0x7d02/0x7d03` controls are the real mode switch over subject bit `0x40`, choosing between the
|
|
|
|
|
|
bounded pair-value branch and the current-selection/status branch around `0x7d0d/0x7d0e`, while
|
|
|
|
|
|
the smaller `0x7d0f/0x7d10` controls flip the same special-service subflags later rendered as
|
|
|
|
|
|
`Caboose` and `Dining Car`. One adjacent control is tighter now too: the extracted
|
|
|
|
|
|
`BuildingDetail.win` resource contains the plain-English line `Set the initial cargo amount for
|
|
|
|
|
|
'Disable Cargo Economy' scenarios.`, and the `0x7d01` plus `0x7d09/0x7d0a` message-side branch
|
|
|
|
|
|
can mirror the current subject or selection through
|
|
|
|
|
|
`shell_building_detail_submit_aux_owner_subject_sync_request` `0x004b9e10` into the auxiliary
|
|
|
|
|
|
owner queue at `[0x006cd8d8+0x8f48]`, with side-owner presence now explicitly grounded through
|
|
|
|
|
|
`shell_has_auxiliary_preview_owner` `0x00434050`. That queued request is tighter too: both
|
|
|
|
|
|
current callsites forward the same side-list mirror latch at `[0x006cfe08+0x0c]`, so the
|
|
|
|
|
|
auxiliary owner now clearly sees not just the staged subject but also whether the local
|
|
|
|
|
|
`BuildingDetail` side-list is in mirrored-subject mode. The same `0x7d01` lane also now has one
|
|
|
|
|
|
clear rejection note: localized id `3587` `This option is only available by following the
|
|
|
|
|
|
tutorial.` now sits behind the active tutorial flag at `0x006d3b4c` and the cached previous
|
|
|
|
|
|
tutorial expected-control id at `0x00622b38`, while the neighboring tutorial helper
|
|
|
|
|
|
`tutorial_advance_step_and_refresh_expected_control_ids` `0x00516be0` now grounds
|
|
|
|
|
|
`0x00622b34/0x00622b38/0x00622b3c` as the current and previous expected-control cache rather
|
|
|
|
|
|
than anonymous globals. The last field is tighter now too: `0x00622b3c` is currently best read
|
|
|
|
|
|
as the active tutorial step's alternate-accepted companion control id, because the generic shell
|
|
|
|
|
|
control path compares clicked control ids against it directly and suppresses the `3587` tutorial
|
|
|
|
|
|
rejection when they match. That does not fully close the player-facing caption bind for every
|
|
|
|
|
|
control, but it does bound the neighboring side-owner sync and tutorial-rejection lane instead
|
|
|
|
|
|
of leaving it as anonymous glue.
|
|
|
|
|
|
The extra pre-insert gate is narrower than it
|
|
|
|
|
|
first looked. It is now grounded as tutorial-only:
|
|
|
|
|
|
`tutorial_validate_train_route_station_indicator_step` `0x00516d00` checks the current tutorial
|
|
|
|
|
|
step from the shell tutorial descriptor table at `0x00622b48` before a live station-or-transit
|
|
|
|
|
|
site id can be committed into the staged route entry, and the currently accepted steps align with
|
|
|
|
|
|
localized prompts `3777` and `3778`, the two train-routing tutorial instructions that tell the
|
|
|
|
|
|
player to click the Milan and Turin station indicators. Outside that tutorial state family, the
|
|
|
|
|
|
route-entry insertion path is not gated there. The adjacent validator is tighter now too:
|
|
|
|
|
|
`train_route_list_validate_reachability_and_station_pair` `0x004b2c10` walks that same route list,
|
|
|
|
|
|
resolves placed-structure-backed entries through the live placed-structure and route-node
|
|
|
|
|
|
collections, uses the direct route-node payload branch for the remaining entry family, and fails
|
|
|
|
|
|
with `3089..3091` when the resulting route cannot be traversed or does not preserve a valid
|
|
|
|
|
|
terminal station pair. The post-validation follow-on is bounded too:
|
|
|
|
|
|
`train_current_route_context_uses_strict_reachability_mode` `0x004a9460` is now the small shared
|
|
|
|
|
|
gate above the stricter branch, keyed off the current linked route object's downstream class type,
|
|
|
|
|
|
and `train_set_route_operating_mode_and_scalar` `0x004ab980` now reads as the shared train mode
|
|
|
|
|
|
setter beneath route editing, with the local and multiplayer insertion paths choosing mode
|
|
|
|
|
|
`0x13` only when that stricter second validation succeeds and mode `0x0a` on the looser fallback
|
|
|
|
|
|
path. 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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
player-facing stats bucket is `City Support`. The normalized region band is tighter too:
|
|
|
|
|
|
`world_region_normalize_cached_structure_balance_scalars` `0x00422320` no longer just writes an
|
|
|
|
|
|
anonymous cached preview band at `[region+0x2e2/+0x2e6/+0x2ea/+0x2ee]`. Current growth-report
|
|
|
|
|
|
evidence now grounds `[region+0x2e2]` as the weighted-profit-margin scalar and `[region+0x2ee]`
|
|
|
|
|
|
as the annual-density-adjust scalar later formatted as a percent in `Stats - City/Region`, with
|
|
|
|
|
|
`[region+0x2e6/+0x2ea]` left as the intermediate normalized-delta and clamped companion slots
|
|
|
|
|
|
beneath that final adjust term. 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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 01:11:06 -07:00
|
|
|
|
live-company presentation path through the company-list window. The later interior order of that
|
|
|
|
|
|
same `319` lane is tighter now too: after the route-entry collection refresh on `0x006cfca8` it
|
|
|
|
|
|
refreshes the auxiliary route-entry tracker collection `0x006cfcb4`, then runs
|
|
|
|
|
|
`placed_structure_collection_refresh_local_runtime_records_and_position_scalars` `0x004133b0`,
|
2026-04-08 16:31:33 -07:00
|
|
|
|
then a flagged world-grid cleanup sweep through the compact grid-flag query
|
|
|
|
|
|
`0x00448af0` plus the neighboring local chunk-cell write helper `0x00533fe0`, and only after
|
|
|
|
|
|
that the later route-entry post-pass at `0x00491c20`. The same `319` lane is tighter internally
|
|
|
|
|
|
now too:
|
|
|
|
|
|
before that later world and shell reactivation tail, `world_entry_transition_and_runtime_bringup`
|
|
|
|
|
|
runs one distinct post-bundle status and runtime refresh phase that posts progress ids `0x196`
|
|
|
|
|
|
and `0x197` through `0x005193f0/0x00540120` with paired `0x004834e0` follow-ons, refreshes the
|
|
|
|
|
|
live event collection at `0x0062be18` through
|
|
|
|
|
|
`scenario_event_collection_refresh_runtime_records_from_packed_state` `0x00433130`, rebuilds the
|
|
|
|
|
|
scenario-side port-or-warehouse cargo recipe runtime tables through `0x00435630`, and then runs
|
|
|
|
|
|
the named-candidate availability preseed through `0x00437743`. One later subphase is tighter now
|
|
|
|
|
|
too: before the broad world-reactivation sweep it posts progress ids `0x32dc/0x3714/0x3715`,
|
|
|
|
|
|
reloads one `0x108`-byte packed profile block through `0x00531150`, conditionally copies staged
|
|
|
|
|
|
runtime-profile bytes back into `0x006cec7c` while latch `[profile+0x97]` is set, mirrors the
|
|
|
|
|
|
grounded campaign-scenario bit `[profile+0xc5]` and sandbox bit `[profile+0x82]` into world
|
|
|
|
|
|
bytes `[world+0x66de]` and `[world+0x66f2]`, restores the selected year/profile lane through
|
|
|
|
|
|
`[profile+0x77]` into `[world+0x05/+0x09/+0x15]` through
|
|
|
|
|
|
`world_set_selected_year_and_refresh_calendar_presentation_state` `0x00409e80`; that restore now
|
2026-04-10 01:22:47 -07:00
|
|
|
|
also has one concrete file-side correlation in the classic `.gms` family: local save inspection
|
|
|
|
|
|
now consistently finds `0x32dc` at `0x76e8`, `0x3714` at `0x76ec`, and `0x3715` at `0x77f8` in
|
|
|
|
|
|
`Autosave.gms`, `kk.gms`, and `hh.gms`, leaving one exact `0x108`-byte span from `0x76f0` to
|
|
|
|
|
|
`0x77f8` between `0x3714` and `0x3715`. That span already carries staged-profile-looking payload
|
|
|
|
|
|
text such as `British Isles.gmp`, so the current static-file evidence now supports the atlas-side
|
|
|
|
|
|
`0x108` packed-profile note for the classic save family even though the exact field layout inside
|
|
|
|
|
|
that block is still unresolved. The same classic corpus is tighter now too: inside that
|
|
|
|
|
|
`0x108` span the map-path C string begins at relative offset `0x13`, the display-name C string
|
|
|
|
|
|
begins at `0x46`, the block is otherwise almost entirely zeroed, and the three local samples are
|
|
|
|
|
|
byte-identical except for the leading dword at `+0x00` (`3` in `Autosave.gms` and `hh.gms`,
|
|
|
|
|
|
`5` in `kk.gms`). The currently atlas-tracked bytes `[profile+0x77]`, `[profile+0x82]`,
|
|
|
|
|
|
`[profile+0x97]`, and `[profile+0xc5]` are all `0` in that classic sample set, so the current
|
|
|
|
|
|
file-side evidence grounds the block boundaries and the embedded strings but does not yet show
|
|
|
|
|
|
live examples of those branch-driving latches being set. One 1.05-era file-side analogue is now
|
|
|
|
|
|
visible too, but only as an inference from repeated save structure rather than a disassembly-side
|
|
|
|
|
|
field map: local `.gms` files in `rt3_105/Saved Games` carry one compact string-bearing block at
|
|
|
|
|
|
`0x73c0` with the same broad shape as the classic profile slab, including a leading dword at
|
|
|
|
|
|
`+0x00`, one map-path string at `+0x10`, one display-name string at `+0x43`, and a small
|
|
|
|
|
|
nonzero tail around `+0x76..+0x88`. In that 1.05 corpus the analogue bytes at relative `+0x77`
|
|
|
|
|
|
and `+0x82` are now nonzero in every checked sample (`Autosave.gms`/`nom.gms` show `0x07` and
|
|
|
|
|
|
`0x4d`; `p.gms`/`q.gms` show `0x07` and `0x90`; `g.gms` shows `0x07` and `0xa3`), while
|
|
|
|
|
|
relative `+0x97` and `+0xc5` remain `0`. The compared 1.05 save set is tighter now too:
|
|
|
|
|
|
`Autosave.gms` and `nom.gms` cluster together on `Alternate USA.gmp` with `+0x82 = 0x4d`,
|
|
|
|
|
|
`g.gms` carries `Spanish Mainline.gmp` with `+0x82 = 0xa3`, and `p.gms`/`q.gms` cluster on
|
|
|
|
|
|
`Southern Pacific.gmp` with `+0x82 = 0x90`; across all five files the same inferred analogue
|
|
|
|
|
|
lane at `+0x77` stays fixed at `0x07`, while the same map- or scenario-sensitive tail word at
|
|
|
|
|
|
`+0x80` tracks those `0x4d/0xa3/0x90` byte lanes (`0x364d0000`, `0x29a30000`, `0x1b900000`).
|
|
|
|
|
|
The leading dword at `+0x00` also splits the same corpus, with `Autosave.gms` alone at `3` and
|
|
|
|
|
|
the other four checked 1.05 saves at `5`. That is enough to say the wider save corpus does
|
|
|
|
|
|
contain nonzero candidates for two of the atlas-tracked profile lanes, and that one of them
|
|
|
|
|
|
varies coherently with the loaded scenario family, but not yet enough to claim that the 1.05
|
|
|
|
|
|
block reuses the exact same semantic field assignments as the classic one. The loader-side
|
|
|
|
|
|
family split is tighter now too: `p.gms` and `q.gms` no longer live under a generic fallback;
|
|
|
|
|
|
their save headers now classify as one explicit `rt3-105-scenario-save` branch with preamble
|
|
|
|
|
|
words `0x00040001/0x00018000/0x00000746` and the early secondary window
|
|
|
|
|
|
`0x00130000/0x86a00100/0x21000001/0xa0000100`, while `g.gms` now classifies as a second
|
|
|
|
|
|
explicit `rt3-105-alt-save` branch with the different preamble lane
|
|
|
|
|
|
`0x0001c001/.../0x00000754` and early window
|
|
|
|
|
|
`0x00010000/0x49f00100/0x00000002/0xa0000000`. That branch now carries the same bootstrap,
|
|
|
|
|
|
anchor-cycle, named 1.05 trailer, and narrow profile-block extraction path as the other 1.05
|
|
|
|
|
|
saves. The bridge just below that trailer is now explicit too: the common 1.05 save branch
|
|
|
|
|
|
carries selector/descriptor `0x7110 -> 0x7801` in `Autosave.gms` and `0x7110 -> 0x7401` in
|
|
|
|
|
|
`nom.gms`, and both still reach the same first later candidate at
|
|
|
|
|
|
`span_target + 0x189c`, well before the packed profile at `span_target + 0x3d48`; the
|
|
|
|
|
|
`rt3-105-alt-save` branch instead carries `0x54cd -> 0x5901` and its first later candidate
|
|
|
|
|
|
lands at `packed_profile + 0x104`, essentially on the profile tail; the scenario-save branch
|
|
|
|
|
|
still diverges locally with `0x0001 -> 0x0186` and never enters that later `0x32c8`-spanned
|
|
|
|
|
|
bridge at all. The common-branch bridge payload is narrower now too: both checked base saves
|
|
|
|
|
|
expose the same 0x20-byte primary block at `0x4f14` followed by the same denser secondary block
|
|
|
|
|
|
at `0x671c`, `0x1808` bytes later, and that secondary block now appears to run intact up to the
|
|
|
|
|
|
packed-profile start at `0x73c0` for a total observed span of `0xca4` bytes. The trailing slice
|
|
|
|
|
|
of that secondary block is now typed one level further: a small header at `secondary+0x354`
|
|
|
|
|
|
carries the observed stride `0x22`, capacity `0x44`, and count `0x43`, followed by a fixed-width
|
|
|
|
|
|
67-entry name table starting at `secondary+0x3b5` and running through names like
|
|
|
|
|
|
`AluminumMill`, `AutoPlant`, `Bakery`, `Port00..11`, and `Warehouse00..11`, with a short footer
|
|
|
|
|
|
(`dc3200001437000000`) after the last entry. The trailing per-entry word is now surfaced too:
|
|
|
|
|
|
most entries carry `0x00000001`, while the currently observed zero-trailer subset is
|
|
|
|
|
|
`Nuclear Power Plant`, `Recycling Plant`, and `Uranium Mine`. That footer is tighter now too:
|
|
|
|
|
|
it parses directly as `0x32dc`, `0x3714`, and one trailing zero byte, so the shared
|
|
|
|
|
|
map/save catalog currently ends on the same two grounded late-rehydrate progress ids that the
|
|
|
|
|
|
classic staged-profile band already exposed. The strongest structural read is therefore that the
|
|
|
|
|
|
entire `0x6a70..0x73c0` catalog region is shared verbatim between `Alternate USA.gmp` and the
|
|
|
|
|
|
derived `Autosave.gms`, not rebuilt independently during save. Combined with the earlier
|
|
|
|
|
|
grounded record-layout work under `0x00437743`, `0x00434ea0`, and `0x00434f20`, the current
|
|
|
|
|
|
safest semantic read is that this shared catalog is the bundled source form of the scenario-side
|
|
|
|
|
|
named candidate-availability table later mirrored into `[state+0x66b2]`, with each entry's
|
|
|
|
|
|
trailing dword now reading as the same availability override bit later copied into
|
|
|
|
|
|
`[candidate+0x7ac]`. The loader-side coverage is tighter now too: the same table parser now
|
|
|
|
|
|
attaches both to the common-save bridge payload and directly to the fixed source range in
|
|
|
|
|
|
`.gmp` files and the non-common `rt3-105-scenario-save` / `rt3-105-alt-save` branches. That
|
|
|
|
|
|
makes the scenario variation explicit instead of anecdotal. `Alternate USA` keeps only three
|
|
|
|
|
|
zero-availability names in this table (`Nuclear Power Plant`, `Recycling Plant`, `Uranium
|
|
|
|
|
|
Mine`), `Southern Pacific` widens the zero set to twelve (`AutoPlant`, `Chemical Plant`,
|
|
|
|
|
|
`Electric Plant`, `Farm Rubber`, `FarmRice`, `FarmSugar`, `Nuclear Power Plant`, `Plastics
|
|
|
|
|
|
Factory`, `Recycling Plant`, `Tire Factory`, `Toy Factory`, `Uranium Mine`), and `Spanish
|
|
|
|
|
|
Mainline` widens it again to forty-two, including `Bauxite Mine`, `Logging Camp`, `Oil Well`,
|
|
|
|
|
|
`Port00`, and the `Warehouse00..11` run while also flipping `Recycling Plant` back to
|
|
|
|
|
|
available. The header lanes just ahead of the table vary coherently with those scenario
|
|
|
|
|
|
branches too: `Alternate USA` carries `header_word_0 = 0x10000000`, `Southern Pacific`
|
|
|
|
|
|
carries `0x00000000`, and `Spanish Mainline` carries `0xcdcdcdcd`, while the structural
|
|
|
|
|
|
fields from `header_word_2` onward remain stable (`0x332e`, `0x1`, `0x22`, `0x44`, `0x43`)
|
|
|
|
|
|
and the 9-byte footer still decodes as `0x32dc`, `0x3714`, `0x00` in all three checked maps.
|
|
|
|
|
|
A wider corpus scan over the visible `.gmp`/`.gms` files makes those two anonymous header
|
|
|
|
|
|
lanes less mysterious too: the parser currently sees only three stable
|
|
|
|
|
|
`(header_word_0, header_word_1)` pairs across 79 files with this table shape, namely
|
|
|
|
|
|
`(0x00000000, 0x00000000)`, `(0x10000000, 0x00009000)`, and
|
|
|
|
|
|
`(0xcdcdcdcd, 0xcdcdcdcd)`. The zero-availability count varies widely underneath the first and
|
|
|
|
|
|
third pairs (`0..56` under the zero pair, `14..67` under the `0xcdcdcdcd` pair), so those two
|
|
|
|
|
|
lanes no longer look like counts or direct availability payload; the safest current read is
|
|
|
|
|
|
that they are coarse scenario-family or source-template markers above the stable
|
|
|
|
|
|
`0x332e/0x22/0x44/0x43` table header, with `0xcdcdcdcd` still plausibly acting as one reused
|
|
|
|
|
|
filler or sentinel lane rather than a meaningful numeric threshold. Current exported
|
|
|
|
|
|
disassembly notes still do not ground one direct loader-side or editor-side consumer of
|
|
|
|
|
|
`header_word_0` or `header_word_1` themselves, so that family-marker read remains an
|
|
|
|
|
|
inference from corpus structure rather than a named field assignment.
|
|
|
|
|
|
The new loader-side compare command makes the save-copy claim sharper too: for the checked
|
|
|
|
|
|
pairs `Alternate USA.gmp -> Autosave.gms`, `Southern Pacific.gmp -> p.gms`, and
|
|
|
|
|
|
`Spanish Mainline.gmp -> g.gms`, the parsed candidate-availability table contents now match
|
|
|
|
|
|
exactly entry-for-entry, with the only reported differences being the outer container family
|
|
|
|
|
|
(`map` vs `save`) and source-kind path (`map-fixed-catalog-range` vs the save-side branch).
|
2026-04-08 16:31:33 -07:00
|
|
|
|
has the explicit companion `world_refresh_selected_year_bucket_scalar_band` `0x00433bd0`, which
|
|
|
|
|
|
rebuilds the dependent selected-year bucket floats after the packed year changes; and then
|
|
|
|
|
|
rehydrates the named locomotive availability collection at `[world+0x66b6]` through
|
|
|
|
|
|
`locomotive_collection_refresh_runtime_availability_overrides_and_usage_state` `0x00461e00`.
|
|
|
|
|
|
That locomotive-side restore is tighter now too: its tail explicitly re-enters
|
|
|
|
|
|
`scenario_state_refresh_cached_available_locomotive_rating` `0x00436af0`, which rebuilds one
|
|
|
|
|
|
cached available-locomotive rating at `[state+0x4cbe]` from the current year plus the strongest
|
|
|
|
|
|
surviving available locomotive-side rating scalar `[loco+0x20]`, and the tiny query sibling
|
|
|
|
|
|
`0x00434080` is now bounded as the shell-side clamped read helper over that same cached field,
|
|
|
|
|
|
with the grounded shell-side reader later bucketing that value against `40/50/70/85/100`. The
|
|
|
|
|
|
same rehydrate band also refreshes the live structure-candidate filter and year-visible counts
|
|
|
|
|
|
through `structure_candidate_collection_refresh_filter_and_year_visible_counts` `0x0041e970`,
|
|
|
|
|
|
rebuilding the paired per-slot bands at `[candidates+0x246]` and `[candidates+0x16e]` and the
|
|
|
|
|
|
aggregate counts at `[candidates+0x31a]` and `[candidates+0x242]`; the same late checkpoint also
|
|
|
|
|
|
re-enters `placed_structure_collection_seed_candidate_subtype2_runtime_latch` `0x00434d40`,
|
|
|
|
|
|
which seeds runtime dword `[candidate+0x7b0]` across subtype-`2` candidate records before the
|
|
|
|
|
|
later world-wide reactivation sweep. That checkpoint also now has an explicit shell-facing scalar
|
|
|
|
|
|
publisher: `world_publish_shell_controller_progress_scalar_from_year_thresholds_or_selector_overrides`
|
|
|
|
|
|
`0x004354a0` writes one clamped `0..255` value into the current shell presentation object,
|
|
|
|
|
|
sourcing it either from the shell selector override pairs or from the scenario-side year-threshold
|
|
|
|
|
|
band rooted at `[state+0x3a/+0x51/+0x55/+0x59/+0x5d/+0x61]`; and just ahead of the later
|
|
|
|
|
|
scenario-side recipe rebuild, the same band also re-enters
|
|
|
|
|
|
`scenario_state_ensure_derived_year_threshold_band` `0x00435603`, which only falls into its
|
|
|
|
|
|
heavier rebuild body while `[state+0x3a] < 2` and otherwise leaves the derived year-threshold
|
|
|
|
|
|
companion slots `[state+0x51/+0x55/+0x59/+0x5d/+0x61]` unchanged. The neighboring late status
|
|
|
|
|
|
checkpoints around progress ids `0x196` and `0x197` also share one explicit stage gate now:
|
|
|
|
|
|
`world_query_global_stage_counter_reached_late_reactivation_threshold` `0x00444dc5` compares the
|
|
|
|
|
|
global counter `0x00620e94` against threshold `0x9901`, and the two current callers use a
|
|
|
|
|
|
negative result to clear `[world+0x39]` before the broader world and shell reactivation sweep.
|
|
|
|
|
|
The later reactivation tail is tighter now too: it includes the region-center world-grid flag
|
|
|
|
|
|
reseed pass
|
|
|
|
|
|
`0x0044c4b0`, which clears bit `0x10` across the live grid and then marks one representative
|
|
|
|
|
|
center cell for each class-`0` region through `0x00455f60`; its immediate sibling `0x0044c450`
|
|
|
|
|
|
then reruns `placed_structure_rebuild_candidate_cargo_service_bitsets` `0x0042c690` across every
|
|
|
|
|
|
live grid cell, and the next helper `0x0044ce60` scans the secondary raster at `[world+0x2135]`
|
|
|
|
|
|
for cells with any bits in mask `0x3e`, caching min/max bounds plus a marked-cell count in
|
|
|
|
|
|
`[world+0x21c6..+0x21d6]`; the larger sibling `0x0044c670` then consumes those cached bounds to
|
|
|
|
|
|
normalize the same raster and rebuild one dependent overlay/cache surface before the later
|
|
|
|
|
|
route-style rebuild, shell-window, and briefing branches. That overlay side is tighter now too:
|
|
|
|
|
|
after `0x0044c670` resolves scaled surface dimensions through `0x00534c50`, it walks one local
|
|
|
|
|
|
`3 x 32` sample lattice through the static offset tables at `0x00624b28/0x00624b48`, keeps only
|
|
|
|
|
|
secondary-raster classes `4..0x0d`, folds several interpolated `0x0051db80` samples into one
|
|
|
|
|
|
strongest local score, writes packed overlay pixels into the staged surface buffer, and only then
|
|
|
|
|
|
publishes that staged overlay through `0x00534af0`. The lower helper layer under that overlay
|
|
|
|
|
|
pass is tighter now too: `0x00534e10` is the reusable secondary-raster class-set
|
|
|
|
|
|
predicate for classes `1/3/4/5`, `0x00534e50` is the smaller neighboring class-subset predicate
|
|
|
|
|
|
for `1/4`, `0x00534ec0` covers `2/4/5`, `0x00534f00` covers `3/5`, `0x00534e90` is the
|
|
|
|
|
|
marked-bit query over the same 3-byte cell family, and
|
|
|
|
|
|
`0x00533e70` and `0x00534160` are the coarser siblings over the overlay table at `[world+0x1685]`:
|
|
|
|
|
|
the first clears coarse chunk objects across one clamped rectangle, while the second ensures one
|
|
|
|
|
|
chunk object and seeds local marks through its deeper stamp helper. One level up, the neighboring
|
|
|
|
|
|
rect owner `0x005374d0` now reads as the shared secondary-overlay refresh pass: it reruns the
|
|
|
|
|
|
local sample and unsigned-word reducers `0x00536230/0x00536420`, rebuilds the signed vector byte
|
|
|
|
|
|
planes through `0x00536710`, and then rebuilds the multiscale support surfaces through
|
|
|
|
|
|
`0x00533890`, whose inner reducers now explicitly target the packed sample-triplet buffer plus
|
|
|
|
|
|
the float and unsigned-word support planes rooted at the five-entry per-scale families
|
|
|
|
|
|
`[world+0x15f1..+0x1601]`, `[world+0x1605..+0x1615]`, and `[world+0x1619..+0x1629]`. The setup
|
|
|
|
|
|
side of that same family is tighter now too:
|
|
|
|
|
|
`0x005375c0` is the shared ensure-and-seed owner that allocates the sample, sidecar, mask,
|
|
|
|
|
|
raster, vector, and coarse-cell tables together; crucially, it seeds `[world+0x1655]` with byte
|
|
|
|
|
|
`0x02` and `[world+0x1659]` with byte `0x01`, which closes the default-fill split. The local
|
|
|
|
|
|
evidence now also supports a stronger negative conclusion: unlike `[world+0x1655]`, that second
|
|
|
|
|
|
mask plane is not part of the actively rebuilt runtime overlay path, and in the grounded local
|
|
|
|
|
|
corpus it behaves only as a separately seeded, cleared, and persisted sibling plane. One level
|
|
|
|
|
|
lower, the
|
|
|
|
|
|
base-plane allocator `0x00532c80` now reads more cleanly too: it is the narrower owner that
|
|
|
|
|
|
clears `[world+0x15e1]`, optionally applies the current grid dimensions, allocates the base
|
|
|
|
|
|
float-summary plane `[world+0x1605]`, the four sidecar byte planes `[world+0x1631..+0x163d]`,
|
|
|
|
|
|
both one-byte mask planes `[world+0x1655/+0x1659]`, and the packed secondary raster
|
|
|
|
|
|
`[world+0x165d]`, then seeds those planes with the same `0x02/0x01/0x00` default split. One
|
|
|
|
|
|
level higher again, the broader world-presentation reinitializer `0x00537e60` now sits above
|
|
|
|
|
|
that base allocator and the larger support-family ensure path `0x005375c0`: it stores the live
|
|
|
|
|
|
grid dimensions, hard-resets the whole overlay runtime family through `0x00532590`,
|
|
|
|
|
|
reinitializes the secondary-overlay family for those dimensions, and then republishes the
|
|
|
|
|
|
neighboring overlay constants and support owners used by both the world-side reattach branch and
|
|
|
|
|
|
the `.smp` restore-side presentation rebuild path, including several owners that all funnel
|
|
|
|
|
|
through the shared static-template slot allocator `0x00532ad0` over the local `0x100` pointer
|
|
|
|
|
|
band at `[world+0x08]`. Those neighboring owners are tighter now too: `0x00535070` is the small
|
|
|
|
|
|
primary overlay-surface-or-template setup owner, while `0x005356e0` and `0x00535890` seed two
|
|
|
|
|
|
larger static-template slot bands rooted at `[world+0x1568/+0x156c/+0x1574/+0x1578]` and
|
|
|
|
|
|
`[world+0x1560/+0x1564]` respectively; the remaining heavier sibling `0x00535430` now reads as a
|
|
|
|
|
|
shared four-slot overlay-surface rebuild owner that resamples one source or fallback descriptor
|
|
|
|
|
|
into a short local slot strip above `[world+0x155c]`. The tail of that same reinitializer is
|
|
|
|
|
|
tighter now too: after the larger support-family setup it seeds one seven-entry default overlay
|
|
|
|
|
|
companion set through `0x005373b0`, whose inner allocator `0x00535950` populates the local
|
|
|
|
|
|
`0x1b`-entry slot table from the static template rows `0x005dd300..0x005dd378`. The lifecycle
|
|
|
|
|
|
side is tighter in the same way now: `0x00536044` is the shared teardown owner that frees those same
|
|
|
|
|
|
three five-entry support families together with both mask planes, the packed secondary raster,
|
|
|
|
|
|
the vector-byte planes, the local staging buffer, and the neighboring sidecar or coarse-cell
|
|
|
|
|
|
tables. The remaining base-float lane is tighter too: the larger rebuild owner
|
|
|
|
|
|
`0x00538360` now clearly writes one base float-summary field into `[world+0x1605]`, clears both
|
|
|
|
|
|
one-byte mask planes, and then only repopulates the primary mask plane `[world+0x1655]` for the
|
|
|
|
|
|
qualifying class-`1` interior cells before re-entering `0x00532d90` to normalize that base
|
|
|
|
|
|
float-summary plane globally and `0x00532f60` to expand positive cells through one caller radius.
|
|
|
|
|
|
That asymmetry is now enough to close the local semantic edge: `[world+0x1655]` is the actively
|
|
|
|
|
|
rebuilt primary overlay mask, while `[world+0x1659]` is only the separately seeded and persisted
|
|
|
|
|
|
secondary mask sibling with no comparably grounded distinct read-side consumer. The only grounded
|
|
|
|
|
|
getter call to its root accessor `0x00533b60` is the shell staging branch at `0x00525bad`, and
|
|
|
|
|
|
that branch immediately discards the returned pointer. The bundle side is now explicit too:
|
|
|
|
|
|
`.smp` save-load treats the two mask planes as separate payloads with chunk ids `0x2cee` for
|
|
|
|
|
|
`[world+0x1655]` and `0x2d51` for `[world+0x1659]`, while the neighboring `0x2d49/0x2d50`
|
|
|
|
|
|
branches are the separate packed secondary-raster import lanes rather than alternate consumers
|
|
|
|
|
|
of the second mask plane. So, in the mapped local code, `0x1659` is best treated as a persisted
|
|
|
|
|
|
compatibility or seed-state sibling, not as a second actively consumed runtime overlay mask. The event
|
|
|
|
|
|
side is tighter too:
|
|
|
|
|
|
that `0x00433130` pass in turn materializes each live event record through
|
2026-04-06 01:11:06 -07:00
|
|
|
|
`scenario_event_refresh_runtime_record_from_packed_state` `0x0042db20`. Current shell-side xrefs
|
|
|
|
|
|
now tighten that event branch too: the first rebuilt linked row family under `0x0042db20` aligns
|
|
|
|
|
|
with the standalone condition list later queried by `EventConditions.win`, while the second
|
|
|
|
|
|
rebuilt family aligns with the four grouped effect lists later deep-copied through
|
|
|
|
|
|
`scenario_event_clone_runtime_record_deep_copy` `0x0042e050` during event duplication and effect
|
|
|
|
|
|
staging. The condition side is tighter now too: the tiny helper cluster
|
|
|
|
|
|
`0x0042df30/0x0042df70/0x0042dfb0/0x0042dff0` is no longer just "some adjacent list scans".
|
|
|
|
|
|
Current evidence bounds it as four predicates over the standalone `0x1e`-row condition list,
|
|
|
|
|
|
testing class bits `0x01`, `0x02`, `0x04`, or any of those bits in the static table
|
|
|
|
|
|
`0x005f3e04 + id*0x81`, with special fallback checks through event fields `[event+0x7f9]`,
|
|
|
|
|
|
`[event+0x7fa]`, and `[event+0x7f0] == 0x63`. The shell side is tighter too: vtable slot
|
|
|
|
|
|
`0x005d0cd8` now binds `shell_event_conditions_window_handle_message` `0x004d59e0`, and vtable
|
|
|
|
|
|
slot `0x005d0cf8` binds `shell_event_effects_window_handle_message` `0x004d7060`. The effects
|
|
|
|
|
|
side is tighter too: the lower helper trio is no longer anonymous. `0x004d5d00` now reads as the
|
|
|
|
|
|
effect-type selector refresh under control family `0x4fb2`, `0x004d5f50` reads as the selected
|
|
|
|
|
|
effect parameter-row repaint, `0x004d6090` is the heavier staged-effect editor refresh over the
|
|
|
|
|
|
`0x4fc7/0x4fce/0x4ff6/0x4ff9/0x4ffc/0x5041/0x5044/0x5046/0x5047` bands, and `0x004d67f0`
|
|
|
|
|
|
commits the current editor state back into the staged effect row at `[this+0x78]`. The verb side
|
|
|
|
|
|
is tighter now too: `shell_open_event_conditions_modal_and_return_result` `0x004d9dc0` and
|
|
|
|
|
|
`shell_open_event_effects_modal_and_return_result` `0x004d9e40` are the shared modal openers
|
|
|
|
|
|
above the two editor windows; `0x004da640`, `0x004da700`, and `0x004d9ed0` now read as the add,
|
|
|
|
|
|
edit, and remove verbs for standalone condition rows; `0x004da7c0`, `0x004da860`, and
|
|
|
|
|
|
`0x004da920` are the matching add, edit, and remove verbs for grouped effect rows; and
|
|
|
|
|
|
`0x004d8120` is now the heavier condition-row list panel refresh those condition-side verbs
|
|
|
|
|
|
re-enter after mutation. The conditions-side refresh split is tighter too: `0x0042d700`
|
|
|
|
|
|
aggregates standalone condition-list class or modifier flags, `0x0042d740` aggregates grouped
|
|
|
|
|
|
effect-row type flags for one selected grouped list, `0x004d9970` owns the condition-class
|
|
|
|
|
|
summary and grouped-row status bands, `0x004d77b0` owns the grouped summary-band affordance gate
|
|
|
|
|
|
for `0x4fed..0x4ff0` when selector `0x5000` lands on `0x5002`, `0x004d9d10` owns the smaller
|
|
|
|
|
|
grouped-effect territory-target affordance on control `0x500b`, `0x004d9f50` owns the selected-event mode
|
|
|
|
|
|
strip and summary text panels, and `0x004d9390` is the mode-dependent detail-row switch beneath
|
|
|
|
|
|
that strip. `0x004da0f0` is tighter too: selector `0x5001` now has the strongest current
|
|
|
|
|
|
RT3.lng fit as the condition-side `Test against...` mode above `0x004d9970`, while selector
|
|
|
|
|
|
`0x5002` has the strongest current fit as the grouped-effect-side `Apply effects...` mode.
|
|
|
|
|
|
That `0x5002` branch now clearly builds control `0x5014` from RT3.lng `1160..1164` as `to the
|
|
|
|
|
|
company/player/player (i.e. chairman)/territory for which the condition is TRUE` before
|
|
|
|
|
|
enabling the adjacent `0x5005`, `0x500a`, and `0x5014..0x501c` family. The strongest current
|
|
|
|
|
|
RT3.lng fit for the remaining visible target-scope strip is now `0x5015 = to the whole game`,
|
|
|
|
|
|
`0x5016..0x5018 = to all/human/AI companies`, `0x5019 + 0x500b = to territories`, and
|
|
|
|
|
|
`0x501a..0x501c = to all/human/AI players`; the grouped effect-row type mask matches that split
|
|
|
|
|
|
directly through bits `0x08`, `0x01`, `0x04`, and `0x02`. The
|
|
|
|
|
|
selected-event strip is tighter now too: `0x004db120` is the broader selected-event repaint and
|
|
|
|
|
|
navigation refresh above those smaller helpers, `0x004db520` and `0x004db5e0` are the previous
|
|
|
|
|
|
and next selected-event stepping verbs, `0x004db8b0` is the add-or-clone event modal helper,
|
|
|
|
|
|
`0x004dba90` is the rename verb, `0x004d9360` is the delete verb, `0x004db6a0` is the live
|
|
|
|
|
|
selected-event id setter behind control `0x4e84`, and `0x004db6f0` is the callback-binding plus
|
|
|
|
|
|
pending-selection bootstrap path that seeds the strip during window bring-up. The larger
|
|
|
|
|
|
dispatcher at `0x004dbb80` now makes the strip explicit: `0x4e85..0x4e8a` are previous, next,
|
|
|
|
|
|
add blank, clone selected, rename, and delete event, while the later grouped band commits current
|
|
|
|
|
|
summary state through `0x004d8d50` before changing grouped selector `[this+0x9c]` via `0x004dbf93`.
|
|
|
|
|
|
The selection bootstrap side is tighter too: `0x004daf40` is now the placeholder reset helper for
|
|
|
|
|
|
the selected-event summary controls `0x4eaf`, `0x4eac`, `0x4ed9`, `0x4edb`, and `0x4fdf..0x4fe2`.
|
|
|
|
|
|
The grouped target-scope side is tighter too: `0x004d8ea0` now reads as the commit helper for current
|
|
|
|
|
|
selected-event text panels before selection or grouped-action changes, `0x004d8d50` now records
|
|
|
|
|
|
the hidden selector family `0x5006..0x500e -> 0..8`, and `0x004dab60` projects that ordinal
|
|
|
|
|
|
one-to-one onto the visible grouped-effect target-scope display strip `0x5014..0x501c`. That
|
|
|
|
|
|
split is firmer now: `0x5006..0x500e` are the canonical hidden selectors that get stored into
|
|
|
|
|
|
`[event + group + 0x7fb]`, while `0x5014..0x501c` are the visible mirror rows republished from
|
|
|
|
|
|
that same ordinal rather than a second independently named selector family. The grouped row and
|
|
|
|
|
|
stored-summary refresh side is tighter too: `0x004d88f0` is now the selected grouped-effect
|
|
|
|
|
|
row-list renderer for `0x4ed5`, formatting the grouped `0x28`-byte rows through RT3.lng
|
|
|
|
|
|
`1154..1159`, and `0x004da9a0` is the current grouped-summary-state republisher that reloads
|
|
|
|
|
|
`0x500a`, `0x500b`, and visible action selection `0x5014..0x501c` from `[event + group + ...]`
|
|
|
|
|
|
before tailing back into `0x004da0f0`,
|
|
|
|
|
|
`0x004dbfca` as the grouped target-scope mode selector that persists the chosen control id into
|
|
|
|
|
|
`0x00622074`, with `0x5001/0x5002` now strongest-fit as `Test against...` and
|
|
|
|
|
|
`Apply effects...`, `0x004dbeeb` as the pending shared summary-text triplet publish helper for
|
|
|
|
|
|
`0x4eac/0x4ed9/0x4edb`,
|
|
|
|
|
|
`0x004d91e0` as the selected-event summary-header and grouped-mode commit helper above
|
|
|
|
|
|
`0x004d8d50`, and `0x004dbe7a` as the narrower `0x4ec6/0x4ec7` choice-event single-player-only
|
|
|
|
|
|
warning modal branch rooted at RT3.lng `3887`. The remaining gaps on
|
|
|
|
|
|
this lane are narrower again because the grouped-band `0x4dc09c` table now closes one earlier
|
|
|
|
|
|
overclaim: controls `0x5001/0x5002` are the only `0x4fed..0x501c` entries that route into
|
|
|
|
|
|
`0x004dbfca` on the `0xcf` side, while visible rows `0x5014..0x501c` only route to the smaller
|
|
|
|
|
|
`0x004d9d10` affordance path and the rest of `0x4ff1..0x5013` are default no-ops. The open
|
|
|
|
|
|
question is therefore no longer whether those visible target-scope rows are direct selector verbs;
|
|
|
|
|
|
current evidence says they are not.
|
|
|
|
|
|
The local slot records are rooted at
|
|
|
|
|
|
`[world+0x69d8]`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`[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
|
|
|
|
|
|
same owner now has one broader bounded read-side lane too, because control `0x9470` uses
|
|
|
|
|
|
`shell_company_detail_render_financial_history_panel` to draw the five-step Revenue or Expenses or
|
|
|
|
|
|
Interest or Profit or Lifetime strip, sibling control `0x9471` reuses
|
|
|
|
|
|
`shell_format_company_financial_summary_card` through
|
|
|
|
|
|
`shell_company_detail_render_company_summary_card`, controls `0x947d` and `0x947e` now ground a
|
|
|
|
|
|
bond maturity and repay panel through `shell_company_detail_render_bond_maturity_and_repay_panel`,
|
2026-04-06 20:34:16 -07:00
|
|
|
|
control `0x9488` now grounds the debt or credit or rate summary block through
|
|
|
|
|
|
`shell_company_detail_render_debt_credit_and_rate_summary_panel`, control `0x948a` now grounds the
|
|
|
|
|
|
share-value and dividend-payout block through
|
|
|
|
|
|
`shell_company_detail_render_share_value_and_dividend_summary_panel`, while the broader six-row
|
|
|
|
|
|
per-share stock-data family is now bounded under `shell_format_company_stock_data_panel`, and the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
adjacent territory selector lane is bounded through
|
|
|
|
|
|
`shell_company_detail_select_territory_access_row`,
|
|
|
|
|
|
`shell_company_detail_render_territory_access_row`,
|
|
|
|
|
|
`shell_company_detail_sync_selected_territory_from_picker`, and
|
|
|
|
|
|
`shell_company_detail_refresh_selected_territory_access_summary`; 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`.
|
2026-04-06 20:34:16 -07:00
|
|
|
|
The remaining company-side uncertainty is therefore narrower than before: the broader support and
|
|
|
|
|
|
valuation side is now tighter too because
|
|
|
|
|
|
`company_compute_cached_recent_per_share_performance_subscore`,
|
|
|
|
|
|
`company_compute_five_year_weighted_shareholder_return`, and
|
|
|
|
|
|
`company_compute_public_support_adjusted_share_price_scalar` bound the recent per-share
|
2026-04-08 16:31:33 -07:00
|
|
|
|
performance and investor-support/share-price blend beneath those vote resolvers; the recent
|
|
|
|
|
|
per-share feeder now has a grounded four-lane tail too, with current partial-year weight
|
|
|
|
|
|
`(5 * [world+0x0f]) - 5`, prior full-year weights `48/36/24/12` on `0x1f/0x1e`, dividend
|
|
|
|
|
|
non-decline pair weights `9/8/7/6` on `0x20`, lane weights `40/10/20/30`, the startup age ramp
|
|
|
|
|
|
`0/0/0/100 -> 25/25/35/100 -> 50/50/65/100 -> 75/75/85/100 -> 100/100/100/100`, a strongest-lane
|
|
|
|
|
|
`*1.25` boost, a weakest-lane `*0.8` reduction, and separate bounded-intermediate versus final
|
|
|
|
|
|
difficulty applications under `0x005f33b8`; the next consumer `0x00424fd0` is also tighter now,
|
|
|
|
|
|
with the young-company interpolation against `[company+0x57]`, caller pressure clamped to
|
|
|
|
|
|
`[-0.2, 0.2]`, one `(shares / 20000)^0.33` share-count growth term, and the later threshold
|
|
|
|
|
|
ladder `0.6 / 0.45 / 0.3 / 1.7 / 2.5 / 4.0 / 6.0` before the issue-`0x37` multiplier,
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`scenario_state_compute_issue_opinion_multiplier` now bounds the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-08 16:31:33 -07:00
|
|
|
|
creditor-liquidation lane surfaced by localized ids `300..304`, while the debt-side shell and
|
|
|
|
|
|
bond lane now separately close `0x38` as `Credit Rating` and `0x39` as `Prime Rate`. That means
|
|
|
|
|
|
the remaining gap is now mostly gone on the UI side too: issue `0x37` is already bounded to the
|
|
|
|
|
|
same investor-confidence family as the equity-support and governance-pressure paths, and current
|
|
|
|
|
|
grounded UI evidence still stops at the investor-attitude sentence family rather than one
|
|
|
|
|
|
standalone caption. The calendar side is tighter now too:
|
|
|
|
|
|
`[world+0x15]` is the absolute counter for the same mixed-radix `12 x 28 x 3 x 60`
|
|
|
|
|
|
year-plus-subfield tuple packed by `0x0051d3c0` and unpacked by `0x0051d460`, not just a vague
|
|
|
|
|
|
“calendar-like” blob. The `TrackLay.win` family now clearly owns `Lay single track.` `Lay double track.`
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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.
|
2026-04-04 21:21:59 -07:00
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- CompanyDetail, tighter section ownership: the shell detail family now has an explicit
|
|
|
|
|
|
section-selector lane in addition to the read-side panels already mapped. Controls
|
|
|
|
|
|
`0x9472..0x9475` directly select the four visible CompanyDetail sections through `0x006cfe60`,
|
|
|
|
|
|
`0x9476..0x9479` are the companion visual controls for that same tab strip, and section `0` is now
|
|
|
|
|
|
bounded more tightly as the chairman or governance slice around the portrait-backed chairman band
|
|
|
|
|
|
on `0x9480` plus the dynamic overview widget `0x947f`. That widget is no longer just a vague
|
|
|
|
|
|
status line: the section-0 refresh binds it through a dedicated stack-built dynamic text path,
|
|
|
|
|
|
and the strongest current shared formatter candidate is
|
|
|
|
|
|
`shell_format_company_governance_and_economy_status_panel` at `0x004e5cf0`, which first renders a
|
|
|
|
|
|
five-line company-metric preamble through localized ids `1211..1215`: `Revenues` from slot
|
|
|
|
|
|
`0x2c`, `Profits` from slot `0x2b`, `Load miles hauled` from slot `0x17`, `Revenue per load
|
|
|
|
|
|
mile` from the derived `slot 0x2c / slot 0x17` branch, and `Average speed` from slot `0x26`
|
|
|
|
|
|
rendered through `1216` `%1 m.p.h.`. It then splits the governance summary more concretely: no
|
|
|
|
|
|
linked chairman emits `3045`, wholly owned companies emit `3046` for the scenario-selected
|
|
|
|
|
|
chairman or `3047` for another linked chairman, and investor-owned companies emit the
|
|
|
|
|
|
investor-attitude lines `3048/3049` with one adjective from the table at `0x00622170`. That
|
|
|
|
|
|
branch is no longer using unnamed helpers either: `company_get_linked_chairman_profile_record`
|
|
|
|
|
|
`0x00426ef0` resolves the linked chairman profile and `chairman_profile_owns_all_company_shares`
|
|
|
|
|
|
`0x004768c0` is the full-ownership test behind the `3046/3047` split. The salary side is tighter
|
|
|
|
|
|
too: the formatter computes one signed delta from `[company+0x14f]` and `[company+0x0d59]`, then
|
|
|
|
|
|
chooses `3050..3052` for the scenario-selected chairman or `3053..3055` for another linked
|
|
|
|
|
|
chairman depending on whether that delta is negative, positive, or zero. The bonus line is
|
|
|
|
|
|
narrower still: it only appears when the display year matches `[company+0x34f]`, using amount
|
|
|
|
|
|
`[company+0x353]` with `3056` or `3057`. It then appends the
|
|
|
|
|
|
`1218` `Economy status - %1.` tail caption and stages the adjacent selected-company report or
|
|
|
|
|
|
list help-title pairs `1219/1220` `Income Statement`, `1221/1222` `Balance Sheet`,
|
|
|
|
|
|
`1223/1224` `Haulage Report`, `1225/1226` `Stock Report`, `1227/1228` `Train List`,
|
|
|
|
|
|
`1229/1230` `Station List`, `1231/1232` `Industry List`, and `1233/1234` `Cargo List`.
|
|
|
|
|
|
Current evidence still does not recover separate `CompanyDetail.win` action controls for that
|
|
|
|
|
|
strip under `shell_company_detail_window_handle_message`, so it currently reads as staged
|
|
|
|
|
|
overview text or help content rather than as a closed launcher family. The direct `0x947f`
|
|
|
|
|
|
formatter call is still indirect, but the widget boundary is tighter too: the generic shell
|
|
|
|
|
|
helpers `shell_control_refresh_matching_dynamic_text_payload` `0x00540a47` and
|
|
|
|
|
|
`shell_control_release_dynamic_text_payload` `0x005639d2` now show that type `0x6f` controls
|
|
|
|
|
|
free or swap one heap-backed text payload and then short-circuit as a special dynamic-text case,
|
|
|
|
|
|
which strengthens the reading of `0x947f` as a display-only overview widget rather than a normal
|
|
|
|
|
|
callback control. One adjacent boundary is tighter now too: the broader overview wrapper at
|
|
|
|
|
|
`shell_render_company_overview_panel_header_and_optional_change_affordance` `0x004e5a80` owns
|
|
|
|
|
|
the fallback no-company texts `1210`, `3043`, and `3888`, styles controls `0x3f06` and `0x3f07`,
|
|
|
|
|
|
and on the narrower selected-company branch appends `3044` `Click to change company name and
|
|
|
|
|
|
logo.` plus the neighboring `1941` `Change` affordance before falling through into the shared
|
|
|
|
|
|
`0x004e5cf0` text body. That keeps the name/logo affordance outside the ordinary
|
|
|
|
|
|
`CompanyDetail.win` action dispatcher and makes the `0x947f` alignment cleaner. The message-side
|
|
|
|
|
|
action band is tighter too: `0x94b5` grounds
|
|
|
|
|
|
territory-access purchase, `0x94b6` grounds bankruptcy, `0x94cf..0x94d2` ground bond issue, stock
|
|
|
|
|
|
issue, stock buyback, and dividend-rate changes, `0x9493` routes into the destructive
|
|
|
|
|
|
company-clear helper that deactivates the selected company and clears chairman/share links,
|
|
|
|
|
|
`0x94d6` grounds bankruptcy, `0x94d7..0x94da` ground bond issue, stock issue, stock buyback, and
|
|
|
|
|
|
dividend-rate changes, `0x94db` grounds merger, `0x94dc` grounds resignation, and `0x9538` grounds
|
|
|
|
|
|
chairmanship takeover. The finance-side dialog family is tighter too: the bond-issue lane now has
|
|
|
|
|
|
the dedicated modal renderer `shell_company_detail_render_issue_bond_offer_dialog` `0x004c3560`
|
|
|
|
|
|
for underwriter terms `968..972`, the stock-issue lane has
|
|
|
|
|
|
`shell_company_detail_render_issue_stock_offer_dialog` `0x004c3b50` for the staged offer lines
|
|
|
|
|
|
`975..978`, and the buyback lane has
|
|
|
|
|
|
`shell_company_detail_render_stock_buyback_offer_dialog` `0x004c4300` for broker lines
|
|
|
|
|
|
`981..984`. The compact summary card on sibling control `0x9471` is tighter too:
|
|
|
|
|
|
`shell_format_company_financial_summary_card` at `0x004bfb30` now clearly renders `Cash:`,
|
|
|
|
|
|
`Revenue:`, and `Profits:` from company slots `0x0d`, `0x2c`, and `0x2b`, rather than one looser
|
|
|
|
|
|
unnamed finance block. The dividend lane is now split the same way:
|
|
|
|
|
|
`shell_company_detail_setup_dividend_rate_adjust_controls` `0x004c4c70` binds the paired adjust
|
|
|
|
|
|
controls `0x99e8` and `0xc0f9`,
|
|
|
|
|
|
`shell_company_detail_render_change_dividend_rate_dialog` `0x004c4e30` renders localized ids
|
|
|
|
|
|
`988..990`, and
|
|
|
|
|
|
`shell_company_detail_handle_change_dividend_rate_dialog_message` `0x004c5140` clamps the staged
|
|
|
|
|
|
dividend rate against `company_compute_board_approved_dividend_rate_ceiling` `0x00426260` and
|
|
|
|
|
|
raises localized id `991` when the board refuses a higher dividend, all before the existing
|
|
|
|
|
|
company commit path. All four finance verbs now converge through the shared
|
|
|
|
|
|
callback-driven modal opener `shell_open_custom_modal_dialog_with_callbacks` `0x004c98a0`, which
|
|
|
|
|
|
is also reused by the multiplayer staged text-entry lane. The front controls in section `0` are
|
|
|
|
|
|
tighter too: `0x948b` is a
|
|
|
|
|
|
tutorial-guarded escape or back control that shows localized id `3724` `This option is disabled in
|
|
|
|
|
|
the tutorial.` before falling back to the shell detail-manager escape path, `0x9491` and `0x9492`
|
|
|
|
|
|
only restyle the paired visuals `0x948f` and `0x9490`, and `0x9494` opens localized id `3635`
|
|
|
|
|
|
`Enter the amount that your company's cash should be` and writes the accepted value directly into
|
|
|
|
|
|
the selected company cash pair. The neighboring debt section is tighter now too: controls
|
|
|
|
|
|
`0x947d` and `0x947e` no longer read as one-off bond widgets, but as the owners of two repayable
|
|
|
|
|
|
bond-slot row bands, `0x94e8..0x950f` and `0x9510..0x9537`. Those row controls now clearly
|
|
|
|
|
|
converge on the same repayment path in the message dispatcher: they reject unaffordable repayment
|
|
|
|
|
|
through localized id `2990`, open the early-repayment confirmation rooted at `2991`, and then
|
|
|
|
|
|
either commit through `company_repay_bond_slot_and_compact_debt_table` `0x00423d70` or package
|
|
|
|
|
|
the request through the multiplayer shell transport. The render-side owner is tighter too:
|
|
|
|
|
|
`shell_company_detail_render_bond_maturity_and_repay_panel` formats `Due %1` and `Repay this
|
|
|
|
|
|
bond.` for the selected debt slot while the tiny binder
|
|
|
|
|
|
`shell_company_detail_bind_bond_row_band_for_active_panel` switches between the two row bands.
|
|
|
|
|
|
Current `0xcb` dispatch does not treat `0x948f`, `0x9490`, `0x94d4`, or `0x94d5` as standalone
|
|
|
|
|
|
action cases. The message-side jump table now makes that passive/action split explicit too: in the
|
|
|
|
|
|
dispatch byte map rooted at `0x004c6640`, controls `0x94d6..0x94dc` map to cases `0x06..0x0c`,
|
|
|
|
|
|
control `0x9538` maps to case `0x0d`, and the neighboring companion rows `0x94d4` and `0x94d5`
|
|
|
|
|
|
stay on the default path. The refresh-side ownership is tighter too: the helper now explicitly
|
|
|
|
|
|
loops over `0x94d4..0x9537` as the selected-company-owned band and over `0x9538..0x959b` as the
|
|
|
|
|
|
linked-chairman-owned band, and those two loops do not use the same style polarity.
|
|
|
|
|
|
`0x94d4..0x9537` take style `0x65` when the selected company matches the scenario-selected
|
|
|
|
|
|
company and `0x87` when it differs, while `0x9538..0x959b` take style `0x87` when the selected
|
|
|
|
|
|
company's linked chairman matches the scenario-selected chairman and `0x65` otherwise. The
|
|
|
|
|
|
selected-company action side
|
|
|
|
|
|
is tighter now too: scenario toggle `0x006cec78+0x4a8f` re-enables bankruptcy `0x94d6` together
|
|
|
|
|
|
with passive companion row `0x94d4`, `+0x4a8b` re-enables issue bonds `0x94d7` together with
|
|
|
|
|
|
passive companion row `0x94d5`, `+0x4a87` re-enables stock issue and stock buyback
|
|
|
|
|
|
`0x94d8..0x94d9`, `+0x4a93` re-enables dividend `0x94da` together with the same passive
|
|
|
|
|
|
companion row `0x94d5`, `+0x4adb` re-enables merger `0x94db`, `+0x4acb` re-enables resignation
|
|
|
|
|
|
`0x94dc`, and `+0x4acf` re-enables chairmanship takeover `0x9538`. That makes `0x94d4/0x94d5`
|
|
|
|
|
|
read more like passive companion or heading widgets than hidden verbs. The constructor boundary
|
|
|
|
|
|
is tighter too: current `CompanyDetail.win` setup still only binds explicit callbacks for
|
|
|
|
|
|
`0x9470`, `0x9471`, `0x947d`, `0x947e`, and `0x948c..0x948e`, not for the wider section-0 row
|
|
|
|
|
|
bands. That keeps the remaining `0x94d4..0x959b` content looking more like resource-defined
|
|
|
|
|
|
display rows that are gated and restyled by refresh than like individually code-rendered widgets.
|
|
|
|
|
|
That leaves the main remaining CompanyDetail-specific shell edge at the exact `0x947f` formatter
|
|
|
|
|
|
binding plus the still-unsplit render-side governance rows inside `0x94d4..0x959b`.
|
|
|
|
|
|
- Adjacent `LoadScreen.win` report family: the neighboring shell lane around controls
|
|
|
|
|
|
`0x3ef6..0x4073` is now separated from `CompanyDetail` instead of being treated as one more
|
|
|
|
|
|
extension of the `0x947f` overview path. The real outer owner is
|
|
|
|
|
|
`shell_load_screen_window_construct` `0x004ea620`, which binds `LoadScreen.win`, randomizes the
|
|
|
|
|
|
`LoadScreen%d.imb` background family, stores the singleton at `0x006d10b0`, and seeds the first
|
|
|
|
|
|
visible page-strip controls. Above the older page-specific work, the real message owner is now
|
|
|
|
|
|
`shell_load_screen_window_handle_message` `0x004e3a80`: it owns page id `[this+0x78]`,
|
|
|
|
|
|
page-local substate `[this+0x7c]`, page-kind `[this+0x80]`, current company `[this+0x88]`,
|
|
|
|
|
|
current chairman profile `[this+0x8c]`, display year `[this+0x9c]`, and the page-local report
|
|
|
|
|
|
row latch `[this+0x118]`, then fans back into the shared selector
|
|
|
|
|
|
`shell_load_screen_select_page_subject_and_refresh` `0x004e2c10`, the company-step helper
|
|
|
|
|
|
`0x004e3a00`, and narrower page branches such as `0x004e45d0`. The matching render-side owner is
|
|
|
|
|
|
now bounded too: `shell_load_screen_render_active_page_panel` at `0x004ea060` formats the common
|
|
|
|
|
|
heading and panel frame, then switches on page id `[this+0x78]` and hands control down into the
|
|
|
|
|
|
active page body. That older branch is now demoted to what it actually is:
|
|
|
|
|
|
`shell_load_screen_profile_stock_holdings_page_handle_message`, the page-specific handler beneath
|
|
|
|
|
|
the stock-holdings slice. Inside that same family,
|
|
|
|
|
|
`shell_load_screen_render_profile_stock_holdings_summary_panel` at `0x004e5300` grounds the
|
|
|
|
|
|
selected-profile holdings page: it resolves the current chairman profile from `[this+0x8c]`,
|
|
|
|
|
|
renders the top summary rows `1204` `Stock Value:`, `1205` `Total Assets:`, and
|
|
|
|
|
|
`1206` `Stock Holdings:`, then walks the active company roster and formats one row per positive
|
|
|
|
|
|
holding through `1201` `Click to view details on %1.`, `1207` `%1 Shares`, and `1208` `%1 Value`,
|
|
|
|
|
|
falling back to `1209` `None` when no positive holdings survive. It also appends `3029`
|
|
|
|
|
|
`Click to change player name and portrait.` plus the adjacent `1941` `Change` affordance only
|
|
|
|
|
|
when the rendered profile matches the scenario-selected chairman. The earlier pages are tighter
|
|
|
|
|
|
now too: `0x004e68e0` is the selected-company financial ranking page using the active company
|
|
|
|
|
|
roster plus `1235..1245` for revenue, profit, cash, track mileage, and report affordances; and
|
|
|
|
|
|
`0x004e6ef0` is the active-chairman wealth ranking page using the chairman profile collection
|
|
|
|
|
|
plus `1237`, `1241`, `1246..1250` for cash, stock, total, and purchasing-power style comparisons.
|
|
|
|
|
|
The later sibling renderers are broader than that one holdings page now too: `0x004e7670` is the
|
|
|
|
|
|
selected-company train list page using `1235..1267`, `0x004e8270` is the selected-company
|
|
|
|
|
|
building list page using `1268..1278`, `0x004e8bb0` is the selected-company station list page
|
|
|
|
|
|
using `1279..1288`, and `0x004e9460` is the map-wide cargo list page using `1289..1298` over the
|
|
|
|
|
|
live candidate collection rather than one company roster. The last broad early-page owner is
|
|
|
|
|
|
tighter now too: `0x004e9b20` is the shared multi-year company report-table renderer for page
|
|
|
|
|
|
`4` `Income Statement`, page `5` `Balance Sheet`, and page `6` `Haulage Report`, all driven from
|
|
|
|
|
|
`0x004ea060` with one caller-supplied mode byte and yearly company rows built through
|
|
|
|
|
|
`company_read_year_or_control_transfer_metric_value`. The row families are bounded too:
|
|
|
|
|
|
income-statement rows `1301..1315`, balance-sheet rows `2816` and `1317..1322`, and
|
|
|
|
|
|
haulage-report rows `1323..1335`. The only special rows inside that family are now tighter too:
|
|
|
|
|
|
`0x00425880` and `0x004258c0` provide the negative-cash and positive-cash interest-rate inserts
|
|
|
|
|
|
for the `%1/%2` placeholders in strings `2815` and `2816`, so they are no longer anonymous
|
|
|
|
|
|
private math blobs. The adjacent early siblings are tighter now too: `0x004e5130` is the
|
|
|
|
|
|
selected-company `Stock Data` page wrapper that falls back through `1299` and otherwise reuses
|
|
|
|
|
|
`0x004c0160` to render the `Largest Shareholders`, `Shares`, and `Per Share Data` family;
|
|
|
|
|
|
`0x004e6ef0` is the `Player List` page; `0x004e5300` is the `Player Detail` holdings page; and
|
|
|
|
|
|
`0x004e51ea` is the `Game Status` briefing panel that pulls current scenario briefing text from
|
|
|
|
|
|
the live scenario text store and appends the `1772/1773` `Briefing` affordance. The active-page
|
|
|
|
|
|
renderer at `0x004ea060` is now tighter too because its 13-byte page descriptor table at
|
|
|
|
|
|
`0x006220a0` has been decoded directly as `{ page kind, title string id, `0x3ef8` backlink
|
|
|
|
|
|
page, selected-company-header flag }`. The currently grounded rendered title order is:
|
|
|
|
|
|
`XXX`, `COMPANY OVERVIEW`, `COMPANY LIST`, `INCOME STATEMENT`, `BALANCE SHEET`,
|
|
|
|
|
|
`HAULAGE REPORT`, `STOCK DATA`, `PLAYER LIST`, `PLAYER DETAIL`, `GAME STATUS`,
|
|
|
|
|
|
`TRAIN LIST`, `TRAIN DETAIL`, `STATION LIST`, `STATION DETAIL`, `CARGO LIST`,
|
|
|
|
|
|
and `INDUSTRY LIST`. Its live body bindings are now bounded too: page `0` falls back to
|
|
|
|
|
|
`1203` `Unable to display page`, page `1` is the company overview wrapper, page `2` is the
|
|
|
|
|
|
company list page, pages `3..5` are income statement, balance sheet, and haulage report, page
|
|
|
|
|
|
`6` is stock data, page `7` is player list, page `8` is player detail, page `9` is game status,
|
|
|
|
|
|
page `0x0a` is train list, page `0x0b` currently falls back to `1203`, page `0x0c` is station
|
|
|
|
|
|
list, page `0x0d` currently falls back to `1203`, page `0x0e` is cargo list, and page `0x0f`
|
|
|
|
|
|
is industry list. The row-click path is tighter now too: player-list rows re-enter page `8`
|
|
|
|
|
|
directly, but train, station, and industry rows leave `LoadScreen.win` through the shell
|
|
|
|
|
|
detail-panel manager at `0x004ddbd0` instead of switching to title-table pages `0x0b` or
|
|
|
|
|
|
`0x0d`. Page `0` is tighter now too: its descriptor is kind `0`, title `1200` `XXX`, backlink
|
|
|
|
|
|
`0`, and header flag `0`, and no current post-constructor selector path has been recovered for
|
|
|
|
|
|
it. The descriptor side now also bounds the only known reverse route for the dormant detail
|
|
|
|
|
|
titles: `0x3ef8` is the table-driven backlink affordance, so if page `0x0b` or `0x0d` were ever
|
|
|
|
|
|
selected, the known reverse path would return to train-list page `0x0a` or station-list page
|
|
|
|
|
|
`0x0c` respectively rather than through a separate detail-only owner.
|
|
|
|
|
|
The launcher side is tighter too: current grounded `shell_open_or_focus_load_screen_page`
|
|
|
|
|
|
callers cover pages `1`, `2`, `3`, `4`, `5`, `6`, `7`, `9`, `0x0a`, `0x0c`, `0x0e`, and `0x0f`,
|
|
|
|
|
|
and no current recovered opener or row-click route selects `0x0b` or `0x0d`. So the
|
|
|
|
|
|
`LoadScreen.win` family now has a much cleaner shape: one outer message owner
|
|
|
|
|
|
`0x004e3a80`, one active-page render owner `0x004ea060`, and then the narrower page-specific
|
|
|
|
|
|
handlers and renderers beneath them. The launcher side is tighter now too: `0x004e4ee0` is the
|
|
|
|
|
|
shared open-or-focus ledger-page owner above this family. Outside sandbox it either re-enters
|
|
|
|
|
|
`shell_load_screen_select_page_subject_and_refresh` on the live runtime at `0x006d10a8` or
|
|
|
|
|
|
allocates that transient runtime, seeds it through `0x004e4b10`, and enters the visible modal
|
|
|
|
|
|
loop; inside sandbox it raises localized id `3899` `The ledger is not available in sandbox mode.`
|
|
|
|
|
|
instead. That narrows the remaining `LoadScreen.win` gap again: `TRAIN DETAIL` and
|
|
|
|
|
|
`STATION DETAIL` now read as dormant title-table entries unless some still-unrecovered nonstandard
|
2026-04-10 01:22:47 -07:00
|
|
|
|
selector reaches them. The live auto-load boundary is tighter now too: hook-driven
|
|
|
|
|
|
`shell_transition_mode(4, 0)` now completes old-mode teardown, reconstructs and republishes
|
|
|
|
|
|
`LoadScreen.win`, and returns cleanly, but the later post-transition service ticks still keep
|
|
|
|
|
|
`[0x006cec78] = 0`, `[shell_state+0x0c]` on the same `LoadScreen.win` singleton, and
|
|
|
|
|
|
`[LoadScreen.win+0x78] = 0` through at least counts `2..8`. So the next runtime edge is no
|
|
|
|
|
|
longer the old mode-`4` teardown or publish band; it is the `LoadScreen.win` message owner
|
|
|
|
|
|
`0x004e3a80` itself, because later service alone is not promoting the plain load screen into the
|
|
|
|
|
|
separate startup-runtime object path. One later runtime probe did not actually reach that edge:
|
|
|
|
|
|
the `0x004e3a80` hook installed, but the run never produced any ready-count, staging,
|
|
|
|
|
|
transition, post-transition, or load-screen-message lines, only ordinary shell node-vcall
|
|
|
|
|
|
traffic. So that result is now treated as a gate-or-cadence miss rather than as evidence against
|
|
|
|
|
|
the `LoadScreen.win` message path itself. The newer shell-state service trace tightens it again:
|
|
|
|
|
|
on a successful staged run the later service ticks do execute in `mode_id = 4`, but the
|
|
|
|
|
|
`0x004e3a80` message hook still stays silent while the state remains frozen in the plain
|
|
|
|
|
|
`LoadScreen.win` shape. So the next runtime boundary is now the shell-runtime prime call
|
|
|
|
|
|
`0x00538b60` beneath `shell_state_service_active_mode_frame` `0x00482160`, not the message owner
|
|
|
|
|
|
alone. The first direct `0x00538b60` probe run is not trustworthy yet, though: it stopped
|
|
|
|
|
|
immediately after the first shell-state service-entry line, before any ready-count or
|
|
|
|
|
|
runtime-prime entry logs. So that result is currently treated as probe validation work, not as a
|
|
|
|
|
|
real boundary move inside RT3. The static service branch is conditional too: `0x00482160` only
|
|
|
|
|
|
enters `0x00538b60` when `[shell_state+0xa0] == 0`, so silence from the runtime-prime hook does
|
|
|
|
|
|
not yet prove the shell stops before that call unless the service-entry logs also show the `+0xa0`
|
|
|
|
|
|
gate open. The newer run now closes that condition: `[shell_state+0xa0]` is `0`, and the
|
|
|
|
|
|
`0x00538b60` runtime-prime hook enters and returns cleanly. The newer run closes the next owner
|
|
|
|
|
|
too: `0x00520620` `shell_service_frame_cycle` also enters and returns cleanly on the same frozen
|
|
|
|
|
|
mode-`4` path, and the logged fields match its generic branch rather than a startup-promotion
|
|
|
|
|
|
lane (`[+0x1c] = 0`, `[+0x28] = 0`, `flag_56 = 0`, `[+0x58]` pulsed then cleared, and
|
|
|
|
|
|
`0x006cec78` stayed `0`). So the next runtime boundary under the same shell-state service pass is
|
|
|
|
|
|
now one level deeper: the per-object service walker `0x0053fda0` beneath `0x00538b60`. The newer
|
|
|
|
|
|
run closes that owner too: it enters and returns cleanly while servicing the `LoadScreen.win`
|
|
|
|
|
|
object itself, with `field_1d = 1`, `field_5c = 1`, and a stable child list under
|
|
|
|
|
|
`[obj+0x70/+0x74]`, and its first child-service vcall target at slot `+0x18` stays
|
|
|
|
|
|
`0x005595d0`. Since `0x006cec78` still stays `0` through those clean object-service passes, the
|
|
|
|
|
|
next runtime boundary is now the child-service target `0x005595d0`, not the higher object
|
|
|
|
|
|
walker. The newer child-service run narrows that again: the first sixteen `0x005595d0` calls are
|
|
|
|
|
|
stable child lanes under the same `LoadScreen.win` parent, with `[child+0x86]` pointing back to
|
|
|
|
|
|
the load-screen object, `field_b0 = 0`, and a split where earlier children carry
|
|
|
|
|
|
`flag_68 = 0x03` and return `4` while later siblings carry `flag_68 = 0x00` and return `0`. The
|
|
|
|
|
|
static body matches that read too: `0x005595d0` is gated by `0x00558670` and then spends most of
|
|
|
|
|
|
its work in draw or overlay helpers `0x54f710`, `0x54f9f0`, `0x54fdd0`, `0x53de00`, and
|
|
|
|
|
|
`0x552560`, so it now reads as another presentation-side service lane rather than the missing
|
|
|
|
|
|
startup-runtime promotion. The widened allocator-window trace then reconciled the runtime with
|
|
|
|
|
|
the static mode-`4` branch one step further: the first transition-window allocation is `0x7c`,
|
|
|
|
|
|
which matches the static pre-construct `0x48302a -> 0x53b070` alloc exactly, and the later
|
|
|
|
|
|
`0x111/0x84/0x3a/0x25...` allocations all occur before `LoadScreen.win` construct returns, so
|
|
|
|
|
|
they now read as constructor-side child or control setup. That means the allocator probe did not
|
|
|
|
|
|
disprove the still-silent startup-runtime slice; it simply exhausted its log budget inside the
|
|
|
|
|
|
constructor before the post-construct block. The later reset-at-return run is now the decisive
|
|
|
|
|
|
one: after `LoadScreen.win` construct returns there are still no further allocator hits before
|
|
|
|
|
|
publish and transition return, which matches the corrected jump-table decode because mode `4`
|
|
|
|
|
|
does not own the `0x46c40 -> 0x4336d0 -> 0x438890` startup-runtime path.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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`
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`0x0047e330` counts how many candidates currently produce that query,
|
|
|
|
|
|
`placed_structure_get_nth_candidate_id_with_local_service_metrics` `0x0047e620` is the ordinal
|
|
|
|
|
|
selector over that same visible candidate family,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`placed_structure_query_cached_express_service_class_score` `0x0047e390` caches one parallel class
|
2026-04-08 16:31:33 -07:00
|
|
|
|
score for the express family now strongly aligned with `Passengers`, `Mail`, and `Troops`,
|
|
|
|
|
|
`placed_structure_refresh_candidate_local_service_comparison_cache_against_peer_site`
|
|
|
|
|
|
`0x0047eb90` rebuilds one peer-site comparison cache over the same local-service inputs, and
|
|
|
|
|
|
`placed_structure_select_best_candidate_id_by_local_service_score` `0x0047f910` is the best-hit
|
|
|
|
|
|
selector above the direct and directional local-service query pair. Those site-side scores now
|
|
|
|
|
|
have grounded shell read-side consumers too:
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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`. Current disassembly
|
|
|
|
|
|
no longer supports treating `3872` `Already Connected by Another Company` or `3873` `Not
|
|
|
|
|
|
Connected` as direct overlay-body emits.
|
|
|
|
|
|
- Station-detail overlay, corrected boundary: the neighboring connection-state note pair now appears
|
|
|
|
|
|
to live in a city connection-bonus label formatter, not in `0x0043f640`. That formatter is now
|
|
|
|
|
|
bounded as `city_site_format_connection_bonus_status_label` at `0x004207d0`: it directly chooses
|
|
|
|
|
|
localized ids `3868` through `3873` after consulting the reusable city-peer scan
|
|
|
|
|
|
`city_connection_bonus_exists_matching_peer_site` at `0x00420030`. Separately, `3879` `Out of
|
|
|
|
|
|
Sync` is 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 `All` legend 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
|
|
|
|
|
|
adjacent city bonus formatter at `0x004207d0` compares that id against the active company selector
|
|
|
|
|
|
before choosing whether a scanned site should carry `3871` `Connected By Another Company` or
|
|
|
|
|
|
`3872` `Already Connected by Another Company`. The larger caller boundary is no longer open
|
|
|
|
|
|
either: the first bounded announcement owner above this formatter family is now
|
|
|
|
|
|
`company_evaluate_and_publish_city_connection_bonus_news` at `0x00406050`, which re-enters the
|
|
|
|
|
|
peer-route candidate builder at `0x004046a0` and later publishes one of the localized
|
|
|
|
|
|
city-connection bonus news strings `2888`, `2890`, or `2921` through the shell news path.
|
|
|
|
|
|
- Station-detail overlay, peer-selector side: the city bonus formatter no longer depends only on
|
|
|
|
|
|
boolean peer existence. The companion helper
|
|
|
|
|
|
`city_connection_bonus_select_first_matching_peer_site` at `0x00420280` is now grounded as the
|
|
|
|
|
|
first-match selector paired with `city_connection_bonus_exists_matching_peer_site`: it walks the
|
|
|
|
|
|
same city-peer candidate set, applies the same site-class table plus the narrower
|
|
|
|
|
|
station-or-transit and linked-instance class-byte flags, and returns one representative matching
|
|
|
|
|
|
peer site id instead of a boolean. `city_site_format_connection_bonus_status_label` reuses that
|
|
|
|
|
|
selector after the note checks so it can recover one linked company context from the selected
|
|
|
|
|
|
peer. The remaining open edge here is therefore above this formatter family, not inside the
|
|
|
|
|
|
peer-scan pair itself.
|
|
|
|
|
|
- Station-detail overlay, caller side: the reusable bridge between the status formatter and the
|
|
|
|
|
|
company news lane is now bounded too. `city_connection_bonus_build_peer_route_candidate` at
|
|
|
|
|
|
`0x004046a0` reuses `city_connection_bonus_select_first_matching_peer_site` with both selector
|
|
|
|
|
|
flags forced on, samples the selected peer's derived coordinates through `0x0047df30` and
|
|
|
|
|
|
`0x0047df50`, and then either tries the shared heavy builder
|
|
|
|
|
|
`city_connection_try_build_route_with_optional_direct_site_placement` `0x00402cb0` or falls
|
|
|
|
|
|
back to the smaller wrapper `city_connection_bonus_try_compact_route_builder_from_region_entry`
|
|
|
|
|
|
`0x00404640` before handing the result back to the company-side announcement sweep at
|
|
|
|
|
|
`0x00406050`. The score side of that announcement lane is tighter now as well:
|
|
|
|
|
|
`city_compute_connection_bonus_candidate_weight` at `0x004010f0` provides the per-city opportunity
|
|
|
|
|
|
weight, `company_query_min_linked_site_distance_to_xy` at `0x00405920` provides the nearest
|
|
|
|
|
|
linked-site distance term, `company_count_linked_transit_sites` at `0x00426590` provides one of
|
|
|
|
|
|
the company-side caps, `company_compute_connection_bonus_value_ladder` at `0x00425320` supplies
|
2026-04-06 20:34:16 -07:00
|
|
|
|
the bounded company-side value scalar, and
|
|
|
|
|
|
`company_compute_prime_rate_from_issue39_scenario_baseline` at `0x00424580` now bounds the
|
|
|
|
|
|
shared prime-rate-side helper that this lane reuses beside the raw issue-`0x39` total,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`scenario_state_sum_issue_opinion_terms_raw` at `0x00436710` now bounds the raw additive
|
|
|
|
|
|
issue-total helper beneath that term, and `company_connection_bonus_lane_is_unlocked` at
|
|
|
|
|
|
`0x00427590` is the small boolean gate above the ladder. Wider governance and CompanyDetail xrefs
|
2026-04-06 20:34:16 -07:00
|
|
|
|
now tighten slot `0x2b` into the rolling net-profits lane reused by annual finance checks and a
|
|
|
|
|
|
per-share/history formatter, while the report-history descriptor table now aligns raw slot `0x09`
|
|
|
|
|
|
with the Income Statement fuel-cost lane surfaced by tooltip `1309`. The wider result now reads
|
|
|
|
|
|
more like recent net profits minus recent fuel burden than a governance-pressure term. That now
|
|
|
|
|
|
also sharpens the annual finance lane at `0x00401c50`: the first bankruptcy branch reads as
|
|
|
|
|
|
sustained cash-and-debt stress over recent profits and fuel burden, the later fallback branch as
|
|
|
|
|
|
a deeper `-300000` cash / three bad years cleanup trigger, and the later stock-issue branch reads
|
|
|
|
|
|
as a price-to-book-versus-coupon approval ladder rather than a generic support vote. The tail is
|
|
|
|
|
|
cleaner now too: it compares total retired versus newly issued principal to choose the `2882..2886`
|
|
|
|
|
|
debt headline family, then publishes `2887` separately from the accumulated repurchased-share
|
|
|
|
|
|
count. The sibling news owner above the same
|
|
|
|
|
|
city-pair route family is bounded now too:
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`simulation_try_select_and_publish_company_start_or_city_connection_news` `0x00404ce0`
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
this branch is therefore narrower now: it is mostly whether `0x39` should be read as the
|
|
|
|
|
|
narrower city-connection public-opinion lane or as part of a broader management-attitude family,
|
|
|
|
|
|
not the ownership of the connection-bonus formatter, peer-route candidate path, or company news
|
|
|
|
|
|
gate.
|
|
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Next Mapping Passes
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|