rrt/docs/control-loop-atlas/map-and-scenario-content-load.md

522 lines
46 KiB
Markdown

## Map and Scenario Content Load
- 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
`world_entry_transition_and_runtime_bringup` at `0x00443a50`,
`shell_map_file_world_bundle_coordinator` at `0x00445de0`, early reference-package save via
`map_bundle_open_reference_package_and_serialize_early_world_datasets` at `0x00444dd0`, and narrower tagged collection owners such as
`geographic_label_database_refresh_records_from_tagged_bundle` and `city_database_entry_collection_refresh_records_from_tagged_bundle`.
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`,
map-scenario open paths, and scenario-text export batch commands.
- 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`,
`map_bundle_open_reference_package_and_serialize_early_world_datasets`, `geographic_label_database_refresh_records_from_tagged_bundle`,
`city_database_entry_collection_refresh_records_from_tagged_bundle`, `scenario_text_export_build_language_file`,
`scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
- State Anchors: shell-side file staging buffers at `0x0062bee0` and `0x0062bec4`, shell and mode
globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state
allocated through `0x00530c80`, and geography tables rooted at `0x0062b2fc` and `0x0062b268`.
- Subsystem Handoffs: the shared `fileopt.win` dialog rooted at `0x004dc670` now looks like the
shell-side selector above the two broad file coordinators. Its message handler sets `0x006d07f8`
for the load or restore side or `0x006d07ec` for the save or package side before the detail-panel
transition manager routes into `shell_map_file_entry_coordinator` or
`shell_map_file_world_bundle_coordinator`. The former unresolved third flag at `0x006d07f0` is now
accounted for too: it escapes into the standalone `SettingsWindow.win` path through
`shell_open_settings_window` rather than another map or save verb. The broad coordinators now hand
their interactive work through the shared `filerqst.win` helper at `0x004dd010`, and that helper
gives the extension split a firmer shape. The paired editor-map path is now grounded as `.gmp`
through load mode `4` and save mode `3`. The remaining non-editor families are no longer anonymous
either: `.gmc` is the campaign-scenario branch, backed both by the campaign-screen resignation
prompt on `0x006cec7c+0xc5` and by the numbered `%s%02d.gmc` save helper at `0x00517c70`; `.gmx`
is the sandbox branch, backed by the shell-side `The briefing is not available in sandbox games.`
restriction on `0x006cec7c+0x82`; and the default `.gms` branch is therefore the standalone
scenario family. When a live runtime world is already active the same helper bypasses those
non-runtime extensions and forces the `.smp` runtime-state branch instead. The auxiliary save-side
mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another
gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned
by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal
reference-bundle path when that dataset object is absent. That fallback owner is tighter now too:
`0x00444dd0` is not a generic database opener, but the early package-save prelude that seeds the
shared stage/progress globals, opens one `0x30d40` bundle through `0x00530c80`, handles the
localized failure modal `0x0fda`, and then serializes the first direct package band in a fixed
order: chunks `0x32c8/0x32c9`, direct saves of `[world+0x66be]`, `[world+0x66b2]`, and
`[world+0x66b6]`, chunk `0x32dc`, the staged `0x108` profile block under `0x3714/0x3715`, then
`world_serialize_runtime_grid_and_secondary_raster_tables_into_bundle` `0x00449520` for the
early world-grid and sidecar-table band, then
`aux_candidate_collection_serialize_records_into_bundle_payload` `0x00416a70` for the direct
`0x0062b2fc` source-or-auxiliary record family, and only after that the neighboring reference
and manager families before `shell_map_file_world_bundle_coordinator` continues with the later
tagged collections. That `0x0062b2fc` seam is tighter now too: `0x00416a70` first builds one
temporary scored queue from each record's linked chain at `[entry+0x04]`, writes one leading
`0xbabe` header, and then serializes the fixed fields, counted dword runs, optional byte payload,
paired one-byte bands, and trailing dword for each queued record; the load-side companion is
`aux_candidate_collection_construct_stream_load_records_and_refresh_runtime_followons`
`0x004196c0`, which now clearly tails into
`aux_candidate_collection_rebank_or_clone_records_by_availability_pass_and_refresh_owner_links`
`0x00419230` before the later world-load side continues, with destructor
`aux_candidate_collection_release_templates_queues_and_indexed_storage` `0x00419680`. The fixed
tail is explicit now too: `0x00444dd0` writes one direct dword from
`[world+0x19]`, one zeroed `0x1f4`-byte slab under `0x32cf`, closes the package, derives the
preview path through `0x00442740`, and then conditionally emits the companion-image and
companion-payload sidecars through `0x00441f70` and `0x00442900` when `[world+0x66c8]` and
`[world+0x66c9]` are set. The shell-side mode owner above those
file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode
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`, falls through the neighboring no-op placeholder `0x433730`, and then
reaches the common free path before clearing `0x006cec78`. The teardown owner itself is tighter
now too: `0x434300` first destroys the two indexed world collections at `[world+0x66b2]` and
`[world+0x66b6]`, then releases the typed global roots `0x0062b244`, `0x006cfcbc`,
`0x0062be10`, `0x006ceb9c`, `0x0062c120`, `0x0062ba8c`, `0x006ada84`, `0x0062ba88`,
`0x0062b2fc`, `0x0062b268`, `0x006cea4c`, and `0x006acd34` in that order before it drains the
shell-helper handle band `[world+0x46a80..+0x46aa0]`, the variable owner band `[world+0x46aa4]`,
and the linked chains at `[world+0x66a6]` and `[world+0x66aa]`.
The corresponding world-load allocation branch is tighter now too: inside `0x00438c70`,
`0x0062b2fc` is allocated as a `0xb8`-byte object and then constructed through
`aux_candidate_collection_construct_seed_globals_and_helper_bands_then_import_records`
`0x0041aa50`, which seeds the collection base, resets the neighboring constructor globals, builds
the helper bands at `[this+0x88/+0x8c/+0x90]` through `0x0041a990`, and only then tails into the
tagged import owner `0x004196c0`. The next two roots in the same load strip are now explicit
too: `0x0062ba8c` is allocated and constructed through
`structure_candidate_collection_construct_and_stream_load_records_then_refresh_counts`
`0x0041f4e0`, which enters the tagged import owner `0x0041ede0` and then refreshes the aggregate
filter/year-visible counts through `0x0041e970`; and `0x006ada84` is allocated and constructed
through `locomotive_collection_construct_and_stream_load_records` `0x00462520`, which enters the
linked-era locomotive import owner `0x00461f10` and then refreshes the live availability override
band through `0x00461e00`. The same fan-out now also has explicit constructor ownership for the
adjacent typed roots: the live company collection `0x0062be10` is constructed through
`company_collection_construct` `0x00429950`, the live profile/chairman collection `0x006ceb9c`
is constructed through `profile_collection_construct` `0x00477740`, the live train collection
`0x006cfcbc` is constructed through `train_collection_construct` `0x004b2340`, the sibling
indexed collection `0x006acd34` is constructed through
`runtime_object_collection_construct_vtable_5cae10` `0x00455320`, the support family
`0x0062b244` is constructed through
`support_collection_construct_seed_counters_and_clear_large_sideband` `0x0040aeb0`, and the live
world root `0x0062c120` is published through
`world_runtime_construct_root_and_seed_global_0x62c120` `0x0044cf70` before the heavier bundle
load body continues through `0x00449200` and `0x0044cfb0`. The profile-side tagged header
siblings are explicit too: runtime load re-enters
`profile_collection_refresh_tagged_header_counts_from_bundle` `0x00477780`, while package save
later mirrors the same `0x5209/0x520a/0x520b` trio back out through
`profile_collection_serialize_tagged_header_counts_into_bundle` `0x004777e0`. The same tagged
symmetry is now bounded for the live company collection too: world-load refresh re-enters
`company_collection_load_tagged_header_counts_and_refresh_live_records_from_bundle` `0x00429af0`,
while the package-save path mirrors the same `0x61a9/0x61aa/0x61ab` bracket through
`company_collection_serialize_tagged_header_counts_and_save_live_records_into_bundle`
`0x00429b90`, whose per-company callback is currently the no-op stub `0x00424000` while the
load-side per-company follow-on is `company_refresh_post_load_year_clamp_and_runtime_support_fields`
`0x004268e0`. The same tagged symmetry is now bounded for the live train collection too:
world-load refresh re-enters
`train_collection_load_tagged_header_counts_and_refresh_live_records_from_bundle` `0x004b2700`,
while the package-save path mirrors the same `0x5209/0x520a/0x520b` header bracket and per-train
record walk through
`train_collection_serialize_tagged_header_counts_and_save_live_records_into_bundle`
`0x004b27a0`; the per-train payload seam itself is now explicit too, with route-list load through
`train_refresh_tagged_route_list_payload_from_bundle` `0x004a84b0` and route-list save through
`train_serialize_tagged_route_list_payload_into_bundle` `0x004a7030`.
The later world-entry reactivation branch correspondingly uses `(1, esi)` rather than `(1, 0)`.
The 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:
`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
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. The post-publish startup subchain is no longer unresolved on the static side, though.
Direct disassembly of the mode-`4` branch at `0x0048302a..0x004830ca` now closes it completely:
after constructing `LoadScreen.win`, the branch sets shell state `[transition+0x08] = 4`,
chooses one page scalar `110.0f`, `236.0f`, or `5.0f`, writes that page id through
`shell_load_screen_window_set_active_page` `0x004ea710`, allocates one `0x46c40`-byte runtime
object through `0x0053b070`, resets it through
`world_runtime_reset_startup_dispatch_state_bands` `0x004336d0`, stores the result into
`0x006cec78`, and then directly enters
`shell_active_mode_run_profile_startup_and_load_dispatch` `0x00438890` as `(1, 0)` before
publishing the active-mode object back through `0x005389c0`. So the static startup chain after
`LoadScreen.win` publish is now explicit rather than inferred from the earlier hook traces. The
remaining runtime-side question is narrower: why the earlier hook snapshots still showed
`[LoadScreen.win+0x78] == 0` and `0x006cec78 == 0` immediately after transition return even
though the static mode-`4` branch does not leave those fields that way once it reaches the
startup-dispatch path. The internal selector split in `0x438890` is tighter now too:
`[0x006cec7c+0x01]` is a separate seven-way startup selector, not the shell mode id. Values `1`
and `7` load `Tutorial_2.gmp` and `Tutorial_1.gmp`, values `3/5/6` collapse into the same
profile-seeded file-load lane through `0x445ac0([0x006cec7c]+0x11, 4, &out_success)`, value `2`
is a world-root initialization lane
that allocates `0x0062c120` and then forces selector `3`, and value `4` is the setup-side world
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; that callback now clearly sits on top
of the indexed string-table getter `0x0053de00` before handing the selected text span into the
world-anchor marker queue path. Controls `0x0e86` and `0x0e87` do not select more setup roots;
they update the persisted shell-state selector pairs at
`[0x006cec74+0x233/+0x237]` and `[0x006cec74+0x23b/+0x23f]` through `0x00502160` and
`0x005021c0`, then immediately save config through `0x00484910(1)`. The constructor body is
tighter too: it seeds the initial `Setup.win` state by running `0x502910`, `0x502550`, and
`0x502c00(1)` before the user interacts with the window, installs `0x0c80..0x0c86` and
`0x0f6f..0x0f71` as homogeneous button bands, and treats `0x0e88` as one separate special
control with retuned float fields rather than another ordinary launch root. The remaining
optional constructor child `0x0bd0` is tighter now too: it is built from a separate template
block and optional owned heap object before registration, not another hidden setup-root button.
The generic shell helper layer beneath that constructor is tighter now too: `0x53fa50` is the
shared resource-bind and child-list initialization helper, `0x53f830` is the child-control
lookup-by-id helper over the intrusive list at `[this+0x70]`, `0x558130` is the child-control
finalizer that stamps the owner pointer and resolves localized captions before the control goes
live, and `0x53f9c0` is the ordered child-control registration helper used by the optional
`0x0bd0` branch and other shell dialogs.
The submode selector itself is tighter now too because its button-state lane is mostly decoded:
`0xbc5` tracks submode `15`, `0xbc6` tracks `16`, `0xbba` tracks `2`, `0xbbb` tracks `3`,
`0xbc3` tracks `13`, `0xbbc` groups `3/4/5`, `0xbbd` tracks `4`, `0xbbe` tracks `5`, `0xbbf`
groups `6/12/14`, `0xbc0` tracks `7`, `0xbc1` tracks `8`, `0xbc2` tracks `9`, `0xe75` tracks
`10`, and `0xf6e` tracks `17`. RT3.lng and the setup art families now also make several of those
top-level roots read less like anonymous ids and more like real menu panels: submode `1` is the
strongest current landing-panel fit, `2` is the `Single Player` root, `7/8/9` are the
`Editor` / `New Map` / `Load Map` family, `15` is `Extras`, and `17` is `Tutorial`. By
elimination against the separate shell `Credits.win` mode, the remaining top-level `Setup.win`
roots now most safely read as `13 = Multi Player` and `16 = High Scores`.
The file-backed side is tighter too: `0x502c00` now maps submodes exactly as `4 -> dataset 5`,
`9 -> 4`, `6 -> 8`, `10 -> 6`, `12 -> 10`, and `14 -> 9`, so the saved-game-backed family is
no longer one blurred list pane but a small set of stable dataset variants above
`0x4333f0/0x4336a0`.
The file-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.
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.
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.
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
`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 carry the candidate table conservatively as a lower setup-side candidate-table
slab inside the broader setup payload family, even though the current named setup-side consumers
still land only on earlier payload offsets. 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
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
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 neighboring profile helpers are tighter now too: `0x0047bbf0` is the broad default reset for
the staged `0x108`-byte runtime-profile record, clearing the whole record and then reseeding the
visible setup and campaign anchors `[profile+0x77]`, `[profile+0x79]`, `[profile+0x7d]`, the
random-like dword `[profile+0x83]`, the first setup row-marker byte `[profile+0x87]`, and the
file-backed launch or rehydrate latch `[profile+0x97]`; `0x00502c00` is now tighter on the same
slab because its file-backed lane copies the selected row's primary and secondary `0x32`-byte
string bands into `[profile+0x11]` and `[profile+0x44]` while arming presence byte
`[profile+0x10]`; and `0x0047bc50` is the compact scan helper over `[profile+0xc6..+0xd5]` that
returns the first selector byte below `2`, which keeps that band tied to staged campaign progress
or unlock state rather than to the earlier setup-panel payload fields.
The message-dispatch side is tighter now too. The local classifier at `0x004b91d8` routes the
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.
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.
- 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`.
- Direct shell stubs above that coordinator are tighter now too: `0x004408b0` is the ordinary
`Save game` wrapper and forwards the pure zero-flag triplet into
`shell_map_file_world_bundle_coordinator`; `0x004408d0` is the sibling `Quick save` wrapper,
forwarding flag triplet `(0, 1, 0)`. RT3.lng closes the neighboring `shell_map_file_entry_coordinator`
pair in the same way: `0x00441ac0` is `Load game` and `0x00441af0` is `Quick load`, with the
same `(0, 0, 0)` versus `(0, 1, 0)` split above `0x00445ac0`.
- Current Boundary: 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 extension family is now carried conservatively as `.gmp` for the
editor-map pair, `.gms` for the standalone scenario family, `.gmc` for the campaign-scenario
family, `.gmx` for the sandbox family, and `.gmt` for the auxiliary preview-surface branch rather
than another gameplay save family. The higher-value handoff boundary is also closed at the
current evidence level: after this bring-up, long-lived simulation cadence still rendezvous with
the same shell-owned frame path rather than surfacing a detached gameplay-only outer loop.