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`.
|
2026-04-10 20:36:44 -07:00
|
|
|
|
The file-list builder under that pair is tighter too: `0x4333f0` no longer just emits unsorted
|
|
|
|
|
|
`0x25a`-byte rows with raw names at `+0x0` and normalized labels at `+0x12d`. After the scan it
|
|
|
|
|
|
now clearly re-enters `0x51dc60`, which is a generic adjacent-swap insertion sort over fixed
|
|
|
|
|
|
records; in this setup-side caller it receives `record_size = 0x25a` and `key_offset = 0x12d`,
|
|
|
|
|
|
so the resulting `Setup.win` rows are sorted by display label rather than by the raw filename.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
`world_rebuild_secondary_raster_derived_surface_and_companion_planes_in_rect` `0x0044e940`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`world_setup_building_collection_phase` `0x0041ea50`, and the conditional region pair
|
|
|
|
|
|
`world_region_collection_seed_default_regions` `0x00421b60` plus
|
2026-04-10 20:36:44 -07:00
|
|
|
|
`world_region_border_overlay_rebuild` `0x004882e0`. The transport/pricing side is tighter now
|
|
|
|
|
|
too: `0x0044fb70` first routes one null-build path through the preview ensure wrapper
|
|
|
|
|
|
`0x0044faf0`, whose deeper worker `0x0044f840` allocates a default target through
|
|
|
|
|
|
`0x00534930`, seeds five palette entries through `0x0051de10`, maps each target pixel back into
|
|
|
|
|
|
the live secondary raster through `0x00533970/0x00533980`, uses the low-3-bit class split plus a
|
|
|
|
|
|
local `+/-4` neighborhood and `0x0051db80` to choose the preview color, and only then re-enters
|
|
|
|
|
|
`0x0044e940` on the full rectangle. The later `322` side is tighter now too: the top owner
|
|
|
|
|
|
`0x0044d410` rejects while generation counter `[world+0x2205]` is live or when `0x00449e90`
|
|
|
|
|
|
cannot supply a surviving work rect, then forwards that rect into `0x005374d0`, `0x00533e70`,
|
|
|
|
|
|
and `0x00537010`, notifies the shell owner at `0x0062be68`, and refreshes neighboring region or
|
|
|
|
|
|
manager companions through `0x00421f50`, `0x00461770`, and `0x00415340`. The local
|
|
|
|
|
|
companion-float strip under that same owner is tighter now too: `0x0044d4e0` and `0x0044d6e0`
|
|
|
|
|
|
are sibling five-pass cross-neighbor relaxers over `[world+0x1605]`; both clamp the caller
|
|
|
|
|
|
rectangle, average the four cardinal neighbors, subtract the current sample, scale the delta by
|
|
|
|
|
|
one fixed coefficient and caller gain, and then feed the surviving rectangle back into
|
|
|
|
|
|
`0x0044d410`. The first helper only checks raster bit `0x01` in `[world+0x2135]`, while the
|
|
|
|
|
|
second adds the tighter acceptance gate `0x004499c0` and can skip the final `0x0044d410` tail
|
|
|
|
|
|
when its caller disables that refresh. Their center-biased sibling `0x0044d880` is now bounded
|
|
|
|
|
|
too: it seeds one baseline from the current center sample when its caller gain is positive,
|
|
|
|
|
|
expands one square neighborhood by radius, and raises surrounding cells toward that baseline
|
|
|
|
|
|
through the same `0x0051dc00` falloff model before tailing into `0x0044d410`. The local
|
|
|
|
|
|
peak-oriented sibling `0x0044da70` then raises one clamped square neighborhood by quantized
|
|
|
|
|
|
local maxima and on its optional branch also consults `0x00534f00` before keeping the larger
|
|
|
|
|
|
peak. So the `0x0044d4e0/0x0044d6e0/0x0044d880/0x0044da70` band now reads as the local
|
|
|
|
|
|
companion-float refinement layer immediately beneath the shared `Calculating Heights...` owner
|
|
|
|
|
|
rather than a set of unrelated scalar writes. The default-region side is tighter too:
|
|
|
|
|
|
one earlier class-0-only helper `0x0041fb00` now sits directly under that same family too:
|
|
|
|
|
|
it skips nonzero `[region+0x23e]` records, rounds the region center through
|
|
|
|
|
|
`0x00455800/0x00455810/0x005a10d0`, stamps one orientation-dependent local secondary-overlay
|
|
|
|
|
|
pattern through `0x00534e10` plus `0x004497a0`, and then runs one bounded scored refinement pass
|
|
|
|
|
|
through `0x0051db80`, `0x00534040`, `0x005340a0`, `0x00534100`, and `0x00533fe0`. The finalizer
|
|
|
|
|
|
`0x00421730` then clears the per-cell region word at `[world+0x212d] + cell*4 + 1` across the
|
|
|
|
|
|
full live world raster, seeds each live region's cached bounds fields, and repopulates that same
|
|
|
|
|
|
raster by writing the nearest class-0 region id `[region+0x23a]` into empty or weaker cell slots
|
|
|
|
|
|
from one bounded center-and-radius sweep. The region-profile side is tighter too: `0x0041f940`
|
|
|
|
|
|
counts only the current subcollection entries whose backing
|
|
|
|
|
|
candidates pass `0x0041f998`, and that smaller predicate now has one real content split instead
|
|
|
|
|
|
of staying anonymous. Candidates whose subtype byte is not `2` pass immediately, while subtype-2
|
|
|
|
|
|
candidates only pass when either availability word `[candidate+0xba]` or runtime recipe latch
|
|
|
|
|
|
`[candidate+0x7ac]` is nonzero; `0x00412af0/0x00412b70` now ground the lookup step beneath both
|
|
|
|
|
|
counters as a casefolded profile-label-to-candidate resolver over the global candidate pool
|
|
|
|
|
|
`0x0062b268`; and the stricter year-filtered counter `0x0041f9b0` then stacks the category and
|
|
|
|
|
|
`[profile+0x26] <= year` gates on top of that same availability test. That border
|
|
|
|
|
|
pass is now tighter too: the
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
for the `Setup_Options_Buttons.imb` list rooted at `0xa7fa`, walks the 49-entry static rule table
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
state at `0x00622af0`, `0x00622aec`, and `0x006d1740`. The category side of that state is now
|
|
|
|
|
|
tighter too: the shared helper `0x0044bd10` quantizes the staged world coordinates into the
|
|
|
|
|
|
current cell, resolves the mixed byte-or-word region id from `[world+0x212d]`, looks up the
|
|
|
|
|
|
owning city-or-region entry through `0x0062bae0`, and returns `[entry+0x272]` with fallback
|
|
|
|
|
|
`5`; the StationPlace world-surface branches at `0x00508b80`, `0x00508d59`, and `0x0050a4e6`
|
|
|
|
|
|
use that result directly to refresh the current selection category latch at `0x00622af0`.
|
|
|
|
|
|
Together with
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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,
|
2026-04-10 20:36:44 -07:00
|
|
|
|
and the deeper shared bulldoze helper at `0x0044b160`. That helper is no longer just a vague
|
|
|
|
|
|
proximity scan: it validates the active company unless editor mode is active, emits the exact
|
|
|
|
|
|
RT3.lng bulldoze rejection ladder `419..424`, reads company cash through `0x0042a5d0`,
|
|
|
|
|
|
debits approved bulldoze cost through `0x0042a080`, and then scans nearby route entries,
|
|
|
|
|
|
linked structures, city-or-region records, route blockers, and placed structures before
|
|
|
|
|
|
dispatching the selected delete branch through `0x004937f0`, `0x004941a0`, `0x0048abf0`, or the
|
|
|
|
|
|
surrounding placed-structure virtual slot `+0x58`. That makes the TrackLay `Bulldoze` mode
|
|
|
|
|
|
tighter than before: the tool-side mode latch at `0x00622b0c == 0x40` is only the front-end
|
|
|
|
|
|
selector, while the actual world-side bulldoze choice and failure policy sits under `0x0044b160`
|
|
|
|
|
|
and is reused by neighboring world-surface callers beyond TrackLay itself.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
`BuildingDetail.win` singletons, the adjacent tool-window strip now grounded as
|
|
|
|
|
|
`Bulldoze.win`, `ChangeHeight.win`, `ChangeTrees.win`, `PaintRegion.win`, `PaintSound.win`,
|
|
|
|
|
|
`PaintTerrain.win`, `PaintTerritory.win`, and `StockBuy.win`, and now the shell-side
|
|
|
|
|
|
`Trainbuy.win` singleton too. That
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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`.
|
2026-04-10 20:36:44 -07:00
|
|
|
|
One smaller constructor-side ownership split is tighter now too: the cached `u16` chain fields
|
|
|
|
|
|
`[site+0x26e]`, `[site+0x270]`, and `[site+0x383]` are no longer free-floating. Constructor-side
|
|
|
|
|
|
helper `0x0041f7e0` prepends one new peer into that chain by storing the old head from
|
|
|
|
|
|
`[this+0x383]` into `[peer+0x270]` and then replacing `[this+0x383]` with the peer's own
|
|
|
|
|
|
placed-structure id `[peer+0x2a4]`; the direct resolver `0x0041f810` turns the cached head id
|
|
|
|
|
|
back into one live record through `0x0062b26c`; and the removal-side companion `0x0041f850`
|
|
|
|
|
|
removes one peer from the same chain before clearing `[peer+0x26e/+0x270]`. That caller split is
|
|
|
|
|
|
now strong enough to treat this as one cached single-link or predecessor-chain family beneath
|
|
|
|
|
|
placed-structure construction and rebuild rather than as unrelated scratch words.
|
2026-04-06 01:11:06 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
raster prep than an unowned generic world-grid scan. One neighboring caller into the same
|
|
|
|
|
|
height-support band is tighter now too: the world-side branch at `0x00419070..0x004190f0`
|
|
|
|
|
|
walks one admitted local window, dispatches the acceptance-gated relaxer `0x0044d6e0` once per
|
|
|
|
|
|
cell with a one-cell radius and no immediate tail refresh, and only after that outer loop
|
|
|
|
|
|
completes re-enters `0x0044d410` on the full surviving rectangle. The higher owner split is
|
|
|
|
|
|
tighter now too.
|
2026-04-06 01:11:06 -07:00
|
|
|
|
`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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
`0x00419200` is now bounded beneath that same projected-rectangle family too: it is the tiny
|
|
|
|
|
|
getter over the temporary dword cell-value bank at `0x0062b300`, flattening one caller cell
|
|
|
|
|
|
through live world width `[0x0062c120+0x2155]` and returning the corresponding dword when the
|
|
|
|
|
|
bank exists, else `0`. Current grounded caller is the later world-side branch at `0x0048af99`.
|
|
|
|
|
|
The smaller sibling
|
2026-04-06 01:11:06 -07:00
|
|
|
|
`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`.
|
2026-04-10 20:36:44 -07:00
|
|
|
|
The constructor or ownership split beneath that family is tighter now too. The shared allocator
|
|
|
|
|
|
`placed_structure_collection_allocate_and_construct_entry` `0x004134d0` no longer just reaches
|
|
|
|
|
|
an anonymous `0x3e1`-byte scratch object: it first constructs one concrete placed-structure
|
|
|
|
|
|
specialization through `placed_structure_construct_concrete_specialization_vtable_5c8c50`
|
|
|
|
|
|
`0x0040c950`, which calls `0x0045b680` to seed the common base table `0x005cb4c0`, clear the
|
|
|
|
|
|
derived field band `[this+0x23a..+0x26a]`, and re-enter the older base initializer
|
|
|
|
|
|
`0x00455610`, then installs the specialization table `0x005c8c50`. The paired cleanup path at
|
|
|
|
|
|
`0x0040c970` now reads like the matching teardown thunk: it briefly reinstalls `0x005c8c50`,
|
|
|
|
|
|
then immediately demotes the record back to the base table through `0x0045b040 ->
|
|
|
|
|
|
0x00455650` before the stack object is freed or unwound. The corresponding serializer entry
|
|
|
|
|
|
`0x0040c980` is equally tight now: it simply tails into `0x0045b560`, which first re-enters the
|
|
|
|
|
|
base serializer `0x004559d0` and then emits the derived payload bracketed by tags `0x5dc1` and
|
|
|
|
|
|
`0x5dc2` over `[this+0x23e]`, `[this+0x242]`, `[this+0x246]`, `[this+0x24e]`, and `[this+0x252]`.
|
|
|
|
|
|
One nearby string clue is useful but narrower than it first looked. The specialization table at
|
|
|
|
|
|
`0x005c8c50` is immediately followed by `RadioStation` and `Radio Station`, but the local member
|
|
|
|
|
|
method `0x0040ce60` now shows why: it compares site stem `[this+0x3ae]` against `Radio Station`,
|
|
|
|
|
|
canonicalizes that stem to `RadioStation` on match, and then re-enters `0x0040cd70` plus
|
|
|
|
|
|
`0x0045c150`. So those adjacent strings ground one stem-normalization lane inside the
|
|
|
|
|
|
specialization, not the specialization name by themselves. The small virtual-slot and cached-id
|
|
|
|
|
|
side is tighter now too. The same `0x005c8c50` table carries three literal-false stubs
|
|
|
|
|
|
`0x0040cc10/0x0040cc20/0x0040cc30`, one literal-true stub `0x0040ccc0`, and the narrower
|
|
|
|
|
|
linked-site-id presence predicate `0x0040ccb0`, which simply returns whether `[this+0x2a8]` is
|
|
|
|
|
|
nonzero. Beneath the stem-normalization path, `0x0040cd70` is now bounded as the common cached
|
|
|
|
|
|
source/candidate resolver: it clears `[this+0x3cc]` and `[this+0x3d0]`, scans source collection
|
|
|
|
|
|
`0x0062b2fc` for a stem match against `[this+0x3ae]`, stores the matched source id into
|
|
|
|
|
|
`[this+0x3cc]` plus the linked candidate/profile id from `[source+0x173]` into `[this+0x3d0]`,
|
|
|
|
|
|
and when no match exists formats localized id `0x00ad` through `0x0051e980` with the current
|
|
|
|
|
|
stem buffer. The two tiny followers `0x0040cec0` and `0x0040cee0` then resolve those cached ids
|
|
|
|
|
|
back into concrete source and candidate records through `0x0062b2fc` and `0x0062b268`
|
|
|
|
|
|
respectively. The derived payload and transient family under that same table is tighter now too.
|
|
|
|
|
|
`0x0045c150` is the clear payload-loader
|
|
|
|
|
|
counterpart to `0x0045b560`: it reinitializes the derived field band, opens the `0x5dc1/0x5dc2`
|
|
|
|
|
|
payload bracket, restores the two payload strings into `[this+0x23e]` and `[this+0x242]`, clears
|
|
|
|
|
|
the transient roots `[this+0x24e]`, `[this+0x256]`, `[this+0x25a]`, and `[this+0x25e]`, and then
|
|
|
|
|
|
rebuilds follow-on specialization state through `0x0045b5f0` and `0x0045b6f0`. The neighboring
|
|
|
|
|
|
builder pair is coherent too: `0x0045b210` formats `amb_%1_01.wav`, allocates a `0x141`-byte
|
|
|
|
|
|
transient object, stores it at `[this+0x24a]`, and registers it through `0x0052da00`, while
|
|
|
|
|
|
`0x0045c310` allocates the separate primary transient handle at `[this+0x246]` from payload
|
|
|
|
|
|
strings `[this+0x23e]` and `[this+0x242]`, then publishes it through `0x00530720` and
|
|
|
|
|
|
`0x0052d8a0`. The next rebuild layer is tighter now too. `0x0045b5f0` resolves one current
|
|
|
|
|
|
world-position tuple through `0x0052e720 -> 0x0051f090 -> 0x00534490`, then uses that derived
|
|
|
|
|
|
scalar to refresh the ambient-side transient again through `0x0045b210`. Above that,
|
|
|
|
|
|
`0x0045b6f0` clears the optional handles at `[this+0x25a]` and `[this+0x25e]`, then probes and
|
|
|
|
|
|
materializes a larger named variant family rooted in `[this+0x23e]` through strings such as
|
|
|
|
|
|
`%1_`, `%1_Upgrade1`, `%1_Anim%2.3dp`, `%1_Upgrade1Anim%2.3dp`, and
|
|
|
|
|
|
`%1_Upgrade1Light%2.3dp`, looping across numbered variants and publishing surviving records
|
|
|
|
|
|
through `0x0052d8a0`. One higher timed branch is bounded now too: `0x0045baf0` advances the two
|
|
|
|
|
|
timer-like fields `[this+0x26a]` and `[this+0x266]`, gates on nearby world-distance and
|
|
|
|
|
|
candidate-side checks, and then formats one output around `rnd_%1_%2.wav` plus the same anim or
|
|
|
|
|
|
light string family before dispatching through `0x00531e50` under owner `0x006d402c`. Their
|
|
|
|
|
|
release-side companions `0x0045b160` and `0x0045c3c0` tear down those same transient roots and
|
|
|
|
|
|
payload strings again, and the recurring service slice `0x0045be50` updates the derived scalar
|
|
|
|
|
|
triplet `[this+0x22e]`, `[this+0x232]`, and `[this+0x236]` before republishing it through
|
|
|
|
|
|
`0x00530720`. The string literals `amb_%1_01.wav`, `%1_Anim%2.3dp`,
|
|
|
|
|
|
`%1_Upgrade1Anim%2.3dp`, `%1_Upgrade1Light%2.3dp`, and `rnd_%1_%2.wav` therefore ground the
|
|
|
|
|
|
derived band `[this+0x23e..+0x26a]` as one payload-plus-transient owner family with ambient,
|
|
|
|
|
|
animation, light, and random-sound components, rather than more linked-site or route-entry ids.
|
|
|
|
|
|
One intermediate transient build layer is bounded now too. `0x0040cf00` first requires
|
|
|
|
|
|
creation-mode byte `[this+0x3d4] == 1` and live seed handle `[this+0x24e]`, re-enters
|
|
|
|
|
|
`0x0045b370(1)`, derives one scaled float from current local geometry through `[this+0x14]`,
|
|
|
|
|
|
`[this+0x21]`, `[this+0x3a]`, and `0x005c8cf4`, and then allocates a second transient handle
|
|
|
|
|
|
into `[this+0x25a]` through `0x00475ed0`. It follows that with one current world-position
|
|
|
|
|
|
resolve through `0x00414450` and `0x0052e720`, several local offset-triplet writes through
|
|
|
|
|
|
`0x00475010`, and four numbered channel updates through `0x00475030` with selector ids `1..4`.
|
|
|
|
|
|
That is enough to bound it as another specialization-side transient-handle builder beneath the
|
|
|
|
|
|
same payload family as `0x0045c310` and `0x0045b210`, without yet over-naming the user-facing
|
|
|
|
|
|
subtype or effect semantics of the built handle.
|
|
|
|
|
|
One adjacent helper block is tighter now too, and it should stay separate from the concrete
|
|
|
|
|
|
`0x005c8c50` vtable rather than being folded into it. The small predicates
|
|
|
|
|
|
`0x0040cc40/0x0040cc60/0x0040cc80` all follow the same `[this+0x173] -> 0x0062b268` chain into
|
|
|
|
|
|
one linked candidate/profile record. `0x0040cc40` returns whether candidate subtype byte
|
|
|
|
|
|
`[candidate+0x32]` equals `1`, `0x0040cc60` returns raw candidate byte `[candidate+0xba]`, and
|
|
|
|
|
|
`0x0040cc80` returns the stricter conjunction `subtype == 1 && class == 3` using class byte
|
|
|
|
|
|
`[candidate+0x8c]`. The current grounded caller for all three is
|
|
|
|
|
|
`world_grid_refresh_projected_rect_sample_band_and_flag_mask` `0x00418610`, where they derive
|
|
|
|
|
|
the two boolean mode inputs for `0x00415020`; that makes them a shared candidate-profile helper
|
|
|
|
|
|
cluster adjacent to the specialization family, not direct overrides in the `0x005c8c50` table.
|
|
|
|
|
|
The neighboring `0x0040ccf0` stays on the same “adjacent but not the same vtable” side of the
|
|
|
|
|
|
boundary: it resolves one linked instance through `0x0047de00`, follows that instance's vtable
|
|
|
|
|
|
`+0x80` owner chain, and returns candidate class byte `[candidate+0x8c]`. Current grounded
|
|
|
|
|
|
callers at `0x00480fb5` and `0x004b03ce` compare that returned class against `3` and `4`, so the
|
|
|
|
|
|
current safest note is simply “linked-instance candidate-class reader.” One gated collection-side
|
|
|
|
|
|
scan in the same neighborhood is bounded now too. `0x0040cd10` first requires shell latch
|
|
|
|
|
|
`[0x006cec74+0x1c3]` and creation-mode byte `[this+0x3d4] == 1`, then resolves the current
|
|
|
|
|
|
subject's center world-grid cell through `0x00455f60`, reads the local site roster count and id
|
|
|
|
|
|
list at `[cell+0xeb]` and `[cell+0xef]`, and resolves each listed placed-structure id through
|
|
|
|
|
|
`0x006cec20`. Its current grounded caller is the broader collection sweep at `0x00413860`, so
|
|
|
|
|
|
the strongest current read is a gated center-cell site-roster scan rather than one more direct
|
|
|
|
|
|
specialization override.
|
|
|
|
|
|
One small neighboring world-grid helper is grounded cleanly enough to keep too. `0x0040ccd0`
|
|
|
|
|
|
takes one live world root, one X-like index, and one Y-like index, multiplies the Y-like term by
|
|
|
|
|
|
world width `[world+0x2145]`, adds the X-like term, scales the resulting slot by fixed cell
|
|
|
|
|
|
stride `0x1d7`, and returns the corresponding cell pointer under base `[world+0x2129]`. Current
|
|
|
|
|
|
grounded callers include the placed-structure cargo-service bitset sweep around `0x0042c386` and
|
|
|
|
|
|
several world-grid overlay passes in the `0x004525bc..0x00452bb2` range, so that helper is now
|
|
|
|
|
|
best read as the shared `(x,y) -> world-cell*` resolver rather than another object-specific
|
|
|
|
|
|
method.
|
|
|
|
|
|
The next sibling table at `0x005c9750` is tighter now too, though still not fully decoded. The
|
|
|
|
|
|
structural anchor is `map_load_city_database` `0x00474610`: that loader stages bundle tags
|
|
|
|
|
|
`0x61a9..0x61ab`, iterates one collection, and dispatches each entry through vtable slot `+0x44`.
|
|
|
|
|
|
In `0x005c9750`, that same slot resolves into the sibling record family beside
|
|
|
|
|
|
`0x0041ab70/0x0041ab80`, which is enough to treat the table as the city-database entry family
|
|
|
|
|
|
rather than a generic unresolved runtime object. The small load-side slot `0x0041ab70` simply
|
|
|
|
|
|
jumps into `0x0045c6f0`, which restores two strings into `[this+0x32e]` and `[this+0x332]`,
|
|
|
|
|
|
stages the same `0x61a9/0x61aa/0x61ab` tag family, and iterates the child runtime band rooted at
|
|
|
|
|
|
`[this+0x316]`. The one real behavioral slot we recovered there is `0x0041ab80`: it refreshes
|
|
|
|
|
|
one named handle through owner `0x006d4020`, writes the resulting handle into `[this+0x1c]`,
|
|
|
|
|
|
queries three base floats through `0x0045c480`, and returns one scaled float derived from the
|
|
|
|
|
|
first queried component. That method is currently called from later world-side branches at
|
|
|
|
|
|
`0x0046e4f7`, `0x004aafee`, and `0x004ab020`. The exact user-facing semantics of the handle and
|
|
|
|
|
|
the many constant-return virtuals in the same table are still open, but the family boundary is no
|
|
|
|
|
|
longer arbitrary: this is now the current city-database entry vtable cluster, not another
|
|
|
|
|
|
placed-structure specialization. One correction matters here too: the lower
|
|
|
|
|
|
`0x00455660/0x00455800/0x00455810/0x00455930` helpers are no longer city-only. Local `.rdata`
|
|
|
|
|
|
now shows the same shared slots under both the city-entry table `0x005c9750` and the sibling
|
|
|
|
|
|
`Infrastructure` table `0x005cfd00`, whose constructors at `0x0048a240/0x0048a2dc/0x00490a3c`
|
|
|
|
|
|
all re-enter `0x00455b20`, install `0x005cfd00`, and seed the same scalar-band family through
|
|
|
|
|
|
`0x00455b70` with the literal stem `Infrastructure`. So the safest current family name for those
|
|
|
|
|
|
low helpers is the broader `0x23a` runtime-object band rather than city-entry-only. `0x00455800`
|
|
|
|
|
|
and `0x00455810` are simply the paired normalized coordinate getters over `[this+0x1e2]` and
|
|
|
|
|
|
`[this+0x1ea]`, which explains why they recur all over the city-side, route-link, and
|
|
|
|
|
|
placed-structure placement code. The narrower helper `0x00455660` sits above those getters: it
|
|
|
|
|
|
scales the caller-supplied normalized coordinate pair, converts it through
|
|
|
|
|
|
`0x006d4024 -> 0x0051f090 -> 0x00534490`, adds a caller-supplied integer height bias, optionally
|
|
|
|
|
|
rounds that height through `0x005a10d0` when the auxiliary-preview gate `0x00434050` is active,
|
|
|
|
|
|
and finally publishes the resulting world-anchor triplet through `0x00530720`. One
|
|
|
|
|
|
serializer-side sibling is bounded too. `0x00455930` queries two triplet-like scalar bands
|
|
|
|
|
|
through `0x0052e720` and `0x0052e880` and writes the resulting six dwords through `0x00531030`;
|
|
|
|
|
|
its load-side stream counterpart `0x00455870` reads the same six four-byte lanes back through
|
|
|
|
|
|
`0x00531150`, republishes the first triplet through `0x00530720`, republishes the second through
|
|
|
|
|
|
`0x0052e8b0`, and returns the summed byte count. The tagged load-side mirror `0x004559d0`
|
|
|
|
|
|
restores the same `[this+0x206/+0x20a/+0x20e]` band beneath tags `0x55f1..0x55f3`, then
|
|
|
|
|
|
dispatches vtable slot `+0x4c`. The `Infrastructure` side is tighter now too. The optional child
|
|
|
|
|
|
attach helper `0x0048a1e0` allocates and seeds one fresh `Infrastructure` child and, when the
|
|
|
|
|
|
owner already has more than one child, clones the first child's two triplet bands into that new
|
|
|
|
|
|
child before attaching it. The looped rebuild `0x0048dd50` then rebuilds the owner-visible
|
|
|
|
|
|
`Infrastructure` children one ordinal at a time, tears down the higher extras above ordinal `5`,
|
|
|
|
|
|
refreshes cached primary-child slot `[this+0x248]` when needed, and finishes with the same
|
|
|
|
|
|
world-cell and route-side follow-on family around `0x00448a70`, `0x00493660`, and `0x0048b660`.
|
|
|
|
|
|
The smaller attach helper `0x00490a3c` is now bounded too: it conditionally allocates one
|
|
|
|
|
|
`Infrastructure` child from a caller-supplied payload stem, attaches it to the current owner, and
|
|
|
|
|
|
then seeds three caller-supplied position lanes through `0x00539530` and `0x0053a5b0`. The
|
|
|
|
|
|
direct route-entry side of the same family is no longer anonymous either: `0x0048e140`,
|
|
|
|
|
|
`0x0048e160`, and `0x0048e180` are the three direct resolvers over owner fields
|
|
|
|
|
|
`[this+0x206/+0x20a/+0x20e]` into the live route-entry collection `0x006cfca8`. Another shared
|
|
|
|
|
|
sibling family is tight enough now too: the real
|
|
|
|
|
|
packed-byte-triplet owner is `0x0052e680`, and the two local
|
|
|
|
|
|
wrappers `0x00455820` and `0x00455840` both force its recursion flag on. `0x00455820` forwards
|
|
|
|
|
|
three explicit byte arguments, while `0x00455840` unpacks one packed `u24`-style dword into
|
|
|
|
|
|
three byte lanes first. The downstream setter writes packed byte-triplet lane `[this+0x43]`,
|
|
|
|
|
|
defaults that lane to `0x007f7f7f` when all three bytes are zero, and when recursion is enabled
|
|
|
|
|
|
it walks child list `[this+0x75]` through `0x00556ef0/0x00556f00` and re-applies the same packed
|
|
|
|
|
|
update recursively to every child. The sibling `Infrastructure` table `0x005cfd00` also still
|
|
|
|
|
|
carries three tiny fixed-return slots after the shared formatter thunk `0x00455860`: two return
|
|
|
|
|
|
literal `0x46`, and one returns fixed float `1.25f`, so those slots are now bounded as small
|
|
|
|
|
|
table constants rather than as missing behavior. One other sibling table is bounded enough now
|
|
|
|
|
|
to keep separate too: local `.rdata` at `0x005c9a60` carries the same shared low `0x23a` slots
|
|
|
|
|
|
plus table-specific overrides `0x0041f680`, `0x0041f720`, `0x0041f7b0`, `0x0041f7e0`,
|
|
|
|
|
|
`0x0041f810`, and `0x0041f850`, with nearby literals `(%1)` and `Marker09`. The exact subtype
|
|
|
|
|
|
name behind that table is still open, but the behavior split is strong enough: `0x0041f680`
|
|
|
|
|
|
first refreshes the current global preview/helper owner through `0x00455de0` and then forwards
|
|
|
|
|
|
mode `1` plus handle `[this+0x23a]` into `0x004cf830`; `0x0041f720` formats `[this+0x356]`,
|
|
|
|
|
|
publishes the resulting shell text, and conditionally dispatches `0x00452fa0` with action
|
|
|
|
|
|
`0x12` when world state `[0x0062c120+0x2175]` is `0` or `0x15`; `0x0041f7b0` publishes one
|
|
|
|
|
|
fixed payload and only then jumps to `0x00453510` when that same world state is `0x12`. The
|
|
|
|
|
|
same table also has a tighter adjacent helper strip now: `0x0041f6a0` is the direct
|
|
|
|
|
|
`[this+0x37f] -> 0x00517cf0` slot-count query, `0x0041f6b0` is the rounded normalized-coordinate
|
|
|
|
|
|
world-scalar query above `0x0044afa0`, and that deeper helper is no longer opaque: it first
|
|
|
|
|
|
computes one distance-like term from the current cell to global preview-anchor fields
|
|
|
|
|
|
`[0x0062be68+0x24/+0x1c]` through the precomputed radial helper `0x0051dc00`, samples the
|
|
|
|
|
|
companion float raster `[world+0x1605]` at the current cell plus two 8-direction rings, keeps
|
|
|
|
|
|
the strongest local rise above the center sample, adds one thresholded preview-scalar bias from
|
|
|
|
|
|
`[0x0062be68+0x20]`, and then adds `0x32` more when the small global gate `0x0041fff0` passes.
|
|
|
|
|
|
That gate itself now reads as a shared shell/preview threshold test: it returns true when
|
|
|
|
|
|
`[0x006cec74+0x2bb] == 2`, or when the same mode dword is merely nonzero and preview scalar
|
|
|
|
|
|
`[0x0062be68+0x20]` exceeds the float threshold at `0x005c9a9c`. `0x0041f6e0` resolves the
|
|
|
|
|
|
current center world-grid cell and checks one packed `u16` token through `0x0042b2d0`, and
|
|
|
|
|
|
`0x0041f6f0` is the raw byte getter over `[this+0x42]`. One nearby mode-gated flag reader is
|
|
|
|
|
|
bounded too: `0x0041f910` returns
|
|
|
|
|
|
literal `1` when shell mode gate `0x004338c0` is inactive or the object class dword
|
|
|
|
|
|
`[this+0x23e]` is nonzero, and only on the class-0 path does it return the same byte
|
|
|
|
|
|
`[this+0x42]`. The release side under that same sibling family is bounded too: `0x00420650`
|
|
|
|
|
|
lazily seeds `[this+0x317]` from shell-profile word `[0x006cec78+0x0d]` before re-entering
|
|
|
|
|
|
`0x00420350`, and that body is now bounded as the local scalar refresh over `[this+0x31b]`:
|
|
|
|
|
|
it compares the seeded word against the current shell-profile word, maps the delta through one
|
|
|
|
|
|
bounded piecewise float curve, and applies one extra affine adjustment when `[this+0x23e]` is
|
|
|
|
|
|
nonzero. `0x00420670` then frees the optional dynamic payload at
|
|
|
|
|
|
`[this+0x37f]`, clears the cached `u16` link chain through `0x0041f8d0`, and tails into base
|
|
|
|
|
|
cleanup `0x00455d20`; and the small collection-side wrapper `0x00421700` simply resolves one
|
|
|
|
|
|
entry id, re-enters `0x00420670`, and erases that id from the owning collection. The
|
|
|
|
|
|
city-entry family also now has one tighter
|
|
|
|
|
|
helper-object branch. `0x00474030`
|
|
|
|
|
|
switches over mode enum `0..10`, maps those cases into `0x00475ed0` with selector families
|
|
|
|
|
|
`4/0xd/0xe` and fixed scalar presets `0.5/0.75/1.0/1.5/2.0`, and on success publishes the
|
|
|
|
|
|
caller-supplied world triplet through `[helper+0x4]`. The refresh helper `0x00474260` first
|
|
|
|
|
|
reloads payload through `0x00455fc0`, then samples the current normalized coordinates plus one
|
|
|
|
|
|
world-height scalar from `0x00448bd0`, and rebuilds `[this+0x23a]` through that same
|
|
|
|
|
|
mode-selected helper builder using mode `[this+0x242]`. The surrounding collection-side and
|
|
|
|
|
|
teardown helpers are tight enough now too. `0x004743d0` and `0x00474400` are the two release
|
|
|
|
|
|
siblings over the same helper field `[this+0x23a]`: both destroy that helper through `0x00475100`
|
|
|
|
|
|
when live and clear the field, but `0x004743d0` tails into the smaller base cleanup
|
|
|
|
|
|
`0x00455650`, while `0x00474400` tails into the heavier dynamic-payload cleanup `0x00455d20`.
|
|
|
|
|
|
The small predicate `0x00474430` simply returns `1` unless shell mode gate `0x004338c0` is
|
|
|
|
|
|
active, in which case it returns byte `[this+0x42]`; the exact meaning of that flag byte is still
|
|
|
|
|
|
open, but the mode-gated query itself is bounded now. One level higher, `0x00474450` constructs
|
|
|
|
|
|
the collection rooted at vtable `0x005ce4a8` with fixed parameters `(0,0,0,0x14,0x0a,0,0)`, and
|
|
|
|
|
|
bootstrap caller `0x004487a9` stores that collection into global `0x006cea50`. The collection's
|
|
|
|
|
|
entry path is also bounded now: `0x004744a0` seeds a stack-local temporary entry through
|
|
|
|
|
|
`0x00474110`, allocates one `0x250`-byte live record, resolves it, constructs it through
|
|
|
|
|
|
`0x00474130`, and then tears the temporary entry down through `0x004743d0`; `0x00474510`
|
|
|
|
|
|
resolves one entry id, releases it through `0x00474400`, and removes it from the collection. The
|
|
|
|
|
|
collection-owned load loop at `0x00474540` sits directly beneath `map_load_city_database`
|
|
|
|
|
|
`0x00474610`: it opens the same `0x61a9/0x61aa/0x61ab` bracket on the caller-supplied bundle,
|
|
|
|
|
|
binds the selected path context, iterates the current collection, dispatches each record through
|
|
|
|
|
|
vtable slot `+0x40`, accumulates the returned byte counts, and tears down the temporary entry
|
|
|
|
|
|
after each record. That still leaves broader semantic questions open, but the current static edges
|
|
|
|
|
|
around the city-entry family are now largely exhausted. The adjacent scaffolding is bounded too.
|
|
|
|
|
|
Base helper `0x00455b20` initializes the shared `0x23a`-sized record family by installing base
|
|
|
|
|
|
vtable `0x005cb1c0`, clearing the scalar bands `[+0x206/+0x20a/+0x20e/+0x22e/+0x232]`, zeroing
|
|
|
|
|
|
the seven-dword block `[+0x212..+0x22a]`, and re-entering `0x0052ecd0`; that is why it shows up
|
|
|
|
|
|
not only in the city-entry temporary constructor `0x00474110`, but also in several later object
|
|
|
|
|
|
construction paths outside the city family. The city collection itself now has a bounded teardown
|
|
|
|
|
|
side too: `0x00474480` is the small release-and-free wrapper for the collection rooted at
|
|
|
|
|
|
`0x005ce4a8`, and teardown caller `0x004492c3` uses it before freeing the global collection
|
|
|
|
|
|
pointer at `0x006cea50`. On the load side, `0x00445713`'s broader setup branch re-enters
|
|
|
|
|
|
`map_load_city_database` `0x00474610` on that same global `0x006cea50` collection. Outside the
|
|
|
|
|
|
city-specific branch, the two tiny dispatch wrappers `0x00455a40` and `0x00455a50` are also now
|
|
|
|
|
|
bounded as raw vtable-slot helpers: the first jumps through entry slot `+0x44`, while the second
|
|
|
|
|
|
pushes one caller argument into slot `+0x40` and then clears the global roots
|
|
|
|
|
|
`0x006acd38/0x006acd3c/0x006acd40`. Finally, `0x00455a70` is now bounded as the generic
|
|
|
|
|
|
current-position triplet publisher that queries `0x0052e720`, converts through
|
|
|
|
|
|
`0x006d4024 -> 0x0051f090 -> 0x00534490`, adds a caller-supplied height bias, optionally rounds
|
|
|
|
|
|
under `0x00434050`, and publishes through `0x00530720`. The temporary-entry constructor and live
|
|
|
|
|
|
entry constructor are bounded now too. `0x00474110` is the tiny stack-local initializer: it
|
|
|
|
|
|
re-enters shared base init `0x00455b20`, clears helper fields `[+0x23a]` and `[+0x242]`,
|
|
|
|
|
|
installs vtable `0x005ce428`, and returns. `0x00474130` is the real live-entry constructor: it
|
|
|
|
|
|
stores the entry id into `[+0x23e]`, clears the trailing helper payload block `[+0x246..+0x24e]`,
|
|
|
|
|
|
stores mode `[+0x242]`, derives one world-height scalar from the supplied coordinate pair through
|
|
|
|
|
|
`0x00448bd0`, builds helper field `[+0x23a]` through `0x00474030`, and then seeds the remaining
|
|
|
|
|
|
default scalar or flag tuple through `0x00455b70` using the fixed defaults at
|
|
|
|
|
|
`0x005ce49c/0x005ce4a0/0x005ce4a4`. That broader helper is no longer city-specific either:
|
|
|
|
|
|
`0x00455b70` is now bounded as the shared scalar-band initializer that clears the same
|
|
|
|
|
|
`[+0x206/+0x20a/+0x20e/+0x22e/+0x232]` family, optionally copies up to three caller-supplied
|
|
|
|
|
|
strings into `[+0x206/+0x20a/+0x20e]`, synthesizes one default local token buffer when the middle
|
|
|
|
|
|
string is absent, chooses one effective mode string from the third or first input, derives one
|
|
|
|
|
|
owner handle or transform token through `0x0051f090` unless the caller suppresses that path, and
|
|
|
|
|
|
then seeds the remaining scalar band through `0x0052edf0` and `0x0052e670`.
|
|
|
|
|
|
The same specialization also now has one tighter UI-facing side. The override trio
|
|
|
|
|
|
`0x0040e4e0`, `0x0040e880`, and `0x0040e9d0` all sit in the same `0x005c8c50` table and reuse
|
|
|
|
|
|
the linked-site-capable vtable `+0x70` latch together with creation-mode byte `[this+0x3d4]`.
|
|
|
|
|
|
`0x0040e4e0` is the strongest current owner for contextual status text: it branches across
|
|
|
|
|
|
linked-peer checks, world-mode fields `[0x0062c120+0x2171/+0x2175/+0x2179/+0x2181]`, current
|
|
|
|
|
|
owner state, and candidate or linked-peer display labels, then formats localized ids such as
|
|
|
|
|
|
`0x00af`, `0x00b0`, `0x00b1`, `0x00b2`, `0x00b3`, and `0x0afd` before pushing the result through
|
|
|
|
|
|
shell presenter `0x00538c70`. `0x0040e880` pairs with that publisher on the action side: it
|
|
|
|
|
|
first resets the same presenter through the fixed token at `0x005c87a8`, then dispatches several
|
|
|
|
|
|
world-side follow-ons through `0x00413620`, `0x00453510`, `0x00452db0`, and `0x00452fa0` under
|
|
|
|
|
|
the same linked-peer and world-mode gates. `0x0040e9d0` is the narrower query-style formatter:
|
|
|
|
|
|
when shell branch `[0x006cec74+0x74]` is live it formats one localized label with the fixed
|
|
|
|
|
|
suffix `\nAbsIndex %d`, otherwise it can return one company-side owner label through
|
|
|
|
|
|
`0x00426b10`, and only then falls back to `0x00455860`. So the current best read is that this
|
|
|
|
|
|
concrete specialization owns both the ambient or animation transient family and one linked-peer
|
|
|
|
|
|
status or action UI surface, without yet proving the user-facing subtype name.
|
|
|
|
|
|
One smaller subtype hook cluster is tighter now too. The concrete table also carries
|
|
|
|
|
|
`0x0040d170` and `0x0040d1b0`, both of which resolve the current candidate through cached stem id
|
|
|
|
|
|
`[this+0x3cc]` and then test candidate dword `[candidate+0x4b]`. When that flag is nonzero,
|
|
|
|
|
|
`0x0040d170` tail-calls the primary transient builder `0x0045c310` and `0x0040d1b0` tail-calls
|
|
|
|
|
|
the matching release path `0x0045c3c0`. The same neighborhood now also gives direct cached-record
|
|
|
|
|
|
resolvers instead of more anonymous pointer math: `0x0040d1f0` returns the linked peer from
|
|
|
|
|
|
`[this+0x2a8]` through `0x006cec20`, while `0x0040d210` returns the owner-side record from
|
|
|
|
|
|
`[this+0x276]` through `0x0062be10`. That makes the specialization-side ownership split cleaner:
|
|
|
|
|
|
the `0x005c8c50` family is not only formatting labels and driving side effects, it also has a
|
|
|
|
|
|
direct candidate-flag hook for building or dropping the primary transient handle.
|
2026-04-06 01:11:06 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
then tails into the linked-site anchor follow-on at `0x0040e360`. One later caller into that
|
|
|
|
|
|
same side-refresh family is bounded now too: `0x00419110` clamps a caller rectangle to the live
|
|
|
|
|
|
world bounds, resolves one per-cell bucket chain through the hashed map at `[arg0+0x88]` and
|
|
|
|
|
|
`0x0053dae0`, and then re-enters `0x0040eba0` for every bucketed site in that rectangle using
|
|
|
|
|
|
the stored per-entry coordinate pair plus two zero flags. Current grounded caller is the edit-side
|
|
|
|
|
|
branch at `0x004bc851`, immediately after the neighboring nibble and companion-float mutations,
|
|
|
|
|
|
so this now looks like a bucket-map-driven local-runtime side-state refresh wrapper rather than
|
|
|
|
|
|
another broad site sweep.
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
main-world interaction surface rather than a generic detail button for one tool family only. One
|
|
|
|
|
|
more shell-side consumer is bounded now too: `0x004bc350` is a world-surface brush handler over
|
|
|
|
|
|
that same `0x07d6` control plus the adjacent mode family `0x0faa..0x0faf` and ordinal strip
|
|
|
|
|
|
`0x0fa0..0x0fa7`. That family is no longer just a generic unlabeled brush owner: the
|
|
|
|
|
|
detail-panel manager now grounds the sibling tool-window strip immediately above it as
|
|
|
|
|
|
`Bulldoze.win`, `ChangeHeight.win`, `ChangeTrees.win`, `PaintTerrain.win`, `PaintRegion.win`,
|
|
|
|
|
|
`PaintSound.win`, and `PaintTerritory.win`, each with its own constructor, refresh pass, and
|
|
|
|
|
|
message owner beneath the same controller rooted at `0x006d0818`. Once its local drag latch is
|
|
|
|
|
|
active it dispatches the current mode dword at `[0x006d0818+0x8c]` into the companion-float
|
|
|
|
|
|
helpers `0x0044d4e0`, `0x0044d880`, and `0x0044da70`, or into the secondary-raster nibble path
|
|
|
|
|
|
`0x00448e20 -> 0x00419110`, so the remaining uncertainty has narrowed from ownership to the
|
|
|
|
|
|
exact one-to-one mode-button mapping inside that grounded tool-window strip. The
|
|
|
|
|
|
`PaintTerrain.win` side is tighter now too: constructor `0x004f7ce0` seeds shell singleton
|
|
|
|
|
|
`0x006d1304`, callback root `0x006d1334`, a constructor-time bulk-update latch at
|
|
|
|
|
|
`0x006d1330/0x006d1331`, and the broader terrain mode or scalar state from `0x00622748..0x00622788`
|
|
|
|
|
|
plus `0x006d1308..0x006d1324`; it then registers the callback strip `0x004f5960`,
|
|
|
|
|
|
`0x004f59f0`, `0x004f6070`, and `0x004f69e0` through `0x00540120`. The direct message owner
|
|
|
|
|
|
under that same family is no longer just a loose branch fan-out either: `0x004f6f50` owns
|
|
|
|
|
|
messages `2/0xca/0xcb`, keeps control `0x07d6` as the live world-surface drag latch, routes
|
|
|
|
|
|
explicit undo control `0x2775` into `0x004f5c30`, maps the selector and mode strips
|
|
|
|
|
|
`0x2711..0x2730`, `0x27d9..0x27df`, `0x283d..0x2843`, `0x28a1..0x28e6`, `0x2915..0x291e`,
|
|
|
|
|
|
`0x2964..0x2967`, and `0x29cd..` into the cached terrain lanes, and then fans the active drag
|
|
|
|
|
|
path into preview helpers `0x004f6930`, `0x004fb5e0`, `0x004fc280`, `0x004fc4d0`, and
|
|
|
|
|
|
`0x004fc630` depending on the current terrain mode. Inside that same family
|
|
|
|
|
|
`0x004f5a80/0x004f5c30` are now the bounded local undo snapshot pair, `0x004f5dd0` is the
|
|
|
|
|
|
tiny active-scalar-group selector, `0x004f6b50` owns the ten-entry mode strip rooted at
|
|
|
|
|
|
`0x28dd`, `0x004f6390` is the broad mode-panel repaint owner, `0x004f6250` is the
|
|
|
|
|
|
mode-dependent world-dispatch bridge, `0x004f6930` samples the current preview raster at
|
|
|
|
|
|
world coordinates, and `0x004fb5e0` is the heavier preview-raster rebuild and shell-surface
|
|
|
|
|
|
publish worker. The next local helpers under that same family are tighter now too:
|
|
|
|
|
|
`0x004f5ea0` and `0x004f5ec0` are just the two tiny special-mode predicates
|
|
|
|
|
|
`(mode 5, variant 2)` and `(mode 4, variant 2)`, `0x004f8770` is the weighted RGB sampler
|
|
|
|
|
|
over the current preview or undo-backed raster neighborhood, `0x004f89b0` is the local
|
|
|
|
|
|
byte-grid connected-component grower over the eight-neighbor tables `0x00624b28/0x00624b48`,
|
|
|
|
|
|
and `0x004f8bb0` is the main drag-path preview compositor that consumes the drag sample strip
|
|
|
|
|
|
`[this+0xf0/+0xf4/+0xf8]`, allocates temporary float or byte grids, and then rasterizes the
|
|
|
|
|
|
surviving preview rectangle through the current terrain descriptor tables at `0x005f3500`,
|
|
|
|
|
|
the weighted color sampler `0x004f8770`, the component grower `0x004f89b0`, and several live
|
|
|
|
|
|
world samplers before writing final RGBA pixels back into the preview surface. The late tail
|
|
|
|
|
|
under that same compositor is tighter too: the mode-`4`, variant-`2` path toggles
|
|
|
|
|
|
secondary-raster byte-2 bit `0x20` by comparing current and previous cells through
|
|
|
|
|
|
`0x00534f40`; the broader mode-`4/5` world-facing branch samples class set `2/4/5` through
|
|
|
|
|
|
`0x00534ec0`, re-enters `0x0044de30` and `0x0044df10`, latches pending world coordinates into
|
|
|
|
|
|
`[0x006d1304+0x78/+0x7c]`, and rewrites the low nibble plus the signed overlay-vector planes
|
|
|
|
|
|
through `0x00448df0`, `0x00448e20`, `0x00448ec0`, `0x00448ee0`, `0x00448e60`, and
|
|
|
|
|
|
`0x00448e90`. After the optional mode-`5` smoothing sweep through `0x004f8370`, the publish
|
|
|
|
|
|
tail either materializes one shell preview surface through `0x0051f090 -> 0x00534730` or, for
|
|
|
|
|
|
the mode-`4/5` world-facing variants, converts the surviving preview rectangle back into
|
|
|
|
|
|
world-space bounds and re-enters `0x0044e940`, `0x00452f20`, and `0x0044d410`. The companion
|
|
|
|
|
|
preview-rebuild owner `0x004fb5e0` is tighter on entry, too: `0x004f726c` is the early
|
|
|
|
|
|
rebuild path that first copies the live preview raster into `[this+0x118]` and snapshots undo
|
|
|
|
|
|
state through `0x004f5a80`, while `0x004f7ada` is the later drag-active path that first
|
|
|
|
|
|
allocates a temporary occupancy mask before rebuilding and then conditionally mirrors that
|
|
|
|
|
|
mask through `0x00450520` on the world-mode-`0x17` side path. So the remaining uncertainty
|
|
|
|
|
|
has narrowed again from family ownership to the exact meaning of a few per-mode scalar and
|
|
|
|
|
|
token lanes, not to whether `PaintTerrain.win` itself is still a mixed shell/world owner. The
|
|
|
|
|
|
same brush strip is tighter now too:
|
|
|
|
|
|
`0x004bc210` stores the selected ordinal and refreshes one scalar caption
|
|
|
|
|
|
from table `0x00621e24`, `0x004bc260` exposes the cached world coordinate pair plus the
|
|
|
|
|
|
currently selected scalar, and `0x004bc290` restyles the ordinal strip `0x0fa1..0x0fa7` plus
|
|
|
|
|
|
the mapped mode strip `0x0faa..0x0faf` against the current mode dword `[0x006d0818+0x8c]`. The
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
beneath that final adjust term. The per-region prepass feeding that normalization is tighter too:
|
|
|
|
|
|
`0x00420d40` clears `[region+0x306/+0x30a/+0x30e]`, walks the linked placed-structure chain from
|
|
|
|
|
|
`[region+0x383]`, accumulates two local placed-structure metrics through `0x0040ca70` and
|
|
|
|
|
|
`0x0040ca80`, and only for class-0 candidates also folds source field `[source+0x141]` through
|
|
|
|
|
|
`0x0040cec0` into the third accumulator before tailing into the later scalar refresh. That tail
|
|
|
|
|
|
helper `0x00420560` is tighter now too: on class-0 regions it revisits the same linked chain and
|
|
|
|
|
|
folds a class-mix contribution into `[region+0x312]`, with one source-derived term for candidate
|
|
|
|
|
|
class `0`, a separate branch keyed by `[candidate+0x78c]` and `[site+0x246]` for class `2`, one
|
|
|
|
|
|
fixed increment for class `3`, and no current contribution from class `1`. The
|
|
|
|
|
|
remaining setup-side uncertainty has therefore narrowed
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
gate; `[0x006cec74+0x178]` now looks like the direct seeding-burst gate and selected-year-adjust
|
|
|
|
|
|
policy; 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. One write side for that
|
|
|
|
|
|
`[shell+0x178]` policy is now grounded too: inside `shell_dispatch_ui_command` `0x00464410`,
|
|
|
|
|
|
command ids `0x9d26..0x9d28` store `command_id - 0x9d26` directly into `[0x006cec74+0x178]`,
|
|
|
|
|
|
yielding live values `0`, `1`, and `2`. That means the later restore branch is no longer gated
|
|
|
|
|
|
by an abstract hidden shell latch; at least one of its adjustment inputs is an explicit UI
|
|
|
|
|
|
launch policy and current evidence still does not show that value being recovered from saved
|
|
|
|
|
|
state. 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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
bytes `[world+0x66de]` and `[world+0x66f2]`, and restores the selected year/profile lane through
|
|
|
|
|
|
a tighter two-stage calendar path than before. Current local disassembly now shows the raw saved
|
|
|
|
|
|
lane at `[profile+0x77]` first feeding helper `0x0051d3f0` with constant components
|
|
|
|
|
|
`(month=1, day=1, subphase=0, tick=0)`, which writes the resulting Jan-1-style tuple dwords
|
|
|
|
|
|
into `[world+0x05/+0x09]`. Only after that seed does the same lane enter one mode-sensitive
|
|
|
|
|
|
adjustment branch: non-editor startup mode can decrement the lane by `1` or `3` depending on
|
|
|
|
|
|
shell-state editor gate `[0x006cec74+0x68]`, shell-side selected-year-adjust policy
|
|
|
|
|
|
`[0x006cec74+0x178]`, and the saved special-condition slot `[0x006cec78+0x4af7]`, and only that
|
|
|
|
|
|
adjusted lane then feeds
|
|
|
|
|
|
helper `0x0051d390` before `world_set_selected_year_and_refresh_calendar_presentation_state`
|
|
|
|
|
|
`0x00409e80` stores the final absolute counter into `[world+0x15]` and refreshes
|
|
|
|
|
|
`[world+0x0d/+0x11]`. That means the restore no longer reads as a direct
|
|
|
|
|
|
`[profile+0x77] -> [world+0x15]` copy; the raw lane seeds the tuple immediately, but the final
|
|
|
|
|
|
absolute-counter restore still depends on live shell/startup context. That dependency is tighter
|
|
|
|
|
|
now too: current local evidence shows `[shell+0x178] == 1` decrementing the restored lane by
|
|
|
|
|
|
`1`, `[shell+0x178] == 2` subtracting `3`, and otherwise a nonzero
|
|
|
|
|
|
`[0x006cec78+0x4af7]` supplying the fallback `-1` branch. That field is no longer unresolved:
|
|
|
|
|
|
current local disassembly now shows `0x00436d10` bulk-zeroing the dword table rooted at
|
|
|
|
|
|
`[startup+0x4a7f]` through `rep stos`, which includes `+0x4af7`, while the editor-side special
|
|
|
|
|
|
conditions owner at `0x004cb2b0/0x004cb8e0` counts and commits that same 49-entry table from the
|
|
|
|
|
|
static rule descriptors at `0x005f3ab0`. The `.smp` save or restore family now grounds that live
|
|
|
|
|
|
band directly too: `world_runtime_serialize_smp_bundle` `0x00446240` writes `49` dwords from
|
|
|
|
|
|
`[world+0x4a7f..+0x4b3f]` plus one trailing scalar at `[world+0x4b43]`, and
|
|
|
|
|
|
`world_load_saved_runtime_state_bundle` `0x00446d40` restores the same fixed `0xc8`-byte band
|
|
|
|
|
|
symmetrically. Slot `30` in that table is localized pair `3722/3723`
|
|
|
|
|
|
`Disable Cargo Economy`, so `+0x4af7` now reads as the live copy of that saved scenario rule,
|
|
|
|
|
|
not a startup-runtime-only mystery latch. The neighboring fixed reads line up with the same rule
|
|
|
|
|
|
cluster too: `+0x4aef` is slot `28` `Completely Disable Money-Related Things`, `+0x4af3` is slot
|
|
|
|
|
|
`29` `Use Bio-Accelerator Cars`, `+0x4afb` is slot `31` `Use Wartime Cargos`, `+0x4aff` is slot
|
|
|
|
|
|
`32` `Disable Train Crashes`, `+0x4b03` is slot `33` `Disable Train Crashes AND Breakdowns`, and
|
|
|
|
|
|
`+0x4b07` is slot `34` `AI Ignore Territories At Startup`. So the remaining loader gap is
|
|
|
|
|
|
narrower than before: the restore still depends on live shell policy `[shell+0x178]`, but the
|
|
|
|
|
|
`+0x4af7` input itself is save-derived scenario rule data rather than something that requires
|
|
|
|
|
|
runtime tracing to discover. Its read-side family is no longer isolated to the selected-year
|
|
|
|
|
|
restore either: it also shapes the chunk size in
|
|
|
|
|
|
`simulation_run_chunked_fast_forward_burst` `0x00437b20` and appears in candidate/local-service
|
|
|
|
|
|
selection and station-detail-side scoring branches around `0x0047f910`, `0x00410d87`, and
|
|
|
|
|
|
`0x005069c6`, which now makes this whole slot cluster look like broader runtime consumers of
|
|
|
|
|
|
scenario special conditions rather than one startup-only mode enum. One file-side anchor is now
|
|
|
|
|
|
tighter too: the checked classic and 1.05 `gmp/gms/gmx` corpus does expose the same aligned
|
|
|
|
|
|
`0x0d64..0x0e2c` `50`-dword band as the grounded `.smp` runtime save or restore copy into
|
|
|
|
|
|
`[world+0x4a7f..+0x4b43]`, but most checked file families only populate a sparse subset of that
|
|
|
|
|
|
band. The first `36` dwords still behave like the older inferred fixed rule matrix with hidden
|
|
|
|
|
|
slot `35` fixed to sentinel value `1`, while the trailing `13` unlabeled rule lanes plus one
|
|
|
|
|
|
scalar vary much more selectively by file family. Current local corpus scans make that split
|
|
|
|
|
|
concrete: the grounded 1.05 scenario-save family (`p.gms`, `q.gms`) stably lights lanes
|
|
|
|
|
|
`35, 37, 39, 44, 45, 46, 47, 48`; the base 1.05 save family (`Autosave.gms`, `nom.gms`) only
|
|
|
|
|
|
shares lane `35` stably and otherwise varies sparsely through `42`, `45`, and `47`; the checked
|
|
|
|
|
|
grounded 1.05 maps, the lone 1.05 alt save, and the visible sandbox-family `.gmx` files keep
|
|
|
|
|
|
only the sentinel lane `35` nonzero. So the current loader boundary is narrower than before: the
|
|
|
|
|
|
`.smp` path still gives a grounded direct runtime-band restore, and checked `gmp/gms/gmx` files
|
|
|
|
|
|
now show a partially populated projection of that same aligned band rather than a wholly separate
|
|
|
|
|
|
fixed record family. The overlap against the later scalar window is now explicit too: trailing
|
|
|
|
|
|
band indices `36..49` are byte-identical with post-window offsets `0x00..0x34`, so every nonzero
|
|
|
|
|
|
lane in that prefix of the post-sentinel scalar window is also a nonzero lane in the aligned
|
|
|
|
|
|
runtime-rule band. That means the real “other fields” boundary inside the post-sentinel window
|
|
|
|
|
|
starts only at `0x0e2c`: `0x0df4..0x0e2c` is the aligned-band overlap prefix, while
|
|
|
|
|
|
`0x0e2c..0x0f30` is the later tail that still looks like save-side scalar state. Local corpus
|
|
|
|
|
|
scans now make that tail split more specific. The base 1.05 save family
|
|
|
|
|
|
(`Autosave.gms`, `nom.gms`) shares a stable tail subset at relative offsets
|
|
|
|
|
|
`0xb4`, `0xc0`, `0xe0`, `0xfc`, and `0x100`, with additional per-file lanes around them. The
|
|
|
|
|
|
1.05 scenario-save family (`p.gms`, `q.gms`) has a much denser stable tail covering
|
|
|
|
|
|
`0x08`, `0x0c`, `0x10`, `0x14`, `0x20`, `0x24`, `0x28`, `0x30`, `0x34`, `0x3c`, `0x5c`,
|
|
|
|
|
|
`0x6c`, `0xa0`, `0xa8`, `0xbc`, `0xc0`, `0xc4`, `0xc8`, `0xcc`, `0xdc`, `0xe0`, `0xe4`,
|
|
|
|
|
|
`0xe8`, `0xf4`, `0xf8`, and `0xfc`; those values still differ per save, but the occupancy is
|
|
|
|
|
|
stable. The lone 1.05 alt save (`g.gms`) only lights `0x20`, `0x34`, `0xf0`, and `0xf4`.
|
|
|
|
|
|
Grounded map families and classic saves keep the tail zeroed, while the only current map-side
|
|
|
|
|
|
outlier remains `Tutorial_2.gmp` under the broad unknown map-family bucket. The immediately
|
|
|
|
|
|
following fixed file window at `0x0df4..0x0f30` is now bounded separately as well: checked maps
|
|
|
|
|
|
and classic saves leave that whole post-sentinel band zeroed,
|
|
|
|
|
|
while checked 1.05 saves carry sparse nonzero dwords there, many of which decode cleanly as
|
|
|
|
|
|
normal little-endian `f32` values. That makes the adjacent band look like a 1.05 save-only
|
|
|
|
|
|
runtime band rather than scenario-static payload, even though its semantics are still open.
|
|
|
|
|
|
One numeric alignment inside that band is now exact too: the tail start `0x0e2c` is the same
|
|
|
|
|
|
relative distance from the aligned runtime-rule base `0x0d64` as live object offset `+0x4b47`
|
|
|
|
|
|
is from grounded world-rule base `[world+0x4a7f]`, so the bounded tail window
|
|
|
|
|
|
`0x0e2c..0x0f30` is offset-aligned with live bytes `[world+0x4b47..+0x4c4b]`. The first
|
|
|
|
|
|
grounded live field at that boundary is no longer anonymous. `0x004367c0` sets one outcome mode
|
|
|
|
|
|
in `[world+0x4a73]`, zeros `[world+0x4d]`, snapshots the selected-year lane to `[world+0x4c88]`,
|
|
|
|
|
|
and then copies localized id `2923` `You lose.` or `2924` `You win, cheater...` into
|
|
|
|
|
|
`[world+0x4b47]`; `0x00472dd0` formats localized id `3918` `%1 has won the game!` with one live
|
|
|
|
|
|
profile name and writes that string into the same destination; and one compact runtime-effect
|
|
|
|
|
|
branch inside `world_apply_compact_runtime_effect_record_to_resolved_targets` `0x00431b20` resets
|
|
|
|
|
|
the same destination to the fixed placeholder token at `0x005c87a8`. That gives a grounded live
|
|
|
|
|
|
interpretation for the start of the tail: `[world+0x4b47]` is the start of a victory or outcome
|
|
|
|
|
|
status-text buffer, not a float lane. The same evidence also gives a useful caution: those live
|
|
|
|
|
|
helpers copy up to `0x12c` bytes into `[world+0x4b47..+0x4c73]`, so the current bounded file-tail
|
|
|
|
|
|
window `0x0e2c..0x0f30` cuts through the first `0x104` bytes of a grounded text field rather
|
|
|
|
|
|
than ending on a clean live-field boundary. One small continuation probe now tightens that edge:
|
|
|
|
|
|
the remaining file window `0x0f30..0x0f58` is exactly the last `0x28` bytes needed to reach the
|
|
|
|
|
|
clean live-field boundary at `[world+0x4c73]`, and checked 1.05 saves still carry sparse nonzero
|
|
|
|
|
|
bytes in that continuation window rather than a trailing text-looking suffix. Checked 1.05 save
|
|
|
|
|
|
bytes in the aligned region therefore still do not resemble preserved text; they stay mostly zero
|
|
|
|
|
|
at the beginning and many nonzero lanes decode as ordinary `f32` values. So the safest current
|
|
|
|
|
|
note is: the tail is offset-aligned with the live object beyond `+0x4b43`, but it is not yet a
|
|
|
|
|
|
validated byte-for-byte mirror of the live `[world+0x4b47]` status-text buffer, and the current
|
|
|
|
|
|
nonzero save-side content continues right up to the first clean field edge at `0x0f58`. The next
|
|
|
|
|
|
exact grounded fields after that edge are byte lanes, not restored dwords: `0x0f59` maps to
|
|
|
|
|
|
`[world+0x4c74]` `Auto-Show Grade During Track Lay`, `0x0f5d` maps to `[world+0x4c78]`
|
|
|
|
|
|
`Starting Building Density Level`, `0x0f61` maps to `[world+0x4c7c]` `Building Density Growth`,
|
|
|
|
|
|
`0x0f65` maps to grounded dword `[world+0x4c80]` `leftover simulation time accumulator`, and
|
|
|
|
|
|
`0x0f6d` maps to byte `[world+0x4c88]` `selected-year lane snapshot`. The first later grounded
|
|
|
|
|
|
dword after that is `[world+0x4c8c]` at `0x0f71`. That means the simple 4-byte file-lane model
|
|
|
|
|
|
stops matching grounded live field boundaries immediately after the text-buffer edge: the post-
|
|
|
|
|
|
`0x0f58` file bytes are still offset-correlated to live state, but they are no longer naturally
|
|
|
|
|
|
dword-aligned with the next grounded object fields. The new byte-neighborhood probe makes the
|
|
|
|
|
|
mismatch more concrete. In checked 1.05 scenario saves, the exact grounded byte offsets
|
|
|
|
|
|
themselves do not look like clean selector values: `p.gms` carries `0x33` at `0x0f5d` and `0x8c`
|
|
|
|
|
|
at `0x0f6d`, while `q.gms` carries `0xcc` and `0xba` at those same offsets. The only clean
|
|
|
|
|
|
float-looking starts in that neighborhood instead appear one byte earlier, at `0x0f5c` and
|
|
|
|
|
|
`0x0f6c`: `p.gms` decodes those as roughly `7.6` and `6.0172`, while `q.gms` decodes them as
|
|
|
|
|
|
roughly `23.6` and `44.6824`. That tightens the current read further: the checked save bytes
|
|
|
|
|
|
remain offset-correlated to the live `[world+0x4c74..+0x4c8c]` neighborhood, but they are still
|
|
|
|
|
|
not a validated byte-for-byte mirror of the exact live field layout. Local
|
|
|
|
|
|
A second byte-oriented neighborhood immediately after that now has the same kind of split rather
|
|
|
|
|
|
than a clean restored-field mirror. The earlier grounded anchors in that band all stay zero in
|
|
|
|
|
|
the checked 1.05 saves: exact file offset `0x0f87` maps to selected-year bucket companion scalar
|
|
|
|
|
|
`[world+0x4ca2]`, while `0x0f93` and `0x0f97` map to the two startup-dispatch reset-owned bands
|
|
|
|
|
|
`[world+0x4cae]` and `[world+0x4cb2]`, and the local corpus leaves all three exact dword starts
|
|
|
|
|
|
zeroed. The same is true for the later exact byte-owned policy lanes: file offsets `0x0f78`,
|
|
|
|
|
|
`0x0f7c`, `0x0f7d`, and `0x0f7e` map cleanly to grounded byte fields `[world+0x4c93]` and
|
|
|
|
|
|
`[world+0x4c97..+0x4c99]`: the linked-site removal follow-on gate plus the three editor
|
|
|
|
|
|
locomotives-page policy bytes `All Steam Locos Avail.`, `All Diesel Locos Avail.`, and `All
|
|
|
|
|
|
Electric Locos Avail.`. In the checked 1.05 save corpus those four exact byte lanes all stay
|
|
|
|
|
|
`0`, which is at least structurally clean. The later grounded dword fields in the same
|
|
|
|
|
|
neighborhood are less direct again. Exact file offset `0x0f9f` maps to `[world+0x4cba]` (the
|
|
|
|
|
|
station-list selected-station mirror) and exact offset `0x0fa3` maps to cached
|
|
|
|
|
|
available-locomotive rating `[world+0x4cbe]`, but the checked save bytes at those exact dword
|
|
|
|
|
|
starts do not look like clean preserved ids or floats. The only stable float-looking starts sit
|
|
|
|
|
|
three bytes earlier, at `0x0f9c` and `0x0fa0`: `p.gms` yields roughly `96.8754` and `186.4795`,
|
|
|
|
|
|
`q.gms` yields `329.9467` and the same `0x0fa0`-side candidate shape, `g.gms` yields `7.0` and
|
|
|
|
|
|
`95.8507`, and `Autosave.gms` only shows the later `0x0fa0` candidate at about `68.2629`. So
|
|
|
|
|
|
this later band now has the same conservative read as the post-text one: the save bytes are
|
|
|
|
|
|
still offset-correlated to grounded live fields, but the exact live byte or dword layout is not
|
|
|
|
|
|
yet validated as a direct on-disk mirror.
|
|
|
|
|
|
One more structural cut is now grounded beyond that neighborhood. The aligned scalar plateau
|
|
|
|
|
|
`0x0fa7..0x0fe7` ends exactly at the later recipe-book root `[world+0x0fe7]` already grounded in
|
|
|
|
|
|
the port-or-warehouse cargo editor and runtime rebuild path. We still do not have live semantic
|
|
|
|
|
|
names for the plateau itself, but its aligned dword run now splits cleanly by save family. The
|
|
|
|
|
|
base 1.05 saves (`Autosave.gms`, `nom.gms`) carry one stable signature with
|
|
|
|
|
|
`0x0faf = 0x8000003f`, `0x0fb3 = 0x75c28f3f`, repeated `0x75c28f3c` lanes through `0x0fbf`, a
|
|
|
|
|
|
sign-flipped lane `0x0fc3 = 0xa3d70a3c`, one tiny marker at `0x0fc7 = 0x0000003b`, and
|
|
|
|
|
|
`0x0fcb = 0x00300000`. The scenario-save family (`p.gms`, `q.gms`) carries a different stable
|
|
|
|
|
|
plateau over the same offsets, beginning `0x0faf = 0x4000003f`, `0x0fb3 = 0xe560423f`, then
|
|
|
|
|
|
`0x03126f3b`, `0x1374bc3c`, and paired `0x23d70a3c` lanes at `0x0fbf/0x0fc3`, with
|
|
|
|
|
|
`0x0fc7 = 0x0000003c`. The alt-save family (`g.gms`) follows the base signature through
|
|
|
|
|
|
`0x0fc7`, then diverges sharply into the same `0xcdcdcd..` fill pattern already seen in its
|
|
|
|
|
|
earlier header lanes. So the current best fit for `0x0fa7..0x0fe7` is a family-shaped aligned
|
|
|
|
|
|
scalar plateau that belongs to save-side runtime state and terminates immediately before the
|
|
|
|
|
|
grounded recipe-book block, not one more directly named live-field mirror. One conservative
|
|
|
|
|
|
loader-side summary probe now starts exactly at that recipe root instead of extending the plateau
|
|
|
|
|
|
model further. The fixed recipe-book block spans twelve books from `0x0fe7` with stride `0x4e1`,
|
|
|
|
|
|
and the checked map/save pairs `Alternate USA.gmp -> Autosave.gms`, `Southern Pacific.gmp ->
|
|
|
|
|
|
p.gms`, and `Spanish Mainline.gmp -> g.gms` preserve that rooted block byte-for-byte in the
|
|
|
|
|
|
sampled local corpus. The current probe therefore treats it as preserved scenario payload rather
|
|
|
|
|
|
than save-only runtime drift and only reports per-book signatures: a coarse head kind over the
|
|
|
|
|
|
pre-line region, the raw `book+0x3ed` annual-production dword, and one raw summary for each of
|
|
|
|
|
|
the five fixed `0x30`-byte cargo lines beginning at `book+0x3f1`: coarse line kind, raw mode
|
|
|
|
|
|
dword, raw annual-amount dword, and the raw supplied/demanded cargo-token dwords at `+0x08` and
|
|
|
|
|
|
`+0x1c`. That is enough to separate zero, `0xcd`-filled, and mixed books or lines without
|
|
|
|
|
|
overstating line semantics beyond the grounded editor/runtime ownership already documented below.
|
|
|
|
|
|
Local
|
|
|
|
|
|
corpus clustering now makes the remaining split more specific. The base 1.05 save family
|
|
|
|
|
|
(`Autosave.gms`, `nom.gms`) shares a narrow tail-heavy subset with stable relative offsets
|
|
|
|
|
|
`0xec`, `0xf8`, `0x118`, `0x134`, and `0x138`, while still varying in value across files. The
|
|
|
|
|
|
1.05 scenario-save family (`p.gms`, `q.gms`) shares a much broader stable set spanning almost the
|
|
|
|
|
|
whole window from `0x04` through `0x134`, again with per-file scalar differences but consistent
|
|
|
|
|
|
occupancy. Pairwise compare runs tighten that read further: `Autosave.gms` vs `nom.gms` does not
|
|
|
|
|
|
preserve one common numeric tail signature even at the shared base-save offsets, and `p.gms` vs
|
|
|
|
|
|
`q.gms` keeps the broad scenario-save occupancy pattern but still changes every shared value, with
|
|
|
|
|
|
`q.gms` additionally lighting two extra lanes at `0x78` and `0x84`. So the current best fit is
|
|
|
|
|
|
“family-shaped live scalar state” rather than family-default constants. The lone 1.05 alt-save
|
|
|
|
|
|
sample (`g.gms`) only lights up four lanes at `0x58`, `0x6c`,
|
|
|
|
|
|
`0x128`, and `0x12c`. The checked 1.05 maps and classic saves stay zero in that same bounded
|
|
|
|
|
|
window, which strengthens the current read that this is runtime-save scalar state rather than
|
|
|
|
|
|
generic map payload. One older unknown map-family outlier in the local corpus does still carry a
|
|
|
|
|
|
populated window: `Tutorial_2.gmp` under the classic install tree. So the safest current note is
|
|
|
|
|
|
“zero for grounded map families and classic save families, nonzero for observed 1.05 save
|
|
|
|
|
|
families, with one older unknown-map exception.” Static consumer grounding is still sparse for
|
|
|
|
|
|
that tail: direct object-offset hits currently only name the trailing scalar `[world+0x4b43]`
|
|
|
|
|
|
through the editor panel and `.smp` save or restore family, while local opcode searches do not
|
|
|
|
|
|
yet surface equally direct reads for the intervening `+0x4b0b..+0x4b3f` tail lanes. So the
|
|
|
|
|
|
save-file family clustering is now strong, but those later tail scalars remain structurally
|
|
|
|
|
|
bounded rather than semantically named. The
|
|
|
|
|
|
same branch is no longer world-entry-only either: current local
|
|
|
|
|
|
disassembly now shows the identical lane-adjust and
|
|
|
|
|
|
`0x51d3f0 -> 0x51d390 -> 0x409e80` sequence in the post-fast-forward selected-year tail at
|
|
|
|
|
|
`0x004370e0`, which lines up with the existing post-fast-forward callers already mapped under
|
|
|
|
|
|
`0x00433bd0`, `0x00435603`, `0x0041e970`, and `0x00436af0`. That restore now
|
|
|
|
|
|
also has some neighboring slot semantics bounded well enough to carry in the loader notes. Slot
|
|
|
|
|
|
`31` `[0x006cec78+0x4afb]` is no longer best read as an unnamed runtime cargo-economy latch:
|
|
|
|
|
|
local disassembly now ties it directly to the saved special-condition table entry `Use Wartime
|
|
|
|
|
|
Cargos`, and the strongest current runtime owner is
|
|
|
|
|
|
`structure_candidate_collection_refresh_cargo_economy_filter_flags` `0x0041eac0`. Inside that
|
|
|
|
|
|
candidate-collection sweep the branch at `0x0041ed37` only activates when slot `31` is set and
|
|
|
|
|
|
then treats the string family `Clothing`, `Cheese`, `Meat`, `Ammunition`, `Weapons`, and
|
|
|
|
|
|
`Diesel` as one special cargo set before writing the live candidate filter byte `[entry+0x56]`.
|
|
|
|
|
|
That makes the old read-side note around `0x00412560` tighter too: the neighboring descriptor
|
|
|
|
|
|
gate is now best understood as using the live copy of the `Use Wartime Cargos` scenario rule,
|
|
|
|
|
|
not an anonymous cargo-economy mode byte. Slot `34` `[0x006cec78+0x4b07]` is similarly bounded
|
|
|
|
|
|
on the runtime side: the wrapper at `0x004013f0`, which sits immediately above the broader
|
|
|
|
|
|
company-start or city-connection chooser `0x00404ce0`, snapshots region dword `[entry+0x2d]`
|
|
|
|
|
|
across all `0x18` live region records in `0x006cfc9c`, zeros that field while the chooser runs,
|
|
|
|
|
|
and then restores the original values on exit. That is a strong current fit for the editor rule
|
|
|
|
|
|
`AI Ignore Territories At Startup`, even though the exact meaning of region field `+0x2d`
|
|
|
|
|
|
remains open. Slot `29` `[0x006cec78+0x4af3]` is less semantically tidy but still worth carrying
|
|
|
|
|
|
as a bounded consumer family: the branch at `0x0041d286` activates one later placed-structure or
|
|
|
|
|
|
building-side scoring path only when that slot is nonzero and the linked candidate or era record
|
|
|
|
|
|
at `[entry+0x41]` equals `5`, while two already-grounded world helpers
|
|
|
|
|
|
`world_scan_secondary_grid_marked_cell_bounds` `0x0044ce60` and
|
|
|
|
|
|
`world_service_secondary_grid_marked_cell_overlay_cache` `0x0044c670` also gate on the same
|
|
|
|
|
|
slot. So the identity of slot `29` as saved rule data is grounded, but the downstream runtime
|
|
|
|
|
|
semantics are still mixed enough that the loader should preserve the raw value without trying to
|
|
|
|
|
|
rename its whole consumer family yet. The neighboring train-safety slots are now bounded enough
|
|
|
|
|
|
to keep as a cautious runtime split too. Slot `33` `[0x006cec78+0x4b03]`
|
|
|
|
|
|
`Disable Train Crashes AND Breakdowns` is the coarse gate in the currently recovered train-side
|
|
|
|
|
|
deterioration family around `0x004af8a0`: the very first branch at `0x004af8ab` jumps straight
|
|
|
|
|
|
to the function tail when the slot is set, bypassing the year-scaled threshold build, the later
|
|
|
|
|
|
random or threshold comparison, and the two follow-on state transitions at `0x004ad7a0` and
|
|
|
|
|
|
`0x004ada00`. Slot `32` `[0x006cec78+0x4aff]` `Disable Train Crashes` is narrower in the same
|
|
|
|
|
|
family: after the threshold path has already run, the branch at `0x004af9c1` uses slot `32` to
|
|
|
|
|
|
suppress only the lower failure-transition path and force the milder follow-on at `0x004ada00`.
|
|
|
|
|
|
That same slot-`33` read also appears in the smaller train-side scalar query at `0x004ac460`,
|
|
|
|
|
|
where setting it returns one fixed float immediately before the ordinary route-object-dependent
|
|
|
|
|
|
calculation runs. So the current best loader-facing read is: slot `33` is the broad train
|
|
|
|
|
|
deterioration bypass, slot `32` is the narrower crash-only branch inside that same family, but
|
|
|
|
|
|
the exact player-facing names of the two unnamed train helpers still need one more naming pass.
|
|
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
live grid cell. The small secondary-raster premark helper `0x0044c570` is bounded now too: it
|
|
|
|
|
|
only admits cells whose current raster byte has no bits in mask `0x3e` and whose parallel class
|
|
|
|
|
|
query `0x00534e10` is false, then rewrites that masked class field to `0x02` and widens the same
|
|
|
|
|
|
cached bounds-and-count band `[world+0x21c6..+0x21d6]`. The next helper `0x0044ce60` scans the secondary raster at `[world+0x2135]`
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
marked-bit query over the same 3-byte cell family, and the nearby local counter `0x0044bdb0`
|
|
|
|
|
|
is now bounded as the 8-neighbor count companion for that same `2/4/5` subset, walking the
|
|
|
|
|
|
shared `0x00624b28/0x00624b48` offset tables and re-entering `0x00534ec0` on each bounded
|
|
|
|
|
|
neighbor cell. The first caller cluster around `0x0044bf9d..0x0044c37b` therefore reads as a
|
|
|
|
|
|
secondary-raster neighborhood service band rather than a generic map scan.
|
2026-04-08 16:31:33 -07:00
|
|
|
|
`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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
component-walk owner under the same neighborhood band is tighter now too: `0x0044c200`
|
|
|
|
|
|
allocates a temporary `width*height` visit bitmap at `0x0062c128`, seeds one class-`2/4/5`
|
|
|
|
|
|
starting cell, derives an initial direction index through the remap table `0x005ee5d4`, and then
|
|
|
|
|
|
fans into the deeper recursive walker `0x0044be20`. That deeper walker widens dirty bounds
|
|
|
|
|
|
`[world+0x21ad..+0x21b9]`, stamps one companion-word orientation lane through `0x005ee5cc`,
|
|
|
|
|
|
reuses `0x00534ec0` plus `0x0044bdb0` to filter admissible neighbors, tracks temporary
|
|
|
|
|
|
visitation in `0x0062c128`, and then applies the local byte-1 edge-bit `0x04/0x08` updates
|
|
|
|
|
|
before returning. So the `0x0044bf9d..0x0044c422` cluster now reads as a real connected-component
|
|
|
|
|
|
walk plus edge-flag refresh layer over the secondary raster rather than only a loose group of
|
|
|
|
|
|
local neighbor counters. The adjacent mutation strip is tighter now too: `0x0044dcf0` refreshes
|
|
|
|
|
|
companion-word bit `0x200` in one local rectangle by checking whether any neighbor belongs to
|
|
|
|
|
|
class set `2/4/5`, while `0x0044df10` clears three local sidecar byte planes, demotes class `4`
|
|
|
|
|
|
to `1` and class `5` to `3`, and then reruns that marked-bit refresh over the surrounding
|
|
|
|
|
|
`+/-1` window. One level up, `0x0044e500` is the rect-wide owner that recomputes byte-1 edge
|
|
|
|
|
|
bits `0x04/0x08` for class-`2/4/5` cells, dispatches `0x0044df10` on incompatible local
|
|
|
|
|
|
patterns, and finally consumes the pending global seed pair at `[0x006d1304+0x78/+0x7c]`
|
|
|
|
|
|
through `0x0044c200`. The shell-side owner of that pending pair is tighter now too:
|
|
|
|
|
|
`[0x006d1304]` is the live `PaintTerrain.win` shell singleton while the callback-heavy side also
|
|
|
|
|
|
keeps a second rooted pointer at `0x006d1334`; the tool constructor snapshots the broader
|
|
|
|
|
|
terrain-paint state into both families while the world-side raster owner still only consumes
|
|
|
|
|
|
`[0x006d1304+0x78/+0x7c]` as one pending component-seed pair. Its radial sibling `0x0044e7d0`
|
|
|
|
|
|
is narrower:
|
|
|
|
|
|
after validating world-space
|
|
|
|
|
|
coordinates through `0x00414bd0`, it stamps class-`2` marks into the secondary raster by walking
|
|
|
|
|
|
one clamped bounding box and admitting cells only when the radial falloff helper `0x0051db80`
|
|
|
|
|
|
stays positive before re-entering `0x0044c570`. The two small support predicates under that same
|
|
|
|
|
|
strip are now explicit too: `0x00414bd0` is the float grid-bounds gate, and `0x00449df0` is the
|
|
|
|
|
|
integer rectangle clamp-and-validity helper shared by the local mutation owners. One level up,
|
|
|
|
|
|
the broader rect-scoped owner is tighter now too:
|
|
|
|
|
|
`world_rebuild_secondary_raster_derived_surface_and_companion_planes_in_rect` `0x0044e940`
|
|
|
|
|
|
first reclamps the caller rectangle through `0x00449df0`, reruns the local edge-refresh owner
|
|
|
|
|
|
`0x0044e500`, lazily ensures one presentation target through
|
|
|
|
|
|
`0x0051f090/0x00534910/0x00534920/0x00534930`, and then resolves scaled target dimensions
|
|
|
|
|
|
through `0x00534c50` before allocating one temporary `width*height` mask. Its main scan then
|
|
|
|
|
|
walks the live secondary raster `[world+0x165d]` through the same class predicates
|
|
|
|
|
|
`0x00534e10/0x00534e50/0x00534f00/0x00534ec0`: class-`1/3/4/5` cells force `0xff` into the four
|
|
|
|
|
|
sidecar byte planes `[world+0x1631..+0x163d]`, while the broader per-cell pass writes packed
|
|
|
|
|
|
values into the ensured target through `0x00534730` and also updates nibble lanes at byte offsets
|
|
|
|
|
|
`+0x2` and `+0x5` inside the same three-byte secondary-raster cell family. After the publish it
|
|
|
|
|
|
notifies the shell owner at `0x0062be68`, re-enters `0x00449f80` and `0x004881b0`, frees the
|
|
|
|
|
|
temporary mask, expands the caller rectangle by dirty bounds `[world+0x21ad..+0x21b9]` through
|
|
|
|
|
|
`0x00536710`, and finally seeds companion byte `[world+0x162d]` with `0xc4` on cells selected
|
|
|
|
|
|
from mask plane `[world+0x1655]`. So the `0x0044e500 -> 0x0044e940` band is now a real
|
|
|
|
|
|
derived-surface and companion-plane rebuild family rather than only a loose collection of local
|
|
|
|
|
|
raster mutations. The local
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
`[world+0x165d]`, then seeds those planes with the same `0x02/0x01/0x00` default split. The
|
|
|
|
|
|
load-side owner for those same planes is tighter now too: the constructor thunk `0x0044e910`
|
|
|
|
|
|
immediately feeds the heavier payload body `0x0044cfb0`, which reads the rooted chunk families
|
|
|
|
|
|
`0x2ee2/0x2ee3/0x2ef4/0x2ef5/0x2ef6/0x2ee4/0x2ee5/0x2f43/0x2f44`, allocates the core world-grid
|
|
|
|
|
|
and secondary-raster arrays `[world+0x2129..+0x2141]` plus the route-entry collection
|
|
|
|
|
|
`0x006cfca8`, initializes every grid-cell record through `0x0042ae50`, and only then hands off
|
|
|
|
|
|
into `world_compute_transport_and_pricing_grid` `0x0044fb70`, the neighboring presentation
|
|
|
|
|
|
refresh `0x00449f20`, and the shell-mode pulse `0x00484d70`. So the `0x0044e910 -> 0x0044cfb0`
|
|
|
|
|
|
load side is now bounded as the heavy world-grid and secondary-raster bundle-load body rather
|
|
|
|
|
|
than just another anonymous constructor tail. One
|
2026-04-08 16:31:33 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
copy-industry-data flows. That copy side is tighter now too: the handler first enters
|
|
|
|
|
|
`0x00420e00`, which rebuilds the selected region's profile collection `[region+0x37f]`, clones
|
|
|
|
|
|
the source region's live label-weight entries when a source region is present, and otherwise
|
|
|
|
|
|
reseeds a fixed default weight set through repeated `0x004206b0` calls. The no-source companion
|
|
|
|
|
|
`0x00420ed0` now makes that split explicit: it rebuilds the same subcollection and then picks one
|
|
|
|
|
|
of two fixed default label-weight sets based on region class dword `[region+0x23e]`, using one
|
|
|
|
|
|
all-`0.2f` family for nonzero-class regions and one class-`0` family that starts with `0.3f`
|
|
|
|
|
|
before continuing with repeated `0.2f` entries. The lower mutator
|
|
|
|
|
|
`0x004206b0` then linearly scans the selected region subcollection by profile label, creates a
|
|
|
|
|
|
new profile only when the copied weight is positive, removes an existing profile when the copied
|
|
|
|
|
|
weight is non-positive, updates `[entry+0x1e]` otherwise, and refreshes the region's derived
|
|
|
|
|
|
availability-summary bytes `[region+0x2f6/+0x2fa/+0x2fe]` through `0x00420410`; `map_editor_territory_panel_construct` and
|
|
|
|
|
|
the adjacent no-source companion `0x00420ed0` is tighter too: it rebuilds the same collection
|
|
|
|
|
|
root `[region+0x37f]` but then seeds one of two fixed default profile-label sets directly through
|
|
|
|
|
|
repeated `0x004206b0` calls, splitting by region class dword `[region+0x23e]`. Nonzero-class
|
|
|
|
|
|
regions take one all-`0.2f` default set, while class-0 regions take a different set that starts
|
|
|
|
|
|
with one `0.3f` weight before continuing with repeated `0.2f` entries. That makes `0x420ed0`
|
|
|
|
|
|
the true class-split default-profile companion to the source-cloning helper at `0x420e00`,
|
|
|
|
|
|
rather than just more of the same editor copy path.
|
|
|
|
|
|
`map_editor_territory_panel_construct` and
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
per-cargo runtime summary tables. The current local file-side result is now tighter too: the
|
|
|
|
|
|
grounded recipe-book root at `0x0fe7` is preserved byte-for-byte across the checked map/save
|
|
|
|
|
|
scenario pairs, so the loader can safely treat the twelve `0x4e1`-byte books as preserved
|
|
|
|
|
|
scenario payload rather than a drifting save-only band. A conservative summary probe now only
|
|
|
|
|
|
reports per-book head signatures, raw cap dwords at `+0x3ed`, and five raw line summaries rooted
|
|
|
|
|
|
at `+0x3f1` so the file-side structure stays aligned with this grounded ownership. 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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-10 20:36:44 -07:00
|
|
|
|
requested cargo id. The internal indexing is tighter now too: the direct-supply lane indexes from
|
|
|
|
|
|
`[desc+0x1c]`, the demand or input lane uses that same resolved cargo id in the no-subrow branch,
|
|
|
|
|
|
and the scaled production-output lane indexes each subordinate row directly from
|
|
|
|
|
|
`[desc+0x44+row*0x1c]`, with no post-resolution failure guard between the importer writes and the
|
|
|
|
|
|
summary-bank updates. So the strongest current read is narrower than a full cargo-id decode: if
|
|
|
|
|
|
the imported low-16 marker rows fail the exact matcher at `0x0041e9f0`, the resulting `0` ids
|
|
|
|
|
|
will still hit the first summary-bank bucket inside `0x00412650`, but the semantic meaning of
|
|
|
|
|
|
cargo id `0` itself remains ungrounded. The sibling helper
|
|
|
|
|
|
`structure_candidate_supports_or_references_cargo_id`
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`0x004129d0` then uses those same banks plus the cached cargo-membership arrays to answer whether
|
2026-04-10 20:36:44 -07:00
|
|
|
|
a live candidate materially references a cargo at all. One compare step tighter, the raw line
|
|
|
|
|
|
lanes now split cleanly by scenario family while still preserving map->save identity inside each
|
|
|
|
|
|
checked pair. `Alternate USA.gmp` and `Autosave.gms` match at the raw line-content level: books
|
|
|
|
|
|
`0..4` keep mixed line areas, their `line02` slots carry the recurring nonzero mode words
|
|
|
|
|
|
`0x00110000`, `0x000b0000`, `0x000b0000`, `0x00130000`, and `0x00180000`, and the same `line02`
|
|
|
|
|
|
slots also carry one recurring supplied token `0x000040a0`; the neighboring `line00/line01`
|
|
|
|
|
|
slots carry the same recurring demanded token lanes such as `0x00010000`, `0x72470000`,
|
|
|
|
|
|
`0x6f430000`, `0x694c0000`, and `0x694d0000`. `Southern Pacific.gmp` and `p.gms` also match at
|
|
|
|
|
|
the raw line-content level, but the same rooted line slots stay zero in the checked pair.
|
|
|
|
|
|
`Spanish Mainline.gmp` and `g.gms` again match at the raw line-content level yet carry a distinct
|
|
|
|
|
|
nonzero pattern: books `0..4` keep mixed line areas, line-level supplied tokens include
|
|
|
|
|
|
`0x00170000`, `0x00150000`, `0x00004040`, and `0x00004000`, demanded tokens include
|
|
|
|
|
|
`0x00010000`, `0x68430000`, and `0x6c410000`, and the later `line02` mode words stabilize at
|
|
|
|
|
|
`0x00070000` then `0x00010000`. So the loader can now safely treat these raw line lanes as
|
|
|
|
|
|
preserved scenario payload with family-specific signatures, even though the exact cargo-id and
|
|
|
|
|
|
mode-enum semantics still need separate grounding. One inference is tighter now too: when those
|
|
|
|
|
|
demanded-token words are zero in the low 16 bits and printable in the high 16 bits, the byte
|
|
|
|
|
|
order reads as short two-letter stems such as `Gr`, `Co`, `Li`, `Mi`, `Ch`, and `Al`, which fit
|
|
|
|
|
|
the current scenario cargo-name families `Grain`, `Corn`, `Livestock`, `Milk`, `Cheese`, and
|
|
|
|
|
|
`Alcohol` from `RT3.lng`; the runtime probe now exposes those only as probable ASCII stems, not
|
|
|
|
|
|
as fully grounded cargo-id decodes. The supplied-token side is now bounded as a separate layout
|
|
|
|
|
|
family rather than forced into that same stem model: the `Alternate USA` family uses one stable
|
|
|
|
|
|
low-16 marker `0x000040a0` only in `book00..04.line02`, the `Southern Pacific` family leaves the
|
|
|
|
|
|
supplied lanes zero in the checked pair, and the `Spanish Mainline` family splits between two
|
|
|
|
|
|
high-16 numeric tokens `0x00170000` and `0x00150000` in `book00/01.line01` plus the later low-16
|
|
|
|
|
|
markers `0x00004040`, `0x00004000`, and `0x00004040` in `book02..04.line02`. The probe now
|
|
|
|
|
|
exposes those conservatively as token-layout classes `high16-ascii-stem`, `high16-numeric`, and
|
|
|
|
|
|
`low16-marker`, without claiming that the non-stem numeric or marker families are decoded yet.
|
|
|
|
|
|
One more structural layer is stable enough to name conservatively: the checked nonzero recipe
|
|
|
|
|
|
rows repeatedly fall into four line-signature classes. `Alternate USA` books `0..4` all expose
|
|
|
|
|
|
one `demand-numeric-entry` at `line00` (`0x00010000`), one `demand-stem-entry` at `line01`
|
|
|
|
|
|
(`Gr/Co/Li/Mi` stems), and one `supply-marker-entry` at `line02` (nonzero mode plus
|
|
|
|
|
|
`0x000040a0`). `Spanish Mainline` books `2..4` expose that same three-line pattern with
|
|
|
|
|
|
`Ch/Al` stems and the `0x00004040/0x00004000` marker family, while books `0/1` only expose the
|
|
|
|
|
|
earlier `supply-numeric-entry` form at `line01`. So the loader can now distinguish recurring row
|
|
|
|
|
|
roles such as demand-stem versus supply-marker without pretending the marker payloads themselves
|
|
|
|
|
|
are decoded. The importer-side branch map is tighter now too because local `objdump` over
|
|
|
|
|
|
`0x00435630` shows that only nonzero mode dwords materialize into `0xbc` runtime descriptors:
|
|
|
|
|
|
mode `0` lines are skipped entirely, mode `1` enters the demand-only branch, mode `3` enters the
|
|
|
|
|
|
dual demand-plus-supply branch, and every other nonzero mode falls into the supply-side branch.
|
|
|
|
|
|
In the checked corpus that means the recurring `demand-numeric-entry` and `demand-stem-entry`
|
|
|
|
|
|
rows in `Alternate USA` and `Spanish Mainline` are preserved scenario-side line records but do
|
|
|
|
|
|
not become live runtime descriptors, while the later `supply-marker-entry` rows are the ones that
|
|
|
|
|
|
actually reach the runtime import path. The runtime probe now exposes that directly as
|
|
|
|
|
|
`imports_to_runtime_descriptor` plus one conservative `runtime_import_branch_kind`.
|
|
|
|
|
|
The raw token windows beneath those branch labels are tighter now too. The checked mode-zero
|
|
|
|
|
|
demand rows preserve string-bearing windows at `line+0x1c`, and the current probe shows those
|
|
|
|
|
|
conservatively as prefixed ASCII previews such as `..Grain`, `..Corn`, `..Livestock`, and
|
|
|
|
|
|
`..Milk`; local `objdump` over `0x0041e9f0` then shows the importer feeding the corresponding
|
|
|
|
|
|
token buffer into an exact cargo-name matcher built over the live cargo collection at
|
|
|
|
|
|
`0x0062ba8c`. By contrast, the imported `supply-marker-entry` rows feed nonprintable windows such
|
|
|
|
|
|
as `.@...` from `line+0x08` through that same matcher, and the resolver path currently shows no
|
|
|
|
|
|
special-case decoding for those marker forms. So the strongest static read is now: the stem-like
|
|
|
|
|
|
demand rows preserve cargo-name text but are skipped because their mode is zero, while the
|
|
|
|
|
|
nonzero imported marker rows are the live descriptor inputs yet likely fail exact cargo-name
|
|
|
|
|
|
resolution unless another upstream transform exists.
|
|
|
|
|
|
The wrapper layer above that query no longer looks like a hiding place for special treatment
|
|
|
|
|
|
either. Local `objdump` now shows `0x00412960` simply summing the two supply-side floats returned
|
|
|
|
|
|
by `0x00412650`, while `0x004129a0` returns the single scaled production-output lane directly;
|
|
|
|
|
|
neither wrapper checks for cargo id `0` after the query returns. The broader world-side
|
|
|
|
|
|
accumulator at `0x0041e7be` is tighter in the same way: it calls `0x00412650`, requires all four
|
|
|
|
|
|
returned channels to be positive before continuing, and only then scales those four channel
|
|
|
|
|
|
values by one linked-instance count from `0x00413940` into caller-owned accumulators. So the
|
|
|
|
|
|
current strongest read remains structural rather than semantic: unresolved marker rows can still
|
|
|
|
|
|
propagate through the first bank bucket, but the first place that meaning matters is a normal
|
|
|
|
|
|
positivity-gated cargo-channel consumer, not a dedicated null-cargo branch.
|
|
|
|
|
|
The caller side is narrower than before too. The steady-state site bitset owner
|
|
|
|
|
|
`placed_structure_rebuild_candidate_cargo_service_bitsets` `0x0042c690` starts its inner cargo
|
|
|
|
|
|
loop at id `1` and only walks upward through the current live cargo count, so it never
|
|
|
|
|
|
intentionally queries cargo id `0`. The broader placed-structure sweep around `0x00452e60`
|
|
|
|
|
|
tightens the same boundary from above: when the requested cargo id is nonzero it resolves the
|
|
|
|
|
|
backing candidate and enters `structure_candidate_supports_or_references_cargo_id`, but when the
|
|
|
|
|
|
requested id is exactly `0` it skips that cargo-reference helper entirely and falls through to a
|
|
|
|
|
|
different linked-site or station-or-transit gate. Current local `objdump` now shows that bypass
|
|
|
|
|
|
first requiring one subtype latch through the placed-structure vtable `+0x70`, then resolving the
|
|
|
|
|
|
linked site id at `[site+0x2a8]` through the placed-structure collection `0x006cec20`, and then
|
|
|
|
|
|
forwarding that linked peer into the narrower predicate at `0x0047fd50`. That narrower helper is
|
|
|
|
|
|
tighter than before too: it resolves the linked peer's backing candidate through the peer site id
|
|
|
|
|
|
at `[peer+0x04]`, reads candidate class byte `[candidate+0x8c]`, and returns true only for the
|
|
|
|
|
|
first three class values `0/1/2` while rejecting `3/4` and anything above `4`. So current local
|
|
|
|
|
|
callers treat cargo id `0` as a nonstandard request bound to one linked-site reachability or
|
|
|
|
|
|
classification side path, not one ordinary cargo lane they routinely probe.
|
|
|
|
|
|
The vtable-`+0x70` latch itself is bounded a bit better now too. It still lacks a direct field
|
|
|
|
|
|
decode, but every local callsite we checked around `0x0040d230`, `0x0040dba0`, `0x0040dbf0`, and
|
|
|
|
|
|
`0x0040f670` uses that slot only as the precondition for touching `[site+0x2a8]` and the
|
|
|
|
|
|
neighboring linked-site helpers `0x0047dda0`, `0x0047fd50`, and `0x004138b0`. That makes the
|
|
|
|
|
|
safest current read a linked-site-bearing or linked-site-capable latch, strongly aligned with the
|
|
|
|
|
|
subtype-`1` construction lane that is the only grounded writer of `[site+0x2a8]`, rather than a
|
|
|
|
|
|
broad cargo or route-style predicate.
|
|
|
|
|
|
The neighboring maintenance pair tightens that linked-site read further. Local `objdump` now
|
|
|
|
|
|
shows `0x0040dba0` and `0x0040dbf0` as complementary tiny helpers that each require the same
|
|
|
|
|
|
vtable-`+0x70` latch, resolve optional linked peer id `[site+0x2a8]` through `0x006cec20`,
|
|
|
|
|
|
forward that linked peer (or null) into `0x0047dda0`, and then mirror opposite values into local
|
|
|
|
|
|
byte `[site+0x42]` (`0` for `0x0040dba0`, `1` for `0x0040dbf0`). The route-entry side beneath
|
|
|
|
|
|
them is tighter too: `0x0047dda0` reads route-entry anchor id `[peer+0x08]`, resolves that anchor
|
|
|
|
|
|
through `0x006cfca8`, and only dispatches one of two route-entry vtable calls when
|
|
|
|
|
|
`0x0048a090(1)` reports that all three anchor-slot dwords `[entry+0x206]`, `[entry+0x20a]`, and
|
|
|
|
|
|
`[entry+0x20e]` are still `-1`. So the strongest current read is no longer just "linked-site
|
|
|
|
|
|
capable" in the abstract: this latch consistently fronts the local linked-peer class gate
|
|
|
|
|
|
`0x0047fd50`, the paired `[site+0x42]` route-entry state toggles, and the later linked-peer
|
|
|
|
|
|
collection sweep `0x004138b0`, which keeps the `cargo id 0` bypass path firmly on the
|
|
|
|
|
|
linked-site route-anchor or classification side rather than on an ordinary cargo lane.
|
|
|
|
|
|
One smaller ownership boundary is tighter too: byte `[site+0x42]` is not private scratch owned
|
|
|
|
|
|
only by that pair. Local `objdump` shows tiny direct setters at `0x0040cbc0` and `0x0040cbd0`
|
|
|
|
|
|
plus a raw getter at `0x0040cbf0`, so the `0x0040dba0/0x0040dbf0` pair is writing one shared
|
|
|
|
|
|
placed-structure state byte rather than inventing a route-entry-only scratch lane. One caution
|
|
|
|
|
|
is tighter now too: there are later mode-gated reads at other addresses that also touch a
|
|
|
|
|
|
`+0x42` byte on their own object layouts, but current local evidence does not yet prove those are
|
|
|
|
|
|
the same placed-structure owner family, so the safest current note keeps only the immediate
|
|
|
|
|
|
`0x0040cbc0/0x0040cbd0/0x0040cbf0/0x0040dba0/0x0040dbf0` cluster grounded together.
|
|
|
|
|
|
The vtable side sharpens that split too. A local `.rdata` scan shows `0x0040dba0` and
|
|
|
|
|
|
`0x0040dbf0` each appearing exactly once, together with `0x0040cbf0`, in one method table rooted
|
|
|
|
|
|
at `0x005c8c50`; the constructor chain is tighter now too, because `0x0040c950` installs that
|
|
|
|
|
|
same table as one concrete placed-structure specialization above the common base table
|
|
|
|
|
|
`0x005cb4c0`, while `0x0040c970` tears the same specialization back down to the base before the
|
|
|
|
|
|
temporary object is freed. By contrast the raw set or get trio
|
|
|
|
|
|
`0x0040cbc0/0x0040cbd0/0x0040cbf0` also appears in several sibling tables such as `0x005c9750`
|
|
|
|
|
|
and `0x005dd1f0`. So the strongest current structural read is that byte `[site+0x42]` belongs
|
|
|
|
|
|
to one broader placed-structure virtual interface, while the linked-peer route-entry toggles are
|
|
|
|
|
|
one specialization-specific override pair inside that family rather than generic setters used by
|
|
|
|
|
|
every sibling.
|
|
|
|
|
|
One negative boundary is tighter now too: the
|
|
|
|
|
|
raw token words in this recipe block do not look like the already grounded shell-side
|
|
|
|
|
|
`AnyCargo/AnyFreight/AnyExpress` selector-table ids from `0x00621e04/0x00621e10`, so the current
|
|
|
|
|
|
best read is that these on-disk line tokens are not just copied UI selector ordinals or the same
|
|
|
|
|
|
small express-side id family in another wrapper. One broader collection pass also now ties
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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.
|