Close remaining static atlas and export work

This commit is contained in:
Jan Petykiewicz 2026-04-14 17:52:45 -07:00
commit 049ffa6bd8
13 changed files with 842 additions and 338 deletions

View file

@ -18,7 +18,6 @@
`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.
- Current Boundary: current local evidence still carries gameplay entry and later in-engine world
stepping through the same shell-owned outer coordinator family before the shell bundle is
destroyed; no detached gameplay-owned outer loop has surfaced in the local static pass.

View file

@ -70,6 +70,7 @@
plus `GetFileType`.
- 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`.
- Current Boundary: the current startup boundary is now carried at `app_bootstrap_main`. The CRT
shims ahead of it still read as compiler-owned bring-up and import plumbing, and the local static
pass does not expose any earlier nontrivial game-owned callback that seeds globals before that
handoff.

View file

@ -18,7 +18,28 @@ The broader map-editor page owner is now bounded through
the paired overall growth selector whose effects later appear in the city-growth side of the
simulation. `map_editor_city_region_panel_construct` and
`map_editor_city_region_panel_handle_message` own the city-or-region editing lane with rename and
copy-industry-data flows. The adjacent economic tuning page is explicit now too:
copy-industry-data flows. That page is tighter on the save/load seam now too:
`map_editor_city_region_panel_format_selected_region_detail_stats_and_projected_building_rows`
`0x004cc340` is the selected-region detail formatter bound by the constructor, and it is now a
grounded live reader of the same persisted economic tuning band
`[world+0x0be2/+0x0be6/+0x0bea/+0x0bee]`. It formats one current building-count row from
`[region+0x242]`, formats the leading tuning lane through localized id `0x40d`, and then uses
`world_region_accumulate_structure_category_totals` `0x00422900` plus
`world_region_query_projected_structure_count_scalar_by_category` `0x004234e0` to build the
projected/live category rows `0x40e..0x414`. The neighboring callback
`map_editor_city_region_panel_store_selected_region_density_scalar_and_rebuild` `0x004cc930`
writes the selected region density scalar back into `[region+0x25a/+0x25e]` and reconstructs the
page, and the `.smp` restore owner `0x00446d40` now shows the same pair being rehydrated as one
shared density dword on newer bundles or one legacy bucket-to-density lookup on pre-`0x3f3`
bundles, so the `Cities/Regions` page now has one direct live write-side density lane and one
concrete restore-side bridge instead of only the older generic page note.
The broader handler `0x004ce380` is tighter in the same way now too: besides rename and source
chooser flows, it commits one direct callback value into `[region+0x272]`, forwards another
through `0x004204c0` on the selected region, supports two bulk profile-weight sweeps over the
live candidate pool through `0x004206b0` using `0.0` or `1.0`, and uses chooser result `0x3f2`
to re-enter `0x00420e00` for source-region profile cloning before rebuilding the page; the same
direct callback lane now also matches the `.smp` restore-side write into `[region+0x272]`.
The adjacent economic tuning page is explicit now too:
`map_editor_economic_cost_slider_panel_construct` `0x004cadf0` registers
`map_editor_economic_cost_slider_dispatch` `0x004ca980` on six visible slider controls
`0x5bcd..0x5bd7`, re-publishes the current values from
@ -33,6 +54,10 @@ The broader map-editor page owner is now bounded through
preview curve from leading lane `[world+0x0be2]` plus the stepped multiplier table
`[world+0x0be6/+0x0bea/+0x0bee/+0x0bf2/+0x0bf6]`, and republishes the six formatted numeric
rows through the neighboring dynamic-text controls `0x5bce/0x5bd0/0x5bd2/0x5bd4/0x5bd6/0x5bd8`.
The post-load consumer side is tighter now too: the same selected-year bringup later runs
`0x00433bd0`, `0x00435603`, and `0x00434130`, where `0x00433bd0` rebuilds one direct year-bucket
trio plus two dependent three-lane companion bands from the fixed year ladder, and `0x00434130`
reclamps shared year-gap scalar `[world+0x4ca2]` into the closed `1/3..1.0` band.
That closes the economic tuning page as one real save/load-facing editor family rather than only
a constructor plus one write-only slider dispatcher. The city-or-region copy side is tighter now too: the
handler first enters
@ -72,7 +97,11 @@ The broader map-editor page owner is now bounded through
concrete range and row callbacks behind list control `0xa7fa`, so the persisted 49-dword
scenario-rule band `[world+0x4a7f..+0x4b3f]` is no longer only constructor-shaped in the atlas.
The constructor `0x004cb2b0` binds those callbacks, counts enabled rows directly from that same
dword band, and keeps the trailing scalar `[world+0x4b43]` in the same page family.
dword band, and keeps the trailing scalar `[world+0x4b43]` in the same page family. The handler
is tighter now too: `0x004cb8e0` bulk-writes the full 49-dword rule array from `0xa7fb/0xa7fc`,
parses control `0xa7ff` back into trailing scalar `[world+0x4b43]`, and special-cases rule
ordinals `0x19` and `0x1a` through immediate follow-on calls to `0x00493150` and `0x00413620`
after direct row writes from callback band `0xabe0..0xafc7`.
`map_editor_territory_panel_construct` and
`map_editor_territory_panel_handle_message` own the territory rename and border-remap lane;
`map_editor_locomotive_availability_panel_construct` plus
@ -130,26 +159,35 @@ The broader map-editor page owner is now bounded through
the per-cargo runtime summary tables. That helper now reads more concretely too: it clears both
emitted cargo-id tables at `[candidate+0x79c]` and `[candidate+0x7a0]`, clears the per-cargo
float band at `[candidate+0xa1..+0xb8]`, and fills one local `0x35`-cargo status scratch from
the descriptor strip. Each subordinate row contributes status `1` when the cargo is only
referenced and status `2` when the row is currently year-active, with the helper keeping the
maximum status per remapped cargo id through `0x0062ba8c+0x9a`. It then allocates the two final
the descriptor strip. The year window is narrower than the earlier generic wording too: each
subordinate row contributes status `2` only while `start_year <= current_year < end_year`, using
live world year `[0x006cec78+0x0d]` or fallback year `0x709` when no world is present; rows
outside that active window still contribute status `1` as referenced cargo, and the helper keeps
the maximum status per remapped cargo id through `0x0062ba8c+0x9a`. It then allocates the two final
compact membership tables from the `status>=1` and `status>=2` sets with counts stored at
`[candidate+0x7a4]` and `[candidate+0x7a8]`. The same scan also makes the production-mode split
explicit on the runtime side: descriptor mode `0` uses the shared production cap at
`[candidate+0x2a]` divided by the descriptor amount, while nonzero mode bypasses that scaling
path and publishes the row amount directly. At that point the candidate import strip is
structurally closed in current local evidence: the local import and rebuild seam is no longer
where the loader reconstructs runtime descriptor or membership tables. The remaining semantic gap
is narrower instead: current local evidence shows no hidden decode stage for the supply-marker
token family at all. The editor slab preserves unmatched token strings verbatim, the constructor
only reflects selector ordinals for tokens that already exact-match visible live cargo names, and
the importer later writes the exact-matcher result ids straight into the live descriptor lanes,
defaulting failed matches to cargo id `0`. The reset side narrows that further too:
where the loader reconstructs runtime descriptor or membership tables. Current local evidence now
supports a stronger carry for the supply-marker token family too: there is no hidden decode stage
surfaced at all, so those lanes are best kept as opaque scenario-authored marker strings. The
editor slab preserves unmatched token strings verbatim, the constructor
only reflects selector ordinals for tokens that already exact-match visible live cargo names, the
handler writes selected cargo names back into those same token slots verbatim, and the importer
later writes the exact-matcher result ids straight into the live descriptor lanes, defaulting
failed matches to cargo id `0`. The reset side narrows that further too:
`0x00436d10` only zero-fills the recipe books and reseeds their `# %1` name lane, so those
marker-like token strings are not coming from the built-in reset path. So the open question is
now only what upstream source, if any, authored those marker-like token strings before exact
matching. The
immediate sibling
marker-like token strings are not coming from the built-in reset path. A fresh direct-hit scan
on the recipe-book root `0x0fe7` also did not surface any new live writer beyond the already
grounded reset, save/load, and editor-panel families. The one suspicious later hit neighborhood
around `0x00500c73/0x00500d3f` turns out not to be another recipe writer either: it is a shell
control-construction strip using localized ids `0x0fe6/0x0fe8/0x0fe9` as UI resource ids, not
`[world+0x0fe7]` accesses. Combined with the checked map/save pairs that preserve those raw
marker payloads byte-for-byte, that closes the load/save ownership question at the current
evidence level: inside the local binary the marker families are best treated as opaque
scenario-authored token payload, not as a hidden runtime decode stage waiting to be mapped. The immediate sibling
`structure_candidate_refresh_recipe_runtime_mode_flags_0x78c_0x790` `0x00411ce0`, which for
subtype byte `[candidate+0x32] == 2` scans the imported descriptor strip and derives two compact
post-import flags from mode `0` presence and whether every mode-`0` descriptor keeps at least
@ -165,14 +203,54 @@ The broader map-editor page owner is now bounded through
fixed name catalog at `0x0061dbc2/0x0061dc09` for non-subtype-`1` zero-availability candidates
with live ids `<= 0x6e` via localized string `0x0b53`, and only then re-enters the same
named-availability refresh at `0x00412c10` before returning. The broader scenario-state reset
and editor-writer side is explicit now too: the saved named-availability table is not only
startup-owned. `map_editor_industry_availability_panel_handle_message` `0x004cf430` has one
direct row-callback path for controls `0xa028..0xa40f` that forwards the clicked candidate name
plus one callback-supplied boolean into `0x00434f20`, and one bulk boolean sweep from the top
controls `0x9c43` and its sibling that walks the live candidate pool and forwards every
surviving page-visible candidate into that same upsert owner. So the scenario-side
availability override bit at `[entry+0x1e]` now has three grounded write families together:
startup preseed, stream-load refresh, and editor-side direct or bulk override. The package-save
side is explicit now too: in the editor-map branch of `0x00444dd0`, the shared prepass
`scenario_state_preseed_named_candidate_availability_overrides_from_editor_map_placed_structures`
`0x00436a70` walks qualifying placed structures, resolves their linked candidate/runtime owners,
and upserts boolean overrides from owner names immediately before the named-availability
collection is serialized.
The broader scenario-state reset
side is explicit now too: `0x00436d10` is the shared reset-and-rebuild owner beneath startup and
world-entry paths. It zeroes the late scenario-state bands, seeds the fixed year defaults and
chairman-slot rows, rebuilds the two named availability collections at `[state+0x66b2]` and
world-entry paths. It zeroes the late scenario-state bands, immediately reseeds setup-preview
validity byte `[state+0x66be] = 1`, seeds the fixed `Minimum Start Year` / `Default Start Year`
/ `Maximum Start Year` trio at `[state+0x66ca/+0x66d2/+0x66ce]`, the four startup dwords
`[state+0x6a68/+0x6a6c/+0x6a70/+0x6a74] = 1`, and the chairman-slot rows, including the first
slot's special occupied-seat byte `[state+0x69db]`, clears the queued-event and fast-forward
latches `[state+0x46c38/+0x46c3c/+0x66a2/+0x66a6/+0x66aa]`, rebuilds the two named availability
collections at `[state+0x66b2]` and
`[state+0x66b6]`, zero-fills the twelve recipe books at `[state+0x0fe7]`, then only reseeds
their per-book name lane from the fixed `# %1` format string at `0x005c9f78` through
`0x0051b700 -> 0x00518de0`, refreshes selected-year state through `0x00409e80`, `0x00433bd0`,
`0x00435603`, and the year-gap scalar helper `0x00434130`, and only then re-enters
`0x00435630`, `0x0041e970`, `0x00412bd0`, and `0x00436af0`. The startup side is tighter now too:
`0x0051b700 -> 0x00518de0`. The selected-year rebuild tail is tighter now too: in the
campaign/setup-but-not-sandbox lane it copies profile year word `[profile+0x77]` into scenario
default start year `[state+0x66d2]`, seeds Jan-1 tuple lanes `[state+0x05/+0x09]` through `0x0051d3f0`,
optionally decrements that chosen year through the shell/tutorial gates at
`[0x006cec74+0x68/+0x178]` and `[0x006cec78+0x4af7]`, and only then rebuilds the absolute
selected-year counter through `0x0051d390 -> 0x00409e80`, the selected-year bucket band through
`0x00433bd0`, the derived year-threshold band through `0x00435603`, and the year-gap scalar
helper `0x00434130`. The matched editor-side consumer is explicit too:
`map_editor_scenario_metadata_panel_refresh_controls` `0x004ca790` republishes that same reset
block through the `0x5b69..0x5b74` control band, with the start-year trio landing on
`0x5b6a/0x5b6c/0x5b6b`, campaign-scenario byte `[state+0x66de]` on checkbox `0x5b6e`, and the
inverse of metadata byte `[state+0x66f3]` on control `0x5b74`. Only after that chain does it
meet its write-side sibling: `map_editor_scenario_metadata_panel_handle_message` `0x004cb4a0`
commits the same control band back into live state by copying bounded text payloads into
`[state+0x672e]`, `[state+0x4f30]`, and `[state+0x5ae9]`, flipping `0x621f50`,
toggling `[state+0x66de]` and `[state+0x66f3]`, and parsing the three year edit fields through
`0x005a1ea5` before clamping each to `1829..2100` and enforcing
`minimum <= default <= maximum`.
Only after that chain does it
re-enter `0x00435630`, `0x0041e970`,
`0x00412bd0`, and `0x00436af0`, where the cached available-locomotive rating now reads as a
year-tiered baseline `110/200/300` plus the strongest surviving live locomotive rating filtered
by era-policy and named-availability gates, then normalized through the fixed `100/220` scale
pair and clamped into the final `50..100` cache band at `[world+0x4cbe]`. The startup side is tighter now too:
`0x00438890` does not jump straight into one selector-specific file-load branch. It first
re-enters `0x00436d10`, mirrors one shell-managed label list from `0x006d4020+0x429b0` into local
state, allocates the common runtime pools (`0x0062ba8c`, `0x0062b268`, `0x0062b2fc`,
@ -224,9 +302,11 @@ The broader map-editor page owner is now bounded through
`[this+0x178]`. There is still no post-resolution failure guard between the importer writes and
the summary-bank updates. So the strongest current read is narrower than a full cargo-id decode: if
the imported low-16 marker rows fail the exact matcher at `0x0041e9f0`, the resulting `0` ids
will still hit the first summary-bank bucket inside `0x00412650`, but the semantic meaning of
cargo id `0` itself remains ungrounded. So the remaining gap here is semantic only, not another
missing import or rebuild owner. The sibling helper
will still hit the first summary-bank bucket inside `0x00412650`, but the caller layer above
that bank now treats cargo id `0` as the nonstandard linked-site classification sentinel rather
than one ordinary cargo id. So the remaining gap here is only whether any opaque saved marker
words encode real cargo ids before they reach that matcher, not another missing import or
rebuild owner. The sibling helper
`structure_candidate_supports_or_references_cargo_id`
`0x004129d0` then uses those same banks plus the cached cargo-membership arrays to answer whether
a live candidate materially references a cargo at all. One compare step tighter, the raw line
@ -243,8 +323,11 @@ The broader map-editor page owner is now bounded through
`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
preserved scenario payload with family-specific signatures. The mode family itself is now
grounded at the player-facing layer: the editor page and importer split line up on `Disabled`,
`Demand Only`, `Supply Only`, and `Production Demand->Supply`, with mode `0` skipped on import,
mode `1` entering the demand-only branch, mode `3` entering the dual production branch, and the
remaining nonzero modes taking the supply-side branch. 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
@ -300,9 +383,10 @@ The broader map-editor page owner is now bounded through
zero-mode-cap-scaled-subrow lanes by that count, and then writes the finished quartet into
candidate dwords `[candidate+0x8e/+0x92/+0x96/+0x9a]` while stamping
`[candidate+0x8a]` from current world field `[0x006cec78+0x15]`. So the current strongest read
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.
is no longer an unresolved null-cargo semantic question inside the load bridge: 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 rather than 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
@ -317,8 +401,8 @@ The broader map-editor page owner is now bounded through
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.
callers treat cargo id `0` as a nonstandard linked-site-classification sentinel, 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
@ -347,10 +431,10 @@ The broader map-editor page owner is now bounded through
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.
is tighter now too: later mode-gated reads at other addresses also touch a `+0x42` byte on their
own object layouts, so the safest current note keeps only the immediate
`0x0040cbc0/0x0040cbd0/0x0040cbf0/0x0040dba0/0x0040dbf0` cluster grouped together and does not
reuse those later byte reads as same-family evidence.
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
@ -551,7 +635,7 @@ The broader map-editor page owner is now bounded through
`0x00441f70` and
`shell_map_bundle_write_global_staging_buffer_to_companion_image_file_and_return_path`
`0x004420f0` are the paired `_A.tga` / `_A.jpg` import and export helpers around the same
tagged selector strip at `0x00441ec0`. Both derive an extensionless stem through
tagged stage-dword plus fixed-preview strip at `0x00441ec0`. Both derive an extensionless stem through
`support_copy_string_stem_before_first_dot` `0x0051dde0`, choose the `_A.tga` versus `_A.jpg`
suffix by selector byte `[record+0x09]`, and then shuttle the file bytes through the shared
staging descriptor rooted at `0x0062bed4/0x0062bed8`. That keeps the family grounded on the same
@ -559,11 +643,32 @@ The broader map-editor page owner is now bounded through
`world_presentation_rebuild_four_overlay_surface_slots_from_source_or_fallback` `0x00535430`,
instead of leaving it as an isolated save-side file copier. The neighboring exporter
`shell_export_live_setup_preview_payload_record_and_normalize_pixel_block_0x100x0x100`
`0x00442ba0` is bounded now too: it copies the live setup payload from `[this+0x66be]`,
patches world dimensions and three shell bytes into offsets `+0x01..+0x0b`, and then
normalizes the embedded `0x100 x 0x100` pixel block at `+0x03c2` through `0x0047a120`
before the save-side callers forward that same record into `0x00441ec0` or the tagged
slot-table serializer at `0x00446312`. The broader sibling
`0x00442ba0` is bounded now too: it copies one `0x403c2`-byte live setup-preview payload record
from `[this+0x66be]`, forces validity byte `+0x00 = 1`, patches world dimensions and three shell
bytes into offsets `+0x01..+0x0b`, and then normalizes the embedded `0x100 x 0x100` pixel block
at `+0x03c2` through `0x0047a120` before the save-side callers branch in two different ways. The
package-save path at `0x00444f42` calls `0x00442ba0` and then immediately enters `0x00441ec0`
at `0x00444f47`, while the `.smp` serializer branch at `0x00446312` calls `0x00442ba0` first and
then emits `0x2ee0`, writes the full `0x403c2` payload through `0x00531030`, and closes `0x2ee1`
directly without re-entering `0x00441ec0`. The `0x00441ec0` strip is tighter now too: it
snapshots the shared leading dword seeded from `0x00620e94`, then mirrors one `dword + 0x2ee0 +
0x03c2 + 0x2ee1` strip through its staged-object or companion-file transport branches, and does
not itself choose the companion-image selector byte. The same negative boundary is explicit now too:
current local restore-side evidence still does not show either `0x00441ec0` or the later `.smp`
loader `0x00446d40` writing that restored leading dword back into `0x00620e94`, so the sidecar
format is carrying more framing than the grounded restore owners currently republish into live
state. The copied shell-byte trio is
tighter now too: current local disassembly grounds payload bytes `+0x09/+0x0a` as direct mirrors
of shell-controlled sidecar dwords `[0x006d4024+0x11471a/+0x11471e]`, while `+0x0b` is the low
byte of shared dword `0x0062bec4` later seeded by the package-save coordinator from the sibling
selector pair `[0x006d4024+0x11472a/+0x11472e]`; the display-runtime defaults path around
`0x0051ee48..0x0051ee85` initializes those same four dwords together, and the broader
`shell_settings_window_handle_message_dispatch_and_persist_display_runtime_sidecar_family`
`0x00464c80` later owns the direct toggle strip for controls `0xe101..0xe104`, including the
grounded implication rules `!0x11471e -> clear 0x11472a` and `(0x11472a || 0x11472e) -> set 0x11471e`.
Payload bytes `+0x0a/+0x0b` are therefore the same
companion-image and companion-payload sidecar gates later read from `[world+0x66c8/+0x66c9]` in
the package-save tail. The broader sibling
`shell_build_temp_surface_from_payload_preview_pixels_and_query_named_resource_field_0x46`
`0x00442740` is bounded now too: it copies the embedded preview pixels at `[record+0x03c2]`
into a temporary `0x40000`-byte RGBA buffer, constructs one temporary `0xec`-byte surface-like
@ -573,21 +678,23 @@ The broader map-editor page owner is now bounded through
`0x0053b080`. That keeps the short save-side branch at `0x00445925` grounded as a one-shot
preview-surface query helper rather than leaving another anonymous image-copy leaf between the
tagged export path and the progress notice strip. The broader sibling
`shell_map_bundle_rewrite_companion_payload_file_via_tmp_path_and_progress_callback`
`0x004427c0` is now bounded as the temporary-path owner above that same tag strip: it derives
`%1.tmp` from the source path, installs progress callback `0x00441e20` in `0x00d93988`,
`shell_map_bundle_rewrite_companion_payload_file_via_tmp_extension_path_and_progress_callback`
`0x004427c0` is now bounded as the `.tmp`-extension owner above that same tag strip: it strips
the caller extension, appends `.tmp`, installs progress callback `0x00441e20` in `0x00d93988`,
rewrites the middle payload band rooted at `+0x03ce` through `0x00553000`, writes the result
back out through `0x005a276f`, updates payload dword `[record+0x18]`, and only then re-enters
`0x00441ec0`.
The sibling `shell_map_bundle_rewrite_companion_payload_file_from_record_offsets_and_sync_tags`
The sibling
`shell_map_bundle_rewrite_companion_payload_file_from_path_stored_byte_offsets_and_sync_tags`
`0x00442900` is bounded now too: it takes the live payload record in `EAX`, reads the full
companion file named by the caller path, installs the same progress callback, rewrites the
middle payload through `0x00553000` using stored offsets `[record+0x18/+0x1c]`, and then writes
the output back in four spans: the leading `0x03ce` bytes, the transformed middle payload, the
untouched interval `[record+0x18 .. record+0x1c)`, and the remaining tail. It then stores the
caller path back into `[record+0x18]` and re-enters `0x00441ec0`. That closes the offset-driven
save-side sibling reached from `0x004459dd` after `0x00441f70`, so the companion-payload strip
is no longer split between one named temp-path owner and one anonymous offset-band rewrite body.
caller path back into `[record+0x18]` and re-enters `0x00441ec0`. That closes the path-plus-
stored-offset save-side sibling reached from `0x004459dd` after `0x00441f70`, so the
companion-payload strip is no longer split between one named temp-path owner and one anonymous
offset-band rewrite body.
The underlying temporary-surface seam beneath those save-side helpers is bounded now too.
`surface_owner_allocate_first_free_row_slot_0xa0_and_seed_dimensions_and_type` `0x00541b10`
scans the eight-entry row band `[this+0xa0..+0xbc]`, allocates one `0x32`-byte row, seeds width,
@ -692,15 +799,19 @@ The broader map-editor page owner is now bounded through
`0x00444b50` with the loaded scenario title, and it dispatches on many fixed `.rdata` names
including `Go West!`, `Germany`, `France`, `State of Germany`, `New Beginnings`, `Dutchlantis`,
`Britain`, `New Zealand`, `South East Australia`, `Tex-Mex`, `Germantown`, `The American`,
`Central Pacific`, and `Orient Express`, with several branches further gated by setup payload
byte `[this+0x66de]`. The matched branches then perform targeted live-world edits rather than a
generic title-side publish: they patch object-state dwords and bytes inside collections rooted at
`0x0062be18`, `0x0062bae0`, and `0x006ada80`, adjust selected float and scalar fields, copy
paired ten-dword record blocks when specific name/class pairs match, flip secondary-raster bits
through table `0x005ee508..0x005ee5cc`, retune a later collection at `0x0062b268`, and inject
scenario-specific text lines into the shell-owned band `[this+0x4f30]` through repeated
`0x0051e5d0`. That finally makes the broad post-load scenario-fixup seam explicit instead of
leaving it as an unbounded string-switch body beneath world entry.
`Central Pacific`, and `Orient Express`, with several branches further gated by campaign-scenario
byte `[this+0x66de]`. The matched branches now have a few concrete anchors too instead of reading
as one anonymous title-side publish. The non-campaign `Go West!` branch retags world-object row
`7` named `Open Aus` by promoting byte `[obj+0x7f9]` from `1` to `2` when its nested opcode-`0x1b`
payload is still in the expected starter state; the campaign-gated `Go West!`/`Germany`/`Orient Express`
side nudges selected city float pairs `[city+0x25a/+0x25e]` upward when they remain below fixed
thresholds; and the `Central Pacific` branch injects repeated localized `Company Track Miles`
text lines into shell band `[this+0x4f30]` through `0x0051e5d0`. Beyond those grounded examples,
the owner also patches object-state dwords and bytes inside collections rooted at `0x0062be18`,
`0x0062bae0`, and `0x006ada80`, copies paired ten-dword record blocks when specific name/class
pairs match, flips secondary-raster bits through table `0x005ee508..0x005ee5cc`, and retunes one
later collection at `0x0062b268`. That finally makes the broad post-load scenario-fixup seam
explicit instead of leaving it as an unbounded string-switch body beneath world entry.
One neighboring status helper is bounded now too:
`shell_publish_progress_sample_notice_from_byte_delta_and_elapsed_ticks` `0x00442660` is the
shared byte-delta and elapsed-time notice builder used by both the bundle-service strip and the
@ -1095,5 +1206,6 @@ The broader map-editor page owner is now bounded through
`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.
event-validation page. Those residual site-side scores and flag bits can now be carried
conservatively as event-validation gameplay metric bands rather than as another editor ownership
seam.

View file

@ -13,7 +13,7 @@ Related headings in the same file:
- `State Anchors`
- `Subsystem Handoffs`
- `Evidence`
- `Open Questions`
- `Current Boundary`
- `Shared Runtime-Object and Support Families`
### Post-load Generation, PaintTerrain, and Save/Load Restore

View file

@ -335,10 +335,10 @@
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`,
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
@ -512,12 +512,11 @@
forwarding flag triplet `(0, 1, 0)`. RT3.lng closes the neighboring `shell_map_file_entry_coordinator`
pair in the same way: `0x00441ac0` is `Load game` and `0x00441af0` is `Quick load`, with the
same `(0, 0, 0)` versus `(0, 1, 0)` split above `0x00445ac0`.
- Open Questions: bit `0x1` on both broad coordinators now grounds the Quicksave name seed and the
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.
- 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.

View file

@ -788,7 +788,7 @@
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
owner-port fallback, bit `0x08` as the compact-header auxiliary inline 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
@ -833,8 +833,8 @@
prefix and then walks exactly two counted reply sections, each with its own NUL-stem dictionary;
the later value strings are reinserted under synthetic decimal-suffixed keys built from those
stems plus one running per-section index.
The compact-only
auxiliary dword at `[descriptor+0x10]` is tighter in a negative way too: local xref scans now
The compact-header
auxiliary inline 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
@ -1050,14 +1050,14 @@
rebuild cookie `[binding+0x34]` and marks the constructed route live via `[route+0xbc] = 1`,
while the success tail mirrors stored route label `[this+0x9a8]` into `[this+0xb3c]`, patches
`[route+0xa0]` through `0x58bc90 -> 0x597330`, and clears live-route mode mask `[this+0xb38]`.
The remaining negative boundary is narrower now: current local evidence still does not show
companion writes to `[route+0xa4]` or `[route+0xd4]` in the grounded live-route connect path. The stable
transitions therefore still switch by releasing route objects or route bindings and rebuilding
route state, not by mutating callback slots in place. The parser behavior is now tighter as well: semicolon lines only dispatch when
The callback boundary is closed more tightly now too: current local evidence still does not show
companion writes to `[route+0xa4]` or `[route+0xd4]` in either grounded transport-owned connect
path, so the stable transitions still switch by releasing route objects or route bindings and
rebuilding route state, not by mutating those optional callback slots in place. The parser behavior is now tighter as well: semicolon lines only dispatch when
`[route+0xd4]` is non-null, and the subtype-`6` raw fallback only dispatches when `[route+0xa4]`
is non-null. For the currently grounded transport-owned status and live routes, those two branches
therefore remain optional and can cleanly no-op instead of implying a hidden mandatory callback
path. Inside the packet parser, subtype `4` is no longer an unknown callback hop: after cookie
therefore stay intentionally inactive and can cleanly no-op instead of implying a hidden mandatory
transport 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
@ -1071,7 +1071,10 @@
`[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
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
rooted at `[route+0xa0]` and `[route+0xa4]`; for the grounded transport-owned status and live
routes that currently means validated NATNEG-style payloads forward through `0x00597330` on
`[route+0xa0]`, while the raw fallback stays disabled because `[route+0xa4]` remains null. 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`,
@ -1080,8 +1083,13 @@
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.
- Closure note: the older broad request-id question is now replaced by the grounded lane split
already exposed above. `multiplayer_dispatch_requested_action` bounds the outer
`Multiplayer.win` action family, while the concrete transport submit owners are the bound-route
status lane `0x5959e0 -> 0x593980`, the selector-text route-request lane `0x593c40`, and the
second selector-descriptor callback lane rooted at `0x59fc80`. The preview-dataset reuse
boundary is also closed at the current evidence level: beyond `Multiplayer.win`, the same owner
family is reused by the `.gmt` save-mode hook in `0x00445de0`, `BuildingDetail.win` auxiliary
sync, `Start New Company...` side-owner submission, `CompanyDetail.win` async multiplayer
actions, and dataset-side selector or update branches, but not by a broader ordinary
world-service path.

View file

@ -1,9 +1,204 @@
## Next Mapping Passes
## Seam Queue
- 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.
- `done` Load/save setup-preview sidecar save split:
the atlas now distinguishes the direct package-save `0x00442ba0 -> 0x00441ec0` branch at
`0x00444f42/0x00444f47` from the `.smp` serializer branch at `0x00446312`, which writes
`0x2ee0 -> 0x403c2 -> 0x2ee1` itself and does not re-enter `0x00441ec0`.
- `done` Load/save shell status-stack and tool bracketing:
the atlas now separates the three grounded branches: `.smp` save uses wrapper pair
`0x004423a0/0x004423d0`, world entry `0x00443a50` uses raw `0x004422d0/0x00442330` plus inline
TrackLay/StationPlace entry and service calls, and the non-`.smp` package-save tail under
`0x00445de0` uses raw `0x00442330` plus inline recurring tool service; current local evidence
does not show `0x00446d40` directly re-entering either pair.
- `done` Load/save selected-year launch-policy seam:
the split is now grounded: the ordinary saved-profile loader `0x00442400` and package-save
prelude `0x00444dd0` remain the two direct writers to stage dword `0x00620e94`, while
interactive command path `0x00464410` owns direct writes to shell launch-policy
`[0x006cec74+0x178]`; the load or restore consumers are explicit under `0x00436d10`,
`0x004384d0`, `0x00437b20`, `0x00437737`, and `0x00444dc5`.
- `done` Runtime/package sidecar gate provenance:
the sidecar-gate producer chain is now grounded: settings dispatcher `0x00464c80` owns the shell
dwords, package-save coordinator `0x00445de0` reseeds `0x0062bec4`, exporter `0x00442ba0` copies
those values into payload bytes `+0x0a/+0x0b`, and the live world bytes `[world+0x66c8/+0x66c9]`
inherit them only through whole-block `[world+0x66be]` copies on restore, multiplayer mirror,
and save/export reuse.
- `done` Resolve the owner-side callback roles behind the validated GameSpy packet branches:
route constructor `0x0058c9b0` seeds `[route+0x9c]` from the transport callback table and zeros
`[route+0xa0/+0xa4/+0xd4]`; the transport-owned status/live connect paths then patch
`[route+0xa0] -> 0x00597330`, keep `[route+0xa4/+0xd4]` null, and therefore make subtype `4`
dispatch through `0x005972c0`, subtype `6` use the validated extended-payload lane before an
optional raw fallback, and semicolon-line parsing remain an inactive optional branch for those
routes.
- `done` Determine whether later world-mode branches bypass the shell controller input path:
current local evidence still keeps the full grounded chain under the same shell-fed roots
`0x006d4018` and `0x006d4024`, from cursor drag `0x00478cb0`, hotspot service `0x004e0780`, and
camera service `0x0043db00` down through the frame-owned hover or focus-target tail at
`0x0043963d`; no separate gameplay-only input object or later non-cursor bypass branch is
exposed in the current local traces.
- `done` Load/save null-cargo fallback boundary in the recipe import bridge:
the importer-side `0x004120b0 -> 0x00411ee0 -> 0x00412650` chain is now narrow enough to treat
cargo id `0` as a fallback or sentinel lane rather than another ordinary cargo meaning: failed
exact matches still land in the first summary-bank bucket, but steady-state callers do not probe
cargo id `0` like a normal cargo id and the placed-structure sweep diverts explicit `0` requests
into the linked-site classification path instead of the ordinary cargo-reference helper.
- `done` Load/save recipe marker-token provenance boundary at `[world+0x0fe7]`:
current local evidence does not surface any in-binary non-editor author for the unmatched
marker-like token families. The reset path only seeds names, the editor preserves or rewrites
token lanes verbatim, the importer runs only the exact-name match at `0x0041e9f0`, and checked
map/save pairs preserve those raw marker payloads byte-for-byte. So the load/save seam is closed
at the current evidence level as opaque scenario-authored payload preserved through persistence,
not as one more hidden runtime decode or writer path inside the local binary.
- `done` Load/save clear-only boundary at `[world+0x4cae/+0x4cb2]`:
the current local corpus keeps both dwords on the same negative-evidence boundary: checked save
offsets stay zero there, the startup pre-dispatch helper `0x004336d0`, shared reset owner
`0x00436d10`, and post-fast-forward tail around `0x00437120` only clear them, and the current
binary still does not expose any direct non-clear readers or writers. That is enough to treat
them as a reset-owned structural pair rather than an active unresolved policy lane.
- `done` Load/save late-state plateau boundary under `[world+0x4cc2..]`:
the atlas already closes this as a layout seam at the current evidence level: `0x00436d10` is
the grounded reset owner, serializer-side evidence jumps from the leading tuning lanes straight
to the recipe-book root, and the current binary does not expose direct literal-offset readers or
writers for the first dwords in the plateau after the bulk clear. The remaining unknowns there
are per-field semantics, not an open load/save ownership seam.
- `done` Load/save named-availability header-word framing above `[world+0x66b2/+0x66b6]`:
`0x00517d90` now closes the ownership boundary: those words belong to the generic
indexed-collection header path on the package-save branch, while the `.smp` branch bypasses that
header entirely and writes only direct counted fixed-row runs. Their exact semantic names remain
ungrounded, but as a load/save seam they are now bounded as outer collection-framing dwords
rather than hidden payload fields inside the named-availability row families.
- `done` Load/save special-condition tail scalar boundary at `[world+0x4b0b..+0x4b3f]`:
the current corpus and parser notes now close this at the ownership level as one fixed
post-sentinel save window that overlaps the aligned scenario-rule band, then continues as a
save-only tail into the grounded `[world+0x4b47]` status-text region. Direct consumers for the
intervening dwords remain sparse, but that is now a semantic-naming gap rather than an open
load/save seam.
- `done` Load/save compact setup-profile slab framing in classic/1.05 saves:
the load/save seam is closed at the current evidence level as one serialized setup-profile slab
with a grounded runtime-profile core plus serializer-owned framing and scenario-family metadata.
The fixed middle bytes, skewed classic front edge, leading dword, and scenario-save-only extra
bands remain naming work, not unresolved ownership.
- `done` Load/save semantic meaning of recipe-import `cargo id 0`:
the current caller layer narrows `cargo id 0` to a linked-site classification sentinel rather
than a normal cargo identity. Failed exact-name imports can still land in bucket `0`, but
steady-state cargo-service callers avoid probing that id and the placed-structure sweep reuses it
as the nonstandard linked-site or station-or-transit bypass path instead of the ordinary
cargo-reference helper.
- `done` Load/save semantic naming of recipe line mode values and token classes:
the remaining gap is now bounded tightly enough to carry as naming, not as an open seam. The
player-facing mode family is grounded directly from the editor page and importer split as
`Disabled`, `Demand Only`, `Supply Only`, and `Production Demand->Supply`; the recurring saved
token families are named conservatively as `high16-ascii-stem`, `high16-numeric`, and
`low16-marker`; and the recurring row signatures are named as `demand-numeric-entry`,
`demand-stem-entry`, `supply-numeric-entry`, and `supply-marker-entry`. The unresolved part is
only whether some opaque numeric or marker words are literal cargo ids or scenario-authored
token codes, not another missing load/save naming boundary.
- `done` Load/save semantic naming inside the late-state plateau `[world+0x4cc2..]`:
the family now carries one conservative load/save name. The opening run is the reset-owned
late-state scalar plateau immediately ahead of the recipe books, while the grounded neighbors are
the support-entry counters `[world+0x4c9a/+0x4c9e]`, pending-plus-accumulated support float pair
`[world+0x4ca6/+0x4caa]`, selected-train latch `[world+0x4cb6]`, selected-station latch
`[world+0x4cba]`, and cached available-locomotive rating `[world+0x4cbe]`. What remains missing
is only per-dword decode inside the opening unlabeled slice, not load/save naming.
- `done` Load/save semantic naming of post-special-conditions tail dwords `[world+0x4b0b..+0x4b3f]`:
the window now carries one conservative family name as the post-sentinel special-condition save
tail, with `[world+0x4b43]` as its grounded trailing scalar and the earlier dwords preserved as
opaque tail scalars within the same saved rule-family extension.
- `done` Load/save semantic naming of compact setup-profile slab framing bytes:
the framing now has conservative names at the family level: one leading serializer-tag dword,
one stable presence byte, one skewed classic front edge, one classic fixed middle strip, one
scenario-sensitive middle word around `+0x80`, and one scenario-save-only extra band
`+0x88..+0x8b`, all wrapped around the already-grounded setup-profile core.
- `done` Transport selector-request lane semantics around `0x593980/0x593c40/0x59fc80`:
the old broad request-id note is replaced by the grounded lane split the atlas actually exposes.
Outer `Multiplayer.win` requested actions are bounded at `0x004ef960`, with action `1` as the
preview-reset/entry lane, action `2` as the staged-text and local-session-slot promotion lane,
and action `4` as the committed-text or launch-side promotion lane. Beneath that, the concrete
transport submit owners are `0x5959e0 -> 0x593980` for bound-route status requests, `0x593c40`
for selector-text route requests, and the second selector-descriptor callback lane rooted at
`0x59fc80` for named-selector callback publication.
- `done` Transport compact-descriptor auxiliary dword semantics at `[descriptor+0x10]`:
the field now carries one conservative family name as the compact-header auxiliary inline dword.
Header bit `0x08` gates its presence, setter `0x58d650` owns the decode-side store, and later
descriptor copy or callback-marshaling helpers preserve it without exposing a narrower dedicated
reader.
- `done` Transport preview-dataset reuse boundary outside `Multiplayer.win`:
the caller set is now broad enough to close the boundary. Beyond the core `Multiplayer.win`
flows, preview submitter `0x00469d30` is reused by the `.gmt` save-mode branch in `0x00445de0`,
`BuildingDetail.win` auxiliary sync, `Start New Company...` side-owner submission,
`CompanyDetail.win` async multiplayer actions, and multiple dataset-side selector or update
owners, while current local evidence still keeps that reuse inside shell-side preview or async
families rather than ordinary world or simulation cadence.
- `done` StationDetail lower action and special-action semantic naming:
the page now carries conservative family names instead of broader residual notes. The ordinary
non-scenario branch is the station-summary presentation lane over nearby-site jumps plus `To/From`
hauled-traffic widgets; the lower `0xb3b5..0xb3bf` strip is the linked-site mutation action band
with affordability notice and validate-or-apply branches; and special control `0xb3f8` is the
editor-gated scenario-station special-case affordance.
- `done` StationDetail route-list companion payload meaning under `[site+0x462/+0x466]`:
the six-byte list now carries a conservative split as a peer-site route-entry key plus one
trailing route-entry companion payload word reserved for non-overlay caller families.
- `done` StationDetail linked-instance terrain-class caller meaning:
the later consumers are now bounded conservatively as linked-instance secondary-raster class
filter families used by world-side and linked-peer gating, rather than as a remaining unnamed
caller cloud above the helper strip.
- `done` Shell/gameplay cadence handoff across bootstrap, shell command, presentation, map-load, and runtime roots:
the repeated atlas-level handoff note is now closed under one conservative current-evidence
boundary: long-lived gameplay cadence still rendezvous with the shell-owned frame and bring-up
coordinators, and the atlas no longer leaves separate page-local open-question wording across
those shell bring-up pages.
- `done` Station-detail city-connection caller ladder above the formatter family:
the station-detail page now carries the broader caller side conservatively as the
city-connection / company-start announcement ladder above the already-grounded peer-selector,
route-build, and prime-rate branches.
- `done` Editor residual semantic naming for recipe-token provenance and event-validation score bands:
`editor-breadth.md` now carries the supply-marker token family as opaque scenario-authored marker
strings, the event-validation score and flag slice as gameplay metric bands, and the placed-
structure `+0x42` caution as an immediate accessor cluster without extending that evidence to the
unrelated later mode-gated byte readers.
- `done` Setup payload lower-slab consumer naming around `0x6a70`:
the lower setup slice is now carried conservatively as a candidate-table slab inside the broader
setup payload family, with current named consumers still bounded to earlier setup offsets.
- `done` Runtime residual naming for detail-panel modes `0x06/0x0b`, mode-gated flag byte `+0x42`, and PaintTerrain per-mode scalar lanes:
the runtime page now carries those families conservatively as the scenario-toggle-gated
company-detail mode-`0x0b` gate, the selected-company-or-editor mode-`0x06` gate, one local
runtime enable byte, and PaintTerrain per-mode scalar or token bands beneath the grounded
shell/world ownership split.
- `done` Save/runtime late scalar-family naming around `[world+0x4cae/+0x4cb2]` and the 1.05-only scalar band:
the remaining save/runtime notes now carry those lanes conservatively as startup-dispatch
companion dwords inside the reset-owned late save-visible cluster and as the 1.05-only late
scalar tail beneath the aligned runtime-rule base.
- `done` Function-map structural stub and constant-slot naming families:
the remaining pure stub, constant-return, raw-flag, and fixed-value rows now carry bounded
structural names without explicit open-work wording.
- `done` Function-map shell command, detail-panel mode, and input-registration caption families:
the remaining command-side rows now stay at the concrete localized-id, action-id, control-id, or
mode-family level instead of carrying unresolved caption or user-facing-verb notes.
- `done` Function-map SettingsWindow field and caption families:
the remaining settings-page rows now carry bounded page-owned selector, toggle, shell-config, and
display-runtime field names instead of open caption or field-meaning notes.
- `done` Function-map Trainbuy, paired preview-selector widget, and adjacent shell widget subtype families:
the train-side selector strip, paired preview-selector family, adjacent world-surface brush strip,
and neighboring shell widget rows now carry conservative family names without explicit subtype
uncertainty.
- `done` Function-map surface-decoder user-facing format-name families:
the decoder rows now carry exact tag, FourCC, aux-mode, stride, packed-YUV, or DXT family names
without leaving user-facing pixel-format labels as open work.
- `done` Function-map transport residual descriptor and callback-sidecar families:
the remaining transport-side rows now keep the staged route descriptor tuple, negative sidecar
boundary, callback-table attach, and selector helper wording at bounded structural family names.
- `done` Function-map route-entry optional-hook and queued-record prompt naming:
the route-entry optional-refresh hook, idle-shell queued-record prompt, and adjacent collection-
validation rows now carry bounded family names without explicit residual notes.
- `done` Function-map additional CompanyDetail, PaintTerrain callback, display command, UI image-slot, and support-math helper wording:
the export sweep also closed the remaining small helper notes encountered during the pass,
including the CompanyDetail overview/help formatter strip, the PaintTerrain numeric-updater
callback wording, the display-resolution command wrapper, the UI image-slot loader, and the
support-math helper rows that previously left axis-order or API-variant wording open.
## Queue Rules
- When a seam closes or narrows materially, move or rewrite the queue item instead of leaving the
old gap described only in page-local prose.
- Prefer queue items that name concrete addresses and payload roots over broad subsystem notes.
- Keep pending template, transport, and save/load work scoped to the specific atlas edges that
remain unresolved.

View file

@ -97,8 +97,16 @@ The same brush strip is tighter now too:
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
launch policy. The negative boundary is tighter now too: current local disassembly still does
not show `world_entry_transition_and_runtime_bringup` `0x00443a50`,
`shell_setup_load_selected_profile_bundle_into_payload_record` `0x00442400`, or
`world_load_saved_runtime_state_bundle` `0x00446d40` republishing that dword from file-backed
state; a direct full-function sweep on `0x00446d40` still turns up many references to shell root
`0x006cec74`, but no direct `+0x178` operand. The write-side split is now concrete rather than
provisional too: current full-binary xrefs still only ground direct stores to stage dword
`0x00620e94` in the ordinary saved-profile loader `0x00442400` and package-save prelude
`0x00444dd0 = 0x26ad`, while the direct store to `[0x006cec74+0x178]` stays in the interactive
command dispatcher `0x00464410`. The `319` lane itself is no longer the
open structural gap; it now clearly owns chairman-profile slot seeding, profile-record
materialization, a shell editor surface over the same local record family, and a separate
live-company presentation path through the company-list window. The later interior order of that
@ -246,14 +254,22 @@ The same brush strip is tighter now too:
books, but only the nonzero imported rows reach the live `0xbc` descriptor array. The remaining
supply-marker gap is now narrower too: current local evidence shows no special decode path at
all before exact-name resolution. The editor-side page only reflects selector ordinals when the
persisted token strings already match visible live cargo names, and the importer itself writes
the exact-match result ids straight into `[desc+0x1c]` and `[desc+0x44]`, defaulting failed
matches to cargo id `0`. The reset side narrows that authorship question further too:
persisted token strings already match visible live cargo names, the normal in-game editor path
`0x004d0040` writes selected cargo names back into those token slots verbatim, and the importer
itself writes the exact-match result ids straight into `[desc+0x1c]` and `[desc+0x44]`,
defaulting failed matches to cargo id `0`. The reset side narrows that authorship boundary
further too:
`0x00436d10` only zero-fills the recipe books and reseeds their `# %1` name lane, so those
marker-like token strings are not coming from the built-in reset template. So the open question
is now only what upstream source, if any, authored those marker-like token strings before exact
matching, not whether one hidden runtime decode stage still sits between the saved recipe books
and the live descriptor arrays.
marker-like token strings are not coming from the built-in reset template. A fresh direct-hit
scan on the recipe-book root `0x0fe7` also failed to surface any new live writer beyond the
already-grounded reset, save/load, and editor-panel families. The one suspicious later hit
neighborhood around `0x00500c73/0x00500d3f` turns out not to be another recipe writer at all:
it is a shell control-construction strip using localized ids `0x0fe6/0x0fe8/0x0fe9` as UI
resource ids, not `[world+0x0fe7]` field accesses. Checked map/save pairs then preserve those raw
marker payloads byte-for-byte. So the load/save ownership seam is closed at the current evidence
level: within the local binary there is no surfaced non-editor author or hidden decode stage for
those marker families before the saved recipe books are exact-matched back into live descriptor
arrays.
The first grounded consumer beneath that import bridge is tighter now too:
`structure_candidate_query_cargo_runtime_summary_channels` `0x00412650` lazily stamps the current
scenario year into `[candidate+0x788]`, rebuilds four banks across both mode banks `0/1`, and
@ -265,11 +281,12 @@ The same brush strip is tighter now too:
subrows are present, add one cap-share scalar to `[this+0x0a4]` and one cap-scaled row
contribution to `[this+0x178]`. That keeps the cargo-id-`0` boundary narrow and concrete too:
if marker rows still fail `0x0041e9f0`, they will hit the first bank bucket inside `0x00412650`,
but the broader steady-state caller layer already avoids treating cargo id `0` as a normal cargo
request: the candidate-service bitset owner loops from cargo id `1`, and the placed-structure
sweep routes explicit cargo-id-`0` requests into the linked-site classification side instead of
the ordinary cargo-reference helper. That means the remaining load/save gap in this local bridge
is the marker-token meaning itself, not another missing consumer-side structure. The runtime descriptor
but the broader steady-state caller layer already treats cargo id `0` as a fallback or sentinel
request rather than a normal cargo lane: the candidate-service bitset owner loops from cargo id
`1`, and the placed-structure sweep routes explicit cargo-id-`0` requests into the linked-site
classification side instead of the ordinary cargo-reference helper. That keeps the local bridge
closed at the current evidence level: marker rows can still arrive as opaque preserved scenario
payload, but there is no remaining importer or consumer-side ownership seam to map here. The runtime descriptor
construction is tighter in the same way: each imported `0xbc` slot is
zeroed first, seeded with fixed year-window defaults `0x0708..0x270f`, then filled only for
nonzero source-line modes; mode `2/3` writes the supplied token lane through `+0x08 -> +0x1c`
@ -279,8 +296,11 @@ The same brush strip is tighter now too:
The immediate helper strip under that bridge is tighter now too. `0x00411ee0` is no longer just
a vague summary refresh: it clears the two compact cargo-id tables at `[candidate+0x79c]` and
`[candidate+0x7a0]`, clears the per-cargo float band `[candidate+0xa1..+0xb8]`, fills one local
`0x35`-cargo status scratch where `1` means referenced and `2` means currently year-active, keeps
that maximum status per remapped cargo id through `0x0062ba8c+0x9a`, and then compacts the
`0x35`-cargo status scratch where `1` means referenced and `2` means currently year-active. The
year window is narrower than the earlier shorthand too: status `2` only applies while
`start_year <= current_year < end_year`, using live world year `[0x006cec78+0x0d]` or fallback
year `0x709` when no world is present. The helper keeps that maximum status per remapped cargo id
through `0x0062ba8c+0x9a`, and then compacts the
`status>=1` and `status>=2` sets back into the two emitted cargo-id tables with counts
`[candidate+0x7a4]` and `[candidate+0x7a8]`. The same scan publishes the stronger production-side
split too: mode-`0` descriptor rows are cap-scaled by `[candidate+0x2a] / [desc+0x04]`, while
@ -364,9 +384,54 @@ The same brush strip is tighter now too:
`[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. The surrounding `.smp` lane is tighter too: at version `>= 0x3f1` the save path
also writes the late world-status block at `[world+0x66be]` through chunk ids `0x2ee0/0x2ee1`,
and the restore path reads that same pair back while older bundles clear the compatibility tail
at `[world+0x6987]`; both sides also preserve the same twelve `0x4e1` recipe books at
also writes the late setup-preview payload block at `[world+0x66be]` through chunk ids
`0x2ee0/0x2ee1`, and the restore path reads that same pair back while older bundles clear the
compatibility tail at `[world+0x6987]`; the shared helper `0x00441ec0` inside that sidecar
family is tighter now too, but the save split is explicit now: the package-save branch at
`0x00444f42/0x00444f47` calls `0x00442ba0` and then immediately re-enters `0x00441ec0`, while
the `.smp` serializer branch at `0x00446312` instead calls `0x00442ba0` and then emits `0x2ee0`,
writes the full `0x403c2` payload directly through `0x00531030`, and closes `0x2ee1` itself.
`0x00441ec0` therefore belongs to the companion-image or staged-object sidecar transport strip,
not to the ordinary `.smp` slot-table write path. The restore-side boundary is narrower than that
file format now too: current local evidence still does not show either `0x00441ec0` or the
`.smp` restore path inside `0x00446d40` writing that restored leading dword back into
`0x00620e94`, and the same negative read currently applies to shell dword
`[0x006cec74+0x178]`: the load/restore family consumes it for selected-year and seeding
decisions, but the grounded writer is still the interactive command path in `0x00464410`
rather than a file-backed restore. That stage-versus-policy split is explicit on the consumer
side now too: `0x00436d10` uses `[0x006cec74+0x178]` only for the `-1` or `-3` selected-year
adjustment branch after `[profile+0x77]` seeds the Jan-1 tuple, `0x004384d0` only uses the same
shell dword to gate the later economy burst, `0x00437b20` derives its loop count from that shell
dword before the special-condition override at `[world+0x4af7]`, while the save-backed stage
dword `0x00620e94` stays on the separate threshold and candidate-preseed side through
`0x00437737` and `0x00444dc5`. The save-side exporter `0x00442ba0` also shows the
concrete shape of that block by copying one `0x403c2`-byte payload record, forcing validity byte
`+0x00 = 1`, patching current world dimensions into `+0x01/+0x05`, mirroring shell bytes into
`+0x09..+0x0b`, and normalizing the embedded `0x100 x 0x100` preview pixels at `+0x03c2`. That
byte trio is tighter now too: current local disassembly grounds `+0x09/+0x0a` as direct mirrors
of shell-controlled sidecar dwords `[0x006d4024+0x11471a/+0x11471e]`, while `+0x0b` is the low
byte of shared dword `0x0062bec4`, which the package-save coordinator seeds from the sibling
selector pair `[0x006d4024+0x11472a/+0x11472e]` before export. The shell-side provenance is
tighter in the same way: the display-runtime defaults path around `0x0051ee48..0x0051ee85`
initializes those four dwords together, and the broader
`shell_settings_window_handle_message_dispatch_and_persist_display_runtime_sidecar_family`
`0x00464c80` later owns the direct toggle strip for controls `0xe101..0xe104`, including the
grounded implication rules `!0x11471e -> clear 0x11472a` and `(0x11472a || 0x11472e) -> set 0x11471e`.
The two
later sidecar gates are therefore tied
back into that copied layout directly: package-save reads `[world+0x66c8]` and `[world+0x66c9]`,
so the exported payload bytes `+0x0a` and `+0x0b` are the same companion-image and
companion-payload gates in the live world block. The producer boundary is tighter now too:
current local xrefs still do not show standalone byte-local writers for `[world+0x66c8/+0x66c9]`,
but the same search now shows that they are not orphan fields either. The ordinary producer is
the wider payload exporter chain itself: `0x00464c80` toggles the shell-side source dwords,
`0x00445de0` reseeds selector dword `0x0062bec4`, and `0x00442ba0` copies those values into
payload bytes `+0x0a/+0x0b`. The live world bytes are then written only by the bulk
`[world+0x66be]` payload-copy lanes, namely the `.smp` restore path `0x00446d40` and the
multiplayer selector mirror `0x0046cf40`, both of which copy the full setup-preview payload
block containing those two bytes.
companion-payload sidecar gates inside the broader `0x66be` block.
Both sides also preserve the same twelve `0x4e1` recipe books at
`[world+0x0fe7]` as one exact `cap dword + 0x3d-byte header + five repeated line tuples`
structure, where each repeated `0x30`-byte line is serialized field-by-field as mode dword
`+0x00`, annual amount float `+0x04`, supplied-token window `+0x08`, and demanded-token window
@ -453,7 +518,8 @@ The same brush strip is tighter now too:
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. So the local static seam there is closed: it
is runtime-save state, and only the exact owner family of those scalar lanes remains unnamed.
is runtime-save state, now carried conservatively as the 1.05-only late scalar tail beneath the
aligned runtime-rule base rather than an unnamed ownership gap.
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
@ -536,17 +602,18 @@ The same brush strip is tighter now too:
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-owned dwords
`[world+0x4cae]` and `[world+0x4cb2]`, and the local corpus leaves all three exact dword starts
zeroed. Those two later dwords are tighter now in lifecycle even though their user-facing meaning
is still open: the startup-runtime pre-dispatch helper
zeroed. Those two later dwords are now tight enough in lifecycle to carry conservatively as
startup-dispatch companion dwords inside the reset-owned late save-visible cluster: the
already-grounded startup-runtime pre-dispatch helper
`world_runtime_reset_startup_dispatch_state_bands` `0x004336d0` clears both before
`shell_active_mode_run_profile_startup_and_load_dispatch` `0x00438890`, the broader
scenario-state reset owner `0x00436d10` clears them again with the rest of the late save-visible
cluster, and the post-fast-forward selected-year tail around `0x00437120` clears them one more
time alongside the shell presenter cache band `[0x006d4024+0x11425a..+0x114276]`. Local static
evidence still does not show any non-clear readers or nonzero writers for `[world+0x4cae]` or
`[world+0x4cb2]`, so the safest current read is still structural: they are startup and reload
lifecycle dwords, not yet another grounded policy or simulation lane. The same is true for the
later exact byte-owned policy lanes: file offsets `0x0f78`,
`[world+0x4cb2]`, so this pair is closed at the current evidence level as a reset-owned
structural boundary rather than another active policy or simulation seam. 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
@ -569,9 +636,21 @@ The same brush strip is tighter now too:
twelve recipe books at `[world+0x0fe7]`. That reset owner is tighter now too: it does not just
clear one anonymous slab. It first zeroes exactly `0x9b` dwords plus one trailing byte from
`[world+0x4cc2]`, then separately clears the much larger late runtime slab rooted at
`[world+0x66be]`, and also explicitly resets several already-grounded outcome, rule, and policy
`[world+0x66be]`, immediately reseeds setup-preview validity byte `[world+0x66be] = 1`,
reseeds the same editor-owned `Minimum Start Year`, `Default Start Year`, and `Maximum Start
Year` trio inside that same block at `[world+0x66ca] = 0x726`, `[world+0x66d2] = 0x74e`, and
`[world+0x66ce] = 0x7d0`, reseeds the four neighboring startup dwords
`[world+0x6a68/+0x6a6c/+0x6a70/+0x6a74] = 1`, sets the first chairman-slot special occupied-seat
byte `[world+0x69db] = 1`, and also seeds the same scenario-metadata editor band later consumed
by `map_editor_scenario_metadata_panel_refresh_controls` `0x004ca790` and rewritten by
`map_editor_scenario_metadata_panel_handle_message` `0x004cb4a0`, whose write-side body copies
bounded description and briefing texts into `[world+0x672e/+0x4f30/+0x5ae9]`, flips briefing
selector `0x621f50`, toggles `[world+0x66de/+0x66f3]`, and reparses the start-year trio with the
same `1829..2100` clamp plus `minimum <= default <= maximum` normalization, and also
explicitly resets several already-grounded outcome, rule, and policy
lanes: the scenario-rule table `[world+0x4a7f..+0x4b3f]` plus trailing scalar `[world+0x4b43]`,
the outcome-status text root `[world+0x4b47]`, outcome or cheat words `[world+0x4a73/+0x4a77/+0x4a7b]`,
the queued-event and fast-forward latches `[world+0x46c38/+0x46c3c/+0x66a2/+0x66a6/+0x66aa]`,
the byte-policy and cached-scalar cluster `[world+0x4c74/+0x4c78/+0x4c7c/+0x4c88/+0x4c8c..+0x4c99/+0x4c9a/+0x4c9e/+0x4ca6/+0x4caa/+0x4cae/+0x4cb2/+0x4cb6/+0x4cba]`,
and the trailing float strip `[world+0xbce..+0xbf6]` before reseeding its fixed defaults. That
default reseed is concrete now too: `0x00436d10` restores `[world+0x0be2]` and its live mirror
@ -593,23 +672,25 @@ The same brush strip is tighter now too:
`0x00435603`, reruns candidate filter or visible-count maintenance through `0x0041e970`, and
refreshes the cached available-locomotive rating through `0x00436af0`, which now reads as a
year-tiered baseline `110/200/300` plus the strongest surviving live locomotive rating filtered
by era-policy and named-availability gates. So the aligned scalar
by era-policy and named-availability gates, then normalized through the fixed `100/220` scale
pair and clamped into the final `50..100` cache band at `[world+0x4cbe]`. So the aligned scalar
plateau `0x0fa7..0x0fe7` now has a stronger boundary than “some bytes before the recipe books”:
it is the later save-side neighbor immediately in front of a reset-and-rebuild band whose
downstream consumers are already grounded. Local operand-side evidence is negative in a useful
way too: after the bulk clear at `0x00436d10`, the current binary does not expose direct
literal-offset reads or writes for the first dwords in `[world+0x4cc2..]`, so the opening slice
of that plateau is currently better treated as one save-visible late-state family with a grounded
reset owner than as a hidden cluster of missed per-field seams. The local layout seam is
therefore closed at the current evidence level even though we still do not have live semantic
names for most of the plateau dwords themselves. The adjacent startup-side special-case gate is
of that plateau is currently better treated as one reset-owned late-state scalar plateau ahead
of the recipe books than as a hidden cluster of missed per-field seams. The local layout seam is
therefore closed at the current evidence level, and the family-level naming is now stable even
though we still do not have per-dword decodes for most of that opening scalar run. The adjacent
startup-side special-case gate is
bounded now too: `0x00436c70` is not another generic late-state scalar helper. It is one
hard-coded tutorial-signature predicate that only returns true when selector `0x10` is paired
with the `384x448` map-size check, a live city count of `0x41`, first city name
`Italy - North`, the current shell-side file/scenario root satisfying `0x4331e0 == 0x16`, and
row `7` subtype byte `0x6`; the neighboring string table points at `Tutorial_2.gmp`. So this
branch belongs with the startup/load special-case gates beside the reset owner, not with the
unresolved per-dword semantics of the `0x4cc2..` plateau itself. The
now-conservatively named `0x4cc2..` late-state scalar plateau itself. The
serializer-side evidence now sharpens that same boundary too: current local disassembly of
`0x00446240` writes the leading six-dword tuning band, then falls into the world-grid and
secondary-raster loops, and only after those loops jumps straight to `[world+0x0fe7]` for the
@ -641,6 +722,16 @@ The same brush strip is tighter now too:
`0x004bd4a0`, `0x00515820` is the focus-and-follow callback that republishes a selected train id
into the shell detail controller and can recenter the world through the linked route object, and
`0x005158b0` is the inactive-side lower-row refresh callback into `0x004bf320`.
`[world+0x4cba]` is tighter in the same way now: it is the shared selected station id mirrored
by the shell-side station-list and station-detail families, not just another save-visible late
dword in the reset slab. `shell_station_list_window_refresh_rows_selection_and_status`
`0x00506f30` restores `[world+0x4cba]` into the paired row controls `0x61a9/0x61aa` when that
id still resolves to one live current-company station; `shell_station_list_window_handle_message`
`0x005071e0` mirrors direct top-list row activations and side-picker results back into the same
latch before optional recentering and detail-panel transition; and `shell_station_detail_window_handle_message`
`0x00505e50` mirrors the current detail-manager station into `[world+0x4cba]` on setup. The
earlier writes at `0x0050727a` and `0x005072cd` therefore now sit cleanly inside the same
station-list selection seam rather than pointing to a separate late-state owner.
The neighboring bytes `[world+0x4c90/+0x4c91/+0x4c92]` are narrower now too, but only in the
negative sense: the current binary still shows no grounded non-reset readers or writers for
those three one-byte lanes. In local static evidence they only appear in the shared
@ -657,14 +748,31 @@ The same brush strip is tighter now too:
`[world+0x0bde]`; the write path is asymmetric too, because lane `0` uses its dedicated divisor
at `0x005c85d8` while the trailing five lanes share the centered `(value-0xce)/0x005c87f8`
path. The `.smp` save/load pair persists that same six-dword band; the post-load generation
pipeline `0x004384d0` reseeds `[world+0x0bde]` from `[world+0x0be2]` before its early setup
passes; and `world_region_query_projected_structure_count_scalar_by_category` `0x004234e0`
consumes `[world+0x0bde]` as one global multiplier on both its live-region and fallback
projected-structure branches. The editor-side read path is explicit on the same seam:
pipeline `0x004384d0` reseeds `[world+0x0bde]` from `[world+0x0be2]`, runs the preliminary
candidate-availability prepass `0x00437737(0)` on setup calls, and only then enters its visible
`318/320/321/319/322` progress lanes. `world_region_query_projected_structure_count_scalar_by_category`
`0x004234e0` consumes `[world+0x0bde]` as one global multiplier on both its live-region and
fallback projected-structure branches, but the branch split is tighter now too: only the
live-region side also multiplies by cached building-count slot `[region+0x242]`, while the
fallback side swaps to alternate difficulty table `0x005f33cc` and omits that count multiplier
entirely. The same post-load owner is tighter on its gated setup phases too: if shell-state gate
`[0x006cec74+0x174]` is live while master editor-mode latch `[0x006cec74+0x68]` stays clear, it
marks one-shot latch `[world+0x46c3c] = 1`, runs `0x00421c20(1.0, 1)`, and then clears that
latch again; if `[0x006cec74+0x178] > 0` under the same editor-mode clear gate it follows with
`simulation_run_chunked_fast_forward_burst` `0x00437b20`. The `319` lane is tighter in the same
way: when startup latch `0x006cd8d8` is clear it stamps `[0x006cec7c+0x79] = 1`, ensures
`[world+0x6a6c] >= 1`, forces chairman-seat byte `[world+0x69db] = 1`, and then re-enters
`0x004377a0` before the later route-entry and placed-structure refresh families. The
editor-side read path is explicit on the same seam:
`map_editor_economic_cost_panel_refresh_preview_curve_and_numeric_rows` `0x004caaf0` reads the
same six-float band, builds the live preview curve on control `0x5be1`, and republishes the six
numeric value rows through controls `0x5bce/0x5bd0/0x5bd2/0x5bd4/0x5bd6/0x5bd8`, so the panel is
now grounded as a full read/write save-state owner rather than only a slider mutator. The
neighboring `Cities/Regions` editor page is now a second grounded live consumer instead of just a
page-local constructor: `map_editor_city_region_panel_format_selected_region_detail_stats_and_projected_building_rows`
`0x004cc340` reads the same leading tuning lanes `[world+0x0be2/+0x0be6/+0x0bea/+0x0bee]` while
it formats the selected region detail card and projected/live structure rows through
`0x00422900` and `0x004234e0`. The
recurring maintenance side is grounded too:
`simulation_service_periodic_boundary_work` `0x0040a590` derives one bounded bucket from the
selected-year gap, reads the corresponding float from the five-slot table
@ -716,8 +824,10 @@ The same brush strip is tighter now too:
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
save-file family clustering is now strong enough to close the load/save seam at the current
evidence level: those later tail scalars are one bounded post-sentinel special-condition save
tail rather than a hidden extra serializer family. Individual dwords inside that tail still lack
per-lane decodes, but the family-level name is now stable. 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 inside
@ -726,7 +836,12 @@ The same brush strip is tighter now too:
`world_refresh_selected_year_bucket_scalar_band` `0x00433bd0`,
`scenario_state_ensure_derived_year_threshold_band` `0x00435603`,
`structure_candidate_collection_refresh_filter_and_year_visible_counts` `0x0041e970`, and
`scenario_state_refresh_cached_available_locomotive_rating` `0x00436af0`. That restore now
`scenario_state_refresh_cached_available_locomotive_rating` `0x00436af0`. The selected-year
companion itself is tighter now too: `0x00433bd0` picks one scalar from the fixed 21-entry year
ladder, seeds direct bucket lanes `[world+0x65/+0x69/+0x6d]`, then rebuilds the dependent
complement trio `[world+0x71/+0x75/+0x79]` and the later scaled companion trio
`[world+0x7d/+0x81/+0x85]` from that same source after the build-version-sensitive clamp through
`0x00482e00(0x006cec74)`. 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
@ -795,8 +910,8 @@ The same brush strip is tighter now too:
the classic slab preserves the same staged-profile string core and the same middle-band setup
structure, but in the checked classic saves the branch-driving launch/campaign latches stay zero
and most surrounding bytes remain empty. So the local static seam is closed here too: this is the
same serialized setup/runtime profile family, and the remaining work is only the exact meaning of
the fixed middle bytes and the skewed front-edge framing in the classic serializer. One 1.05-era
same serialized setup/runtime profile family, with the classic-only extras carried
conservatively as the fixed middle strip and the skewed front-edge framing. 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
@ -837,17 +952,39 @@ The same brush strip is tighter now too:
too: `map-and-scenario-content-load.md` already grounds `[profile+0x79]` as the live-row
threshold lane used by the payload-row draw callback and `[profile+0x7b]` as the current scroll
or row-index lane adjusted by controls `0x0ce6/0x0ce7`. The current file-side 1.05 corpus now
supports a stronger boundary than the old “still open wider layout” note: this compact slab is
supports a stronger boundary than the earlier wider-layout note: this compact slab is
not a byte-for-byte dump of the staged `0x108`-byte runtime profile, but a closely related
serialized profile payload with a grounded setup core. The direct core lanes match the staged
profile family (`+0x77`, `+0x79`, `+0x7b`, `+0x82`, `+0x87`, `+0x97`, `+0xc5`, and the paired
`+0x10/+0x43` string bands), while the stable presence byte at `+0x0f`, the leading dword at
`+0x00`, the scenario-sensitive middle word around `+0x80`, and the scenario-save-only
`+0x88..+0x8b = 66 67 68 69` band show that the save slab also carries file-format-specific
staging beyond the runtime profile core. The setup-side payload loader is tighter on the same
seam now too: `shell_setup_load_selected_profile_bundle_into_payload_record` `0x00442400` does
not load one single canonical payload shape. In the ordinary saved-profile lane it reads one
small startup payload dword through the ordinary tag-`1` family, then one raw `0x03c2` preview
block, and expands that byte surface into packed `ARGB` dwords rooted at `payload+0x03c2`. That
same ordinary-profile dword is also
stored into shared global `0x00620e94`, which later reappears in the early and late world-entry
threshold gates. In the map-style lane `0x00442400` instead restores the larger setup payload
family under `0x2ee0/0x2ee1`, and current local disassembly now makes the split explicit: this
tagged map-style branch does not perform the `0x00620e94` store at all. Bundle versions
`>= 0x3f7` restore the full `0x403c2`-byte setup-preview payload record, while older bundles
only restore `0x402c9` bytes and explicitly zero the trailing `0x1003e`-byte tail at
`payload+0x2c9`. That keeps the setup payload family aligned with the later live world block
`[world+0x66be]` without overstating it as one uniform file layout across all setup/profile
sources. The current ownership boundary is tighter now too: a direct full-binary xref sweep only
turns up the already-grounded two direct writers to `0x00620e94`, namely the ordinary saved-profile
path in `0x00442400` and the package-save prelude seed `0x00444dd0 = 0x26ad`; `0x00441ec0` only
snapshots that dword into the transported sidecar strip, while `0x00437737` and `0x00444dc5`
only compare against it.
framing and scenario-family metadata around that shared setup core. So the local static seam is
closed here: those grounded lanes belong to one serialized setup-profile slab, while the
surrounding extra bytes are serializer-owned framing rather than stray unrelated runtime fields.
The remaining work is only to name that framing more precisely. The loader-side
surrounding extra bytes now carry conservative framing names rather than reading as stray runtime
fields. The leading dword is the serializer-tag lane, `+0x0f` is the stable presence byte, the
classic branch has one skewed front edge plus one fixed middle strip, the 1.05 middle word around
`+0x80` is the scenario-sensitive profile-framing lane, and `+0x88..+0x8b` is the
scenario-save-only extra band. 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
@ -901,17 +1038,30 @@ The same brush strip is tighter now too:
`[candidate+0x2a]` divided by the descriptor amount, while nonzero mode rows bypass that cap
scaling. The shared reset side is explicit now too: `0x00436d10` is the scenario-state
reset-and-rebuild owner beneath startup and world-entry. It zeroes the late scenario-state bands,
seeds the fixed year defaults and chairman-slot rows, reseeds the same six-dword economic-tuning
seeds the fixed minimum/default/maximum start-year trio and chairman-slot rows, reseeds the same
six-dword economic-tuning
band later serialized at `0x00446240`, restored at `0x00446d40`, and edited through
`0x004ca980/0x004caaf0/0x004cadf0`, rebuilds the two named-availability collections at
`[state+0x66b2]` and `[state+0x66b6]`, zero-fills the twelve recipe books at `[state+0x0fe7]`,
then only re-seeds their per-book name lane from the fixed `# %1` format string at `0x005c9f78`,
refreshes selected-year state through `0x00409e80`,
`0x00433bd0`, `0x00435603`, and the year-gap scalar helper `0x00434130`, and only then
re-enters `0x00435630`, `0x0041e970`, `0x00412bd0`, and `0x00436af0`. The loader-side coverage
then only re-seeds their per-book name lane from the fixed `# %1` format string at `0x005c9f78`.
Its selected-year import tail is explicit now too: in the campaign/setup-but-not-sandbox lane it
copies profile year word `[profile+0x77]` into scenario default start year `[state+0x66d2]`,
seeds the
Jan-1 tuple lanes `[state+0x05/+0x09]` through `0x0051d3f0`, conditionally decrements that chosen
year through the shell/tutorial gates at `[0x006cec74+0x68/+0x178]` and `[0x006cec78+0x4af7]`,
and only then rebuilds the absolute selected-year counter through `0x0051d390 -> 0x00409e80`,
the selected-year bucket band through `0x00433bd0`, the derived year-threshold band through
`0x00435603`, and the year-gap scalar helper `0x00434130`, which now reads more tightly as a
two-stage bridge that first stores one current-year-minus-1850 normalized float into
`[world+0x4ca2]` and then immediately reclamps that same field into the closed `1/3..1.0`
band by flooring sub-threshold values to literal `0.333333...` and capping larger values at
`1.0`. Only after that
selected-year and
calendar chain does it re-enter `0x00435630`, `0x0041e970`, `0x00412bd0`, and `0x00436af0`. The loader-side coverage
around world entry is tighter now too. `0x00443a50` does not just “restore the late profile
block” after the tagged collections load: it re-enters `0x00436d10`, restores `[world+0x66be]`
from `0x2ee0/0x2ee1`, immediately re-normalizes that late setup/status payload through
block” after the tagged collections load: it re-enters `0x00436d10`, restores the late
setup-preview payload block `[world+0x66be]` from `0x2ee0/0x2ee1`, immediately re-normalizes that
payload through
`shell_setup_payload_seed_unique_row_category_bytes_and_marker_slots` `0x0047bc80`, reloads the
`0x108`-byte profile block, mirrors the campaign/setup byte
`[profile+0xc5]` and sandbox launch byte `[profile+0x82]` into `[world+0x66de/+0x66f2]`,
@ -960,7 +1110,7 @@ The same brush strip is tighter now too:
`(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 fixed-record
that they are coarse scenario-family or source-template framing above the stable fixed-record
header strip `0x332e/0x1/0x22/0x2/0x2/(capacity)/(count)/0x0/0x1`, with `0xcdcdcdcd` still
plausibly acting as one reused filler or sentinel lane rather than a meaningful numeric
threshold. Current exported
@ -972,9 +1122,10 @@ The same brush strip is tighter now too:
the non-`.smp` package-save prelude does call the shared indexed-collection serializer
`indexed_collection_serialize_header_and_live_entry_payload_band` `0x00517d90` on those same two
runtime collections before continuing into the later tagged-save families. So the anonymous
file-side `header_word_0/1` pair is no longer excluded by the package-save branch alone, but it
is still not directly explained by the `.smp` save family or by one grounded loader-side field
owner for those two words themselves.
file-side `header_word_0/1` pair is no longer excluded by the package-save branch alone, but the
strongest current ownership boundary is still outside the direct `.smp` runtime row payload:
they behave like outer source-family framing above the stable fixed-record table rather than like
one proven loader-side field pair consumed inside the live named-availability collections.
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
@ -997,14 +1148,19 @@ The same brush strip is tighter now too:
The non-`.smp` package side is explicit now too. The fallback owner
`map_bundle_open_reference_package_and_serialize_early_world_datasets` `0x00444dd0` is not a
generic database opener; it is the early package-save prelude that seeds stage/progress globals
`0x00620e94/0x0062bec8/0x0062becc/0x0062bed0`, opens one `0x30d40` bundle, and then serializes
the first direct save band in fixed order: chunks `0x32c8/0x32c9`, the late world-status block
`[world+0x66be]`, the shared indexed-collection serializer `0x00517d90` over candidate named-
availability collection `[world+0x66b2]`, chunk `0x32dc`, the staged `0x108` profile block
under `0x3714/0x3715`, the same shared serializer over locomotive named-availability collection
`[world+0x66b6]` under `0x32dd`, then
`world_serialize_runtime_grid_and_secondary_raster_tables_into_bundle` `0x00449520` for the
early world-grid/core-raster band under chunk families `0x2ee2..0x2f44`, then
`0x00620e94/0x0062bec8/0x0062becc/0x0062bed0`, opens one `0x30d40` bundle, and, in the editor-map
package branch, first runs
`scenario_state_preseed_named_candidate_availability_overrides_from_editor_map_placed_structures`
`0x00436a70`, which walks qualifying placed structures, resolves their linked candidate/runtime
owners, and upserts boolean availability overrides from owner names before the later direct
collection save. It then serializes the first direct save band in fixed order: chunks
`0x32c8/0x32c9`, the late setup-preview payload block `[world+0x66be]`, the shared
indexed-collection serializer `0x00517d90` over candidate named-availability collection
`[world+0x66b2]`, chunk `0x32dc`, the staged `0x108` profile block under `0x3714/0x3715`, the
same shared serializer over locomotive named-availability collection `[world+0x66b6]` under
`0x32dd`, then `world_serialize_runtime_grid_and_secondary_raster_tables_into_bundle`
`0x00449520` for the early world-grid/core-raster band under chunk families `0x2ee2..0x2f44`,
then
`aux_candidate_collection_serialize_records_into_bundle_payload` `0x00416a70` for the direct
`0x0062b2fc` source-or-auxiliary record family, then
`placed_structure_collection_serialize_local_runtime_records_into_bundle` `0x00413440`, the
@ -1020,8 +1176,19 @@ The same brush strip is tighter now too:
package tail is explicit now too:
after the manager families it writes one direct dword from `[world+0x19]`, a zeroed `0x1f4`-byte
slab under chunk `0x32cf`, closes the bundle, 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.
then conditionally emits the companion-image sidecar through `0x00441f70` when companion-image
byte `[world+0x66c8]` is set and the companion-payload rewrite sidecar through `0x00442900`
when companion-payload byte `[world+0x66c9]` is set. The companion-payload pair is tighter now
too: `0x004427c0` is the `.tmp`-extension rewrite owner that rewrites one whole sidecar file via
`0x00553000`, while `0x00442900` is the path-plus-stored-offset sibling that rewrites the same
middle band from the byte offsets already staged in `[record+0x18/+0x1c]`. The producer boundary
is tighter than the older shorthand too: current local xrefs still do not show standalone
byte-local writers for `[world+0x66c8]` and `[world+0x66c9]` in the package path, but the two
bytes are still written by bulk `[world+0x66be]` copy lanes on restore and multiplayer mirror
through `0x00446d40` and `0x0046cf40`. The ordinary producer is explicit now too: settings
dwords `[0x006d4024+0x11471e]` and selector-rooted `0x0062bec4` feed payload bytes `+0x0a/+0x0b`
through `0x00442ba0`, so the package path is reusing those setup-preview payload lanes rather
than inventing separate byte-local sidecar gates.
The two outer shell coordinators above those package and runtime branches are explicit now too.
`shell_map_file_entry_coordinator` `0x00445ac0` is no longer just the generic open path: it
clears the caller-supplied success dword, increments nested open counter `0x0062be6c`, and then
@ -1035,13 +1202,17 @@ The same brush strip is tighter now too:
`shell_file_request_dialog_collect_target_path` `0x004dd010` with load modes
`4=.gmp`, `8=.gms`, `9=.gmc`, and `10=.gmx`. The save-side sibling
`shell_map_file_world_bundle_coordinator` `0x00445de0` now has the same shape: it seeds save-side
selector `0x0062bec4`, accepts either an explicit caller stem, a Quicksave stem from `0x005ca9cc`,
selector `0x0062bec4` from shell fields `[0x006d4024+0x11472a]` or `[+0x11472e]` according to the
same early branch that chooses the save-side variant, accepts either an explicit caller stem, a Quicksave stem from `0x005ca9cc`,
or the localized `Autosave` stem from string id `387`, chooses save modes
`3=.gmp`, `0=.gms`, `1=.gmc`, `2=.gmx`, or side-owner-only `11=.gmt`, then either appends the
fixed `.smp` token and re-enters `world_runtime_serialize_smp_bundle` `0x00446240` when a live
runtime world exists or falls back to the package side through `0x00444dd0`, with the `.gmt`
side-owner branch packaged instead into `0x006cd8d8+0x8f48` and submitted through
`multiplayer_preview_dataset_submit_transport_request` `0x00469d30`.
`multiplayer_preview_dataset_submit_transport_request` `0x00469d30`. Those two selector dwords
are now bounded as part of the same settings-window-owned sidecar family toggled by
`shell_settings_window_handle_message_dispatch_and_persist_display_runtime_sidecar_family`
`0x00464c80`, not as hidden save/load-only fields.
The paired load-side seam is explicit now too. `aux_candidate_collection_construct_stream_load_records_and_refresh_runtime_followons`
`0x004196c0` rebuilds the same `0x0062b2fc` family from the tagged import source around
`0x005c93fc`, re-entering
@ -1159,8 +1330,10 @@ The same brush strip is tighter now too:
The region-side restore band is explicit now too. After seeding default regions through
`world_region_collection_seed_default_regions` `0x00421b60`, `0x00446d40` restores one counted run
of fixed `0x29`-byte region rows, resolves or allocates each region through
`0x00421660/0x00518140`, republishes pending amount `[region+0x272]` plus paired pointers
`[region+0x25a/+0x25e]`, reruns
`0x00421660/0x00518140`, writes one direct saved dword into `[region+0x272]`, writes one shared
density dword into both `[region+0x25a/+0x25e]`, and on pre-`0x3f3` bundles synthesizes that
shared density dword from the legacy lookup table at `0x005f3a28` keyed by the serialized bucket
field instead of reading it directly, then reruns
`world_region_refresh_profile_availability_display_strings_for_cached_selector_0x2f2`
`0x004204c0`, and then reloads the per-region profile
subcollection at `[region+0x37f]`. It then restores one second counted run of fixed `0x1e+4`
@ -1177,10 +1350,10 @@ The same brush strip is tighter now too:
`0x0fa7..0x0fe7` family-shaped slab lead into the fixed recipe-book root at `0x0fe7`. The
loader-side bridge explicitly fans the first persisted tuning lane back into the neighboring
manager scalar at `[world+0x0bde]` before the later tagged database and rule-band restores run.
The adjacent candidate-side recipe rebuild is structurally closed now too: the remaining
save/load gap is no longer how the loader reconstructs the runtime descriptor and
cargo-membership bands, but what the imported supply-marker token family means before those
bands are projected back into live cargo ids. That same selected-year bridge also has the
The adjacent candidate-side recipe rebuild is structurally closed now too: the local binary now
only treats the imported supply-marker token family as opaque preserved scenario payload before
those bands are projected back into live cargo ids through exact-name matching. That same
selected-year bridge also has the
explicit companion `world_refresh_selected_year_bucket_scalar_band` `0x00433bd0`, which now
reads as the real selected-year bucket rebuild owner rather than a generic follow-on math body:
it bins packed year word `[world+0x0d]` against the 21-step threshold ladder at `0x005f3980`,

View file

@ -27,6 +27,7 @@
`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.
- Current Boundary: the current static boundary keeps simulation cadence rendezvous inside the same
shell-owned presentation path. After world entry, later stepping still re-enters
`shell_state_service_active_mode_frame` and `shell_service_frame_cycle` rather than surfacing a
detached gameplay-only presentation loop.

View file

@ -46,9 +46,16 @@
paired wrappers
`0x004423a0/0x004423d0` bracket that same band while also servicing the live TrackLay.win and
StationPlace.win tool objects through `0x0050e070`, `0x00507a50`, `0x0050a530`, and
`0x0050e1e0`. The live `.smp` serializer uses the higher pair, while `world_entry_transition_and_runtime_bringup`
`0x00443a50` and `world_load_saved_runtime_state_bundle` `0x00446d40` also use the raw
push/pop pair directly around their heavier bundle and status spans.
`0x0050e1e0`. The live `.smp` serializer uses the higher pair, while the neighboring world-entry
branch `0x00443a50` and non-`.smp` package-save tail under `shell_map_file_world_bundle_coordinator`
`0x00445de0` now show the raw split explicitly. `0x00443a50` calls `0x004422d0` at `0x444e2c`,
services TrackLay.win and StationPlace.win entry-side helpers through `0x444e3b/0x444e4a`, then
later calls `0x00442330` at `0x444ecb` and services the recurring tool frames through
`0x444eda/0x444ee9`. The package-save tail similarly calls `0x00442330` directly at `0x00445a55`
and then services StationPlace.win and TrackLay.win at `0x00445a64/0x00445a73` without
re-entering `0x004423d0`. So the save/load-side tool choreography is no longer one uniform wrapper
path across world-entry, package-save, and `.smp` branches, and current local evidence does not
show `0x00446d40` itself directly re-entering either the raw pair or the higher wrapper pair.
`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
@ -1785,7 +1792,9 @@ The controller window dispatcher now looks like the first grounded input
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.
separate gameplay-input stack. The ownership seam is therefore closed at the current local level:
the unresolved pieces are the exact per-tool or per-mode actions beneath that path, not whether a
later non-cursor gameplay branch bypasses the shell controller input roots.
### Evidence
Function-map rows for `shell_controller_window_message_dispatch`,
@ -1918,7 +1927,7 @@ Function-map rows for `shell_controller_window_message_dispatch`,
failure strings 3083 3084 3837 and 3900, the binding-registry lookup path at `0x0045f370`, the
registration block at `0x00460769` through `0x004608e7`, and the localized labels in
`Data/Language/RT3.lng` ids `3466` through `3473`.
### Open Questions
### Current Boundary
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
@ -2122,12 +2131,10 @@ Current evidence grounds the shell-controller-backed input and frame path as the
releases the cached roots `0x006d19d8..0x006d19ec`, frees the per-company cached stock-data
handles in `0x006d1748`, invalidates controls `0x3b60` and `0x3e4e`, clears singleton
`0x006d19f0`, and then tails into the common shell-object teardown. The
remaining adjacent detail-panel gates are now at least structurally bounded too even though their
target families are still unnamed: `0x00434db0` layers extra scenario toggle `[0x006cec78+0x4a7f]`
on top of the broader company-list/detail gate before allowing mode `0x0b`, while `0x00434e20`
only hard-blocks mode `0x06` when all three toggles `[+0x4ab3/+0x4ab7/+0x4abf]` are set and
otherwise, outside editor-map mode, still requires a selected-company id before the direct
openers `0x00440530` and `0x00440690` hand those modes to `shell_detail_panel_transition_manager`.
remaining adjacent detail-panel gates are now bounded tightly enough to carry by family:
`0x00434db0` is the scenario-toggle-gated company-detail mode-`0x0b` availability gate, while
`0x00434e20` is the selected-company-or-editor detail mode-`0x06` availability gate that only
hard-blocks when all three scenario toggles `[+0x4ab3/+0x4ab7/+0x4abf]` are set.
The
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
@ -3089,8 +3096,8 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0`
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
active, in which case it returns byte `[this+0x42]`; the mode-gated query itself is now bounded
as one runtime enable byte on that local object family. 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
@ -3579,7 +3586,8 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0`
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
ownership only in the per-tool sense: the hover-target branch clearly exists, `0x07d6` now looks
like the shared
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
@ -3636,6 +3644,6 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0`
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 `world_rebuild_active_preview_or_tool_overlay_surface_and_publish_normalized_bounds`
`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.
`0x00450520` on the world-mode-`0x17` side path. The remaining local lanes can therefore be
carried conservatively as PaintTerrain per-mode scalar and token bands beneath that already-
grounded mixed shell/world ownership strip, not as another ownership seam.

View file

@ -14,7 +14,7 @@
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.
- Current Boundary: current local evidence keeps this dispatcher on the shell-side UI callback and
detail-panel command path. The live `0x006d0818` lane reads as shell-only, while gameplay world
entry and long-lived frame cadence still rendezvous through the broader shell-owned bring-up and
frame-service coordinators rather than through this command table.

View file

@ -78,8 +78,9 @@ The city bonus formatter no longer depends only on
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.
peer. The broader caller side can now be carried conservatively as the city-connection and
company-start announcement ladder above this formatter family, not as another unresolved edge
inside the peer-scan pair itself.
#### Caller side
The reusable bridge between the status formatter and the
@ -128,9 +129,9 @@ The reusable bridge between the status formatter and the
`Prime Rate` label family all point at the same metric, so this city-connection branch is
reusing the explicit issue-`0x39` prime-rate lane rather than a separate public-opinion or
management-attitude family. The local static seam on this branch is therefore closed: `0x39`
itself is no longer ambiguous here, and the remaining work is only how much of the wider
company-start or city-connection ladder above that helper should be described as
recent-profit/fuel-burden pressure versus the already-grounded prime-rate term.
itself is no longer ambiguous here, and the wider company-start or city-connection ladder can now
be carried conservatively as a recent-profit/fuel-burden pressure lane layered beside the already-
grounded prime-rate term.
The ordinary `StationDetail.win` caller strip is tighter now too. In the non-scenario, non-class
`3/4` branch, `shell_station_detail_window_refresh_controls` `0x00506610` rebuilds the nearby-site
@ -146,8 +147,9 @@ counts entries through `0x0047dc90`, emits the descriptive text block through `0
0x0053f9c0`, and then builds the paired jump-control payload through `0x0053b070 -> 0x0055a040 ->
0x0053f9c0`, with text rows under `0xb40a..0xb40e` and jump controls under `0xb410..0xb414`. So
the local static seam in the ordinary branch is closed: the ownership of those lanes now belongs
to the concrete refresh helpers named here, and the remaining work is only the broader
user-facing meaning of later callers above the row callbacks.
to the concrete refresh helpers named here, and the later callers above them can now be carried
conservatively as the ordinary station-summary presentation lane rather than as a separate
unresolved user-facing family.
The helper strip directly beneath that ordinary branch is explicit now too.
`shell_station_detail_clear_dynamic_rows_and_haul_widgets_if_dirty` `0x005042c0` uses dirty latch
`0x006d16f0` plus the live window singleton `0x006d16d8` to clear the candidate-service and
@ -169,8 +171,9 @@ restyles the lower action controls into affordance states `0x65` or `0x87` accor
candidate class byte `[candidate+0x8c]`, keeping `0xb3bb/0xb3bd/0xb3be` active only for class `0`
and `0xb3bc/0xb3bf` active only for class `1`. That closes the helper seam beneath
`shell_station_detail_window_handle_message` `0x00505e50` and
`shell_station_detail_window_refresh_controls` `0x00506610`: the remaining work in this branch is
above those message and refresh owners, not inside the lower helper strip itself. One layer higher,
`shell_station_detail_window_refresh_controls` `0x00506610`: the branch now reads cleanly as the
linked-site mutation action lane above those message and refresh owners, not as a remaining
helper-side seam. One layer higher,
the lower action dispatch is no longer opaque either. In the message-`0xcb` path,
`shell_station_detail_window_handle_message` first reduces the clicked control to one small family
split: `0xb3bd` selects family `1`, while the sibling controls select family `2`. It then reduces
@ -215,8 +218,8 @@ The neighboring helper
overlay query at `0x0047e690` only consumes the leading `u16` peer-site key when it walks that
list and does not currently read the trailing `u32` companion payload at all. So the local
overlay-side seam is closed: route-list ownership and overlay dependence on that payload are no
longer open here, and the remaining work is only the broader meaning of the payload for other
caller families.
longer open here, and the trailing `u32` can now be carried conservatively as the route-entry
companion payload word for the non-overlay caller families that still preserve it.
#### Route-entry and Cache Side
The adjacent helper strip is tighter now too.
@ -256,5 +259,6 @@ The nearby linked-instance raster wrappers are no
`placed_structure_resolve_linked_instance_secondary_raster_class_record_via_world_query`
`0x0047f1f0` now bound the two record-returning lanes used by the later world-side and
linked-peer filters. So the local static seam in this neighborhood is closed too: route-entry and
terrain-class helper ownership are no longer open here, and the remaining work is only the
broader user-facing meaning of the later callers that consume those gates.
terrain-class helper ownership are no longer open here, and the later callers can now be carried
conservatively as linked-instance secondary-raster class filter families rather than one broader
unnamed user-facing band.