Split control-loop atlas into section files
This commit is contained in:
parent
c28bafbc64
commit
1040a131da
21 changed files with 5356 additions and 3868 deletions
|
|
@ -16,7 +16,8 @@ minimal PE32 Rust hook that can load into RT3 under Wine without changing behavi
|
|||
Bootstrap design and workflow documents live in `docs/`.
|
||||
|
||||
- `docs/README.md`: handbook index and target hashes
|
||||
- `docs/control-loop-atlas.md`: high-level loop and subsystem atlas
|
||||
- `docs/control-loop-atlas.md`: compatibility index for the split atlas
|
||||
- `docs/control-loop-atlas/`: canonical atlas section files
|
||||
- `docs/setup-workstation.md`: toolchain baseline and local setup
|
||||
- `docs/re-workflow.md`: repeatable reverse-engineering workflow
|
||||
- `docs/function-map.md`: canonical function-map schema and conventions
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ project is already mature.
|
|||
- `setup-workstation.md`: toolchain baseline and local environment setup.
|
||||
- `re-workflow.md`: how to analyze the binary, record findings, and export reusable artifacts.
|
||||
- `function-map.md`: canonical schema and conventions for function-by-function mapping.
|
||||
- `control-loop-atlas.md`: curated atlas of top-level loops, gateways, and subsystem handoffs.
|
||||
- `control-loop-atlas.md`: compatibility index for the split atlas, preserving legacy anchors.
|
||||
- `control-loop-atlas/`: canonical section files for the atlas narrative.
|
||||
- `runtime-rehost-plan.md`: bottom-up runtime replacement plan and milestone breakdown.
|
||||
|
||||
## Repo Conventions
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# Atlas Notes
|
||||
|
||||
These notes are a finer-grained navigation layer over the canonical longform atlas in
|
||||
[control-loop-atlas.md](/home/jan/projects/rrt/docs/control-loop-atlas.md).
|
||||
These notes are a finer-grained navigation layer over the canonical split atlas in
|
||||
[docs/control-loop-atlas/](/home/jan/projects/rrt/docs/control-loop-atlas/README.md), with
|
||||
[control-loop-atlas.md](/home/jan/projects/rrt/docs/control-loop-atlas.md) retained as a
|
||||
compatibility index.
|
||||
|
||||
Current subsystem views:
|
||||
|
||||
|
|
@ -16,7 +18,9 @@ Scope policy:
|
|||
|
||||
- [function-map.csv](/home/jan/projects/rrt/artifacts/exports/rt3-1.06/function-map.csv) remains
|
||||
the single canonical ledger.
|
||||
- [control-loop-atlas.md](/home/jan/projects/rrt/docs/control-loop-atlas.md) remains the canonical
|
||||
longform narrative.
|
||||
- [docs/control-loop-atlas/](/home/jan/projects/rrt/docs/control-loop-atlas/README.md) is the
|
||||
canonical atlas narrative.
|
||||
- [control-loop-atlas.md](/home/jan/projects/rrt/docs/control-loop-atlas.md) remains the
|
||||
compatibility index for older links.
|
||||
- These files are curated subsystem views intended to keep active reverse-engineering passes
|
||||
smaller and easier to navigate.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Primary atlas sources:
|
||||
|
||||
- [control-loop-atlas.md#input-saveload-and-simulation](/home/jan/projects/rrt/docs/control-loop-atlas.md#input-saveload-and-simulation)
|
||||
- [input-save-load-and-simulation.md](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md)
|
||||
|
||||
This view isolates the company-side shell panes, linked-transit company maintenance, city-connection
|
||||
news, and annual finance-policy thresholds.
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
Primary atlas sources:
|
||||
|
||||
- [control-loop-atlas.md#input-saveload-and-simulation](/home/jan/projects/rrt/docs/control-loop-atlas.md#input-saveload-and-simulation)
|
||||
- [control-loop-atlas.md#station-detail-overlay](/home/jan/projects/rrt/docs/control-loop-atlas.md#station-detail-overlay)
|
||||
- [input-save-load-and-simulation.md](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md)
|
||||
- [input-save-load-and-simulation.md#station-detail-overlay](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md#station-detail-overlay)
|
||||
|
||||
This view isolates the map-editor page families, candidate and site-service runtime chain, and the
|
||||
station-detail or station-list read-side consumers.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Primary atlas source:
|
||||
|
||||
- [control-loop-atlas.md#multiplayer-session-and-transport-flow](/home/jan/projects/rrt/docs/control-loop-atlas.md#multiplayer-session-and-transport-flow)
|
||||
- [multiplayer-session-and-transport-flow.md](/home/jan/projects/rrt/docs/control-loop-atlas/multiplayer-session-and-transport-flow.md)
|
||||
|
||||
Current grounded owners:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Primary atlas sources:
|
||||
|
||||
- [control-loop-atlas.md#input-saveload-and-simulation](/home/jan/projects/rrt/docs/control-loop-atlas.md#input-saveload-and-simulation)
|
||||
- [input-save-load-and-simulation.md](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md)
|
||||
|
||||
This note isolates the lower route-entry, linked-site, route-link, and auxiliary tracker families
|
||||
that are easy to lose inside the broader runtime section.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Primary atlas sources:
|
||||
|
||||
- [control-loop-atlas.md#input-saveload-and-simulation](/home/jan/projects/rrt/docs/control-loop-atlas.md#input-saveload-and-simulation)
|
||||
- [input-save-load-and-simulation.md](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md)
|
||||
|
||||
This view keeps together the runtime-side shell input path, world-view tool surfaces, world bring-up
|
||||
pipeline, and the large shell-window families that hang off the recurring frame owner.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Primary atlas source:
|
||||
|
||||
- [control-loop-atlas.md#crt-and-process-startup](/home/jan/projects/rrt/docs/control-loop-atlas.md#crt-and-process-startup)
|
||||
- [control-loop-atlas.md#bootstrap-and-shell-service-bring-up](/home/jan/projects/rrt/docs/control-loop-atlas.md#bootstrap-and-shell-service-bring-up)
|
||||
- [control-loop-atlas.md#shell-ui-command-and-deferred-work-flow](/home/jan/projects/rrt/docs/control-loop-atlas.md#shell-ui-command-and-deferred-work-flow)
|
||||
- [control-loop-atlas.md#presentation-overlay-and-frame-timing](/home/jan/projects/rrt/docs/control-loop-atlas.md#presentation-overlay-and-frame-timing)
|
||||
- [control-loop-atlas.md#map-and-scenario-content-load](/home/jan/projects/rrt/docs/control-loop-atlas.md#map-and-scenario-content-load)
|
||||
- [crt-and-process-startup.md](/home/jan/projects/rrt/docs/control-loop-atlas/crt-and-process-startup.md)
|
||||
- [bootstrap-and-shell-service-bring-up.md](/home/jan/projects/rrt/docs/control-loop-atlas/bootstrap-and-shell-service-bring-up.md)
|
||||
- [shell-ui-command-and-deferred-work-flow.md](/home/jan/projects/rrt/docs/control-loop-atlas/shell-ui-command-and-deferred-work-flow.md)
|
||||
- [presentation-overlay-and-frame-timing.md](/home/jan/projects/rrt/docs/control-loop-atlas/presentation-overlay-and-frame-timing.md)
|
||||
- [map-and-scenario-content-load.md](/home/jan/projects/rrt/docs/control-loop-atlas/map-and-scenario-content-load.md)
|
||||
|
||||
Current grounded owners:
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
18
docs/control-loop-atlas/README.md
Normal file
18
docs/control-loop-atlas/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Control-Loop Atlas
|
||||
|
||||
This directory holds the canonical atlas narrative split by the original top-level sections from
|
||||
[docs/control-loop-atlas.md](/home/jan/projects/rrt/docs/control-loop-atlas.md).
|
||||
|
||||
Use the compatibility index when an existing link only knows the old single-file path. Use these
|
||||
section files when adding or reviewing grounded narrative content.
|
||||
|
||||
## Sections
|
||||
|
||||
- [crt-and-process-startup.md](/home/jan/projects/rrt/docs/control-loop-atlas/crt-and-process-startup.md)
|
||||
- [bootstrap-and-shell-service-bring-up.md](/home/jan/projects/rrt/docs/control-loop-atlas/bootstrap-and-shell-service-bring-up.md)
|
||||
- [shell-ui-command-and-deferred-work-flow.md](/home/jan/projects/rrt/docs/control-loop-atlas/shell-ui-command-and-deferred-work-flow.md)
|
||||
- [presentation-overlay-and-frame-timing.md](/home/jan/projects/rrt/docs/control-loop-atlas/presentation-overlay-and-frame-timing.md)
|
||||
- [map-and-scenario-content-load.md](/home/jan/projects/rrt/docs/control-loop-atlas/map-and-scenario-content-load.md)
|
||||
- [multiplayer-session-and-transport-flow.md](/home/jan/projects/rrt/docs/control-loop-atlas/multiplayer-session-and-transport-flow.md)
|
||||
- [input-save-load-and-simulation.md](/home/jan/projects/rrt/docs/control-loop-atlas/input-save-load-and-simulation.md)
|
||||
- [next-mapping-passes.md](/home/jan/projects/rrt/docs/control-loop-atlas/next-mapping-passes.md)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
## Bootstrap and Shell Service Bring-Up
|
||||
|
||||
- Roots: `app_bootstrap_main` at `0x00484440`, `bootstrap_init_shell_window_services` at
|
||||
`0x004840e0`, and `shell_install_global_controller` at `0x0051ff90`.
|
||||
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
|
||||
- Key Dispatchers: `app_bootstrap_main`, `bootstrap_init_shell_window_services`,
|
||||
`shell_install_global_controller`, `shell_service_pump_iteration`, graphics config load or
|
||||
default-init helpers, runtime capability probes, and early shell service initializers under the
|
||||
`0x004610..0x0053f0..` branch.
|
||||
- State Anchors: global shell controller pointer `0x006d4024`, sibling display/runtime globals under
|
||||
`0x006d402c` and `0x006d4030`, and host capability flags gathered by
|
||||
`bootstrap_probe_system_profile`.
|
||||
- Subsystem Handoffs: after the global shell controller has been installed the bootstrap path enters
|
||||
the repeating `shell_service_pump_iteration` loop, which services shell-state work and hands each
|
||||
iteration down into the controller frame path; when the shell lifetime ends this same bootstrap
|
||||
path tears the shell bundle down and returns to `app_bootstrap_main`.
|
||||
- Evidence: `startup-call-chain.md`, function-map rows for `app_bootstrap_main`,
|
||||
`bootstrap_init_shell_window_services`, `shell_install_global_controller`,
|
||||
`shell_service_pump_iteration`, `shell_load_graphics_config_or_init_defaults`,
|
||||
`shell_reset_display_runtime_defaults`, and related graphics setup helpers.
|
||||
- Open Questions: whether gameplay or in-engine world stepping later escapes this bootstrap-owned
|
||||
shell loop entirely, or whether gameplay entry still rendezvous with the same outer coordinator
|
||||
before the shell bundle is destroyed.
|
||||
|
||||
17
docs/control-loop-atlas/crt-and-process-startup.md
Normal file
17
docs/control-loop-atlas/crt-and-process-startup.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## CRT and Process Startup
|
||||
|
||||
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and
|
||||
`app_bootstrap_main` at `0x00484440`.
|
||||
- Trigger/Cadence: single process startup path before shell or engine services exist.
|
||||
- Key Dispatchers: `startup_init_tls_state`, `startup_init_file_handle_table`,
|
||||
`startup_run_init_callback_table`, `__setenvp`, `startup_build_argv`,
|
||||
`___crtGetEnvironmentStringsA`, then `app_bootstrap_main`.
|
||||
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
|
||||
- Subsystem Handoffs: exits the generic CRT path at `app_bootstrap_main`, which becomes the first
|
||||
RT3-owned coordinator.
|
||||
- Evidence: `artifacts/exports/rt3-1.06/startup-call-chain.md`,
|
||||
`artifacts/exports/rt3-1.06/function-map.csv`.
|
||||
- Open Questions: exact ownership boundary between compiler CRT shims and the first game-owned
|
||||
bootstrap helper; whether any nontrivial startup callbacks seed game globals before
|
||||
`app_bootstrap_main`.
|
||||
|
||||
4117
docs/control-loop-atlas/input-save-load-and-simulation.md
Normal file
4117
docs/control-loop-atlas/input-save-load-and-simulation.md
Normal file
File diff suppressed because it is too large
Load diff
450
docs/control-loop-atlas/map-and-scenario-content-load.md
Normal file
450
docs/control-loop-atlas/map-and-scenario-content-load.md
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
## Map and Scenario Content Load
|
||||
|
||||
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the larger active-mode profile owner
|
||||
`shell_active_mode_run_profile_startup_and_load_dispatch` at `0x00438890`, the shell-mode
|
||||
switcher `shell_transition_mode` at `0x00482ec0`, the first grounded world-entry branch
|
||||
`world_entry_transition_and_runtime_bringup` at `0x00443a50`,
|
||||
`shell_map_file_world_bundle_coordinator` at `0x00445de0`, reference-database setup via
|
||||
`map_bundle_open_reference_databases` at `0x00444dd0`, and narrower loaders such as
|
||||
`map_load_geographic_label_database` and `map_load_city_database`.
|
||||
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`,
|
||||
map-scenario open paths, and scenario-text export batch commands.
|
||||
- Key Dispatchers: `shell_map_file_entry_coordinator`,
|
||||
`shell_active_mode_run_profile_startup_and_load_dispatch`, `shell_transition_mode`,
|
||||
`world_entry_transition_and_runtime_bringup`, `world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`,
|
||||
`map_bundle_open_reference_databases`, `map_load_geographic_label_database`,
|
||||
`map_load_city_database`, `scenario_text_export_build_language_file`,
|
||||
`scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
|
||||
- State Anchors: shell-side file staging buffers at `0x0062bee0` and `0x0062bec4`, shell and mode
|
||||
globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state
|
||||
allocated through `0x00530c80`, and geography tables rooted at `0x0062b2fc` and `0x0062b268`.
|
||||
- Subsystem Handoffs: the shared `fileopt.win` dialog rooted at `0x004dc670` now looks like the
|
||||
shell-side selector above the two broad file coordinators. Its message handler sets `0x006d07f8`
|
||||
for the load or restore side or `0x006d07ec` for the save or package side before the detail-panel
|
||||
transition manager routes into `shell_map_file_entry_coordinator` or
|
||||
`shell_map_file_world_bundle_coordinator`. The former unresolved third flag at `0x006d07f0` is now
|
||||
accounted for too: it escapes into the standalone `SettingsWindow.win` path through
|
||||
`shell_open_settings_window` rather than another map or save verb. The broad coordinators now hand
|
||||
their interactive work through the shared `filerqst.win` helper at `0x004dd010`, and that helper
|
||||
gives the extension split a firmer shape. The paired editor-map path is now grounded as `.gmp`
|
||||
through load mode `4` and save mode `3`. The remaining non-editor families are no longer anonymous
|
||||
either: `.gmc` is the campaign-scenario branch, backed both by the campaign-screen resignation
|
||||
prompt on `0x006cec7c+0xc5` and by the numbered `%s%02d.gmc` save helper at `0x00517c70`; `.gmx`
|
||||
is the sandbox branch, backed by the shell-side `The briefing is not available in sandbox games.`
|
||||
restriction on `0x006cec7c+0x82`; and the default `.gms` branch is therefore the standalone
|
||||
scenario family. When a live runtime world is already active the same helper bypasses those
|
||||
non-runtime extensions and forces the `.smp` runtime-state branch instead. The auxiliary save-side
|
||||
mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another
|
||||
gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned
|
||||
by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal
|
||||
reference-bundle path when that dataset object is absent. The shell-side mode owner above those
|
||||
file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode
|
||||
switch: its ABI is now grounded as a `thiscall` with two stack arguments because the body reads
|
||||
the requested mode from `[esp+0x0c]` and returns with `ret 8`. The grounded world-entry
|
||||
load-screen call shape is `(4, 0)`, not a one-arg mode switch. The second stack argument is now
|
||||
tighter too: current local evidence reads it as an old-active-mode teardown flag, because the
|
||||
`0x482fc6..0x482fff` branch only runs when it is nonzero and then releases the prior active-mode
|
||||
world through `0x434300`, `0x433730`, and the common free path before clearing `0x006cec78`.
|
||||
The later world-entry reactivation branch correspondingly uses `(1, esi)` rather than `(1, 0)`.
|
||||
The current live hook probes now push the remaining auto-load gap much later too: on the
|
||||
hook-driven path `shell_transition_mode(4, 0)` returns cleanly, and the full old-mode teardown
|
||||
stack under `0x5389c0` now returns too, including `0x5400c0`, the `0x53fe00 -> 0x53f860`
|
||||
remove-node sweep over `[object+0x74]`, and the nearby mode-`2` teardown helper `0x00502720`.
|
||||
The same live run now also reaches and returns from `shell_load_screen_window_construct`
|
||||
`0x004ea620` and the immediate shell publish through `0x00538e50`.
|
||||
Its constructor jump table is tighter now too: mode `1` is not the direct `Game.win`
|
||||
constructor, but the startup-dispatch arm rooted at `0x483012`; mode `2` enters `Setup.win`,
|
||||
mode `3` enters `Video.win`, mode `4` enters the plain `LoadScreen.win` branch at `0x4832e5`,
|
||||
mode `5` enters `Multiplayer.win`, mode `6` enters `Credits.win`, and mode `7` enters
|
||||
`Campaign.win`. The important static correction is that the startup-runtime slice
|
||||
`0x004ea710 -> 0x0053b070(0x46c40) -> 0x004336d0 -> 0x00438890` belongs to mode `1`, not mode
|
||||
`4`. Mode `4` only constructs and publishes the `LoadScreen.win` object through `0x004ea620`
|
||||
and `0x00538e50`. The older hook-driven `(4, 0)` path therefore was not mysteriously skipping
|
||||
the startup-runtime object; it was entering the wrong jump-table arm for that work. The caller
|
||||
split above that owner is tighter now too:
|
||||
`world_entry_transition_and_runtime_bringup` reaches the same owner at `0x443b57` with `(0, 0)`
|
||||
after dismissing the current shell detail panel and servicing `0x4834e0(0, 0)`, while the
|
||||
saved-runtime path at `0x446d7f` does the same before immediately building the `.smp` bundle
|
||||
payloads through `0x530c80/0x531150/0x531360`. That makes the `LoadScreen.win` startup lane the
|
||||
only currently grounded caller that enters `0x438890` with `(1, 0)` instead of `(0, 0)`. The
|
||||
remaining runtime uncertainty is narrower now too: the plain-run logs still show the plain
|
||||
`LoadScreen.win` state under `(4, 0)`, and the corrected allocator-window run now reinforces the
|
||||
same read because the post-construct allocator stream stays empty instead of showing the expected
|
||||
`0x46c40` startup-runtime allocation. The first lower allocator probe on `0x005a125d` was not
|
||||
trustworthy because that shared cdecl body sits behind the thunk and the initial hook used the
|
||||
wrong entry shape, and the first direct thunk hook was also not trustworthy because a copied
|
||||
relative-`jmp` thunk cannot be replayed through an ordinary trampoline. But the later corrected
|
||||
thunk run plus the jump-table decode now agree: the next meaningful hook-driven test is mode
|
||||
`1`, not mode `4`.
|
||||
`mode_id = 2`, and it never advanced into `ready gate passed`, staging, or transition. So that
|
||||
run did not actually exercise the `0x0053b070 -> 0x004336d0 -> 0x00438890` subchain at all.
|
||||
The next runtime pass now lowers the ready-poll defaults to `1` and `0` and adds an explicit
|
||||
ready-count log so the mode-`4` startup lane either stages immediately or shows exactly how far
|
||||
the gate gets. That adjustment worked on the next run: the hook now stages and completes the
|
||||
`shell_transition_mode` path again, with `LoadScreen.win` construction and publish returning
|
||||
cleanly. But the post-publish startup subchain is still unresolved: there is still no trusted
|
||||
`0x46c40` allocator hit, no direct `0x004336d0` entry, and no direct `0x00438890` entry. So
|
||||
the next clean runtime boundary is the tiny `LoadScreen.win` scalar setter at `0x004ea710`,
|
||||
which sits immediately before the `0x0053b070` allocation in the static mode-`4` branch. The
|
||||
immediate next runtime check is even more concrete than the helper hook, though: inspect the
|
||||
state that `0x004ea710` should leave behind. The hook now logs the post-transition
|
||||
`LoadScreen.win` singleton, its field `[+0x78]`, `0x006cec78`, the shell state's `[+0x0c]`
|
||||
active-mode object field, and the startup selector. If `0x004ea710` really ran on the mode-`4`
|
||||
branch, `[LoadScreen.win+0x78]` should no longer be zero after `shell_transition_mode` returns.
|
||||
The latest run answered that directly: after transition return, `field_active_mode_object` is
|
||||
still the `LoadScreen.win` singleton, `0x006cec78` is still null, `[LoadScreen.win+0x78]` is
|
||||
still zero, and the startup selector is still `3`. So the current best read is that RT3 is
|
||||
still parked in the plain `LoadScreen.win` state at transition return rather than having entered
|
||||
the separate runtime-object path yet. That shifts the best next runtime boundary from “deeper
|
||||
inside `shell_transition_mode`” to “what later active-mode service tick, if any, promotes the
|
||||
load-screen object into the startup-dispatch path.” The next run now logs the first few
|
||||
shell-state service ticks after auto-load is attempted with that same state tuple
|
||||
(`0x006cec78`, `[shell_state+0x0c]`, `0x006d10b0`, `[LoadScreen.win+0x78]`, selector), so the
|
||||
next question is very narrow: does one later service tick finally promote the plain
|
||||
`LoadScreen.win` state into the startup-runtime object path, or does it stay frozen as-is? The
|
||||
internal selector split in `0x438890` is tighter now too: `[0x006cec7c+0x01]` is a separate
|
||||
seven-way startup selector, not the shell mode id. Values `1` and `7` load `Tutorial_2.gmp` and
|
||||
`Tutorial_1.gmp`, values `3/5/6` collapse into the same profile-seeded file-load lane through
|
||||
`0x445ac0([0x006cec7c]+0x11, 4, &out_success)`, value `2` is a world-root initialization lane
|
||||
that allocates `0x0062c120` and then forces selector `3`, and value `4` is the setup-side world
|
||||
reset or regeneration lane that rebuilds `0x0062c120` from `0x006d14cc/0x006d14d0` before later
|
||||
world setup continues. The write side is tighter now too: `Campaign.win` writes selector `6`,
|
||||
`Multiplayer.win` writes selector `3` on one pending-status path, and the larger `Setup.win`
|
||||
dispatcher writes selectors `2`, `3`, `4`, and `5` on its validated launch branches. That makes
|
||||
the file-load subfamily read less like one generic save-open branch and more like a shared
|
||||
profile-file lane reused by setup, multiplayer, and campaign owners. The world-entry owner
|
||||
boundary is tighter now too: `world_entry_transition_and_runtime_bringup` at `0x00443a50` no
|
||||
longer stops at the initial shell transition and world allocation head. The same grounded
|
||||
function continues through the larger post-load generation tail up to `0x00444dc2`, which means
|
||||
the later `Setting up Players and Companies...` and neighboring post-load passes are not
|
||||
floating raw callsites after all. That same owner now clearly covers the event-runtime refresh
|
||||
through `0x433130`, chairman-profile materialization through `0x437220`, neighboring route and
|
||||
tracker refresh families, and the one-shot kind-`8` runtime-effect service through `0x432f40`
|
||||
before clearing shell-profile latch `[0x006cec7c+0x97]`. The `Setup.win` dispatcher
|
||||
is less opaque now too: the early `0x0bc1..0x0c24` family is mostly fixed submode selection above
|
||||
`0x00502c00`, except for the separate `0x0bc2/0x0bc5/0x0bc6/0x0bc7` shell-open quartet above
|
||||
`0x00501f20`; `0x0c1f` is the settings-window escape; `0x0c1e/0x0c20/0x0c22` are direct shell
|
||||
requests into `0x00482150`; the fixed submode buttons now have concrete lower targets such as
|
||||
`0x0bc1/0x0bc8 -> 15`, `0x0bc3 -> 16`, `0x0bc4 -> 1`, `0x0c80 -> 13`, `0x0c1c -> 17`,
|
||||
`0x0c1d -> 2`, `0x0c24 -> 14`, `0x0c81 -> 14`, `0x0c82 -> 6`, `0x0c83 -> 10`, and
|
||||
`0x0c84 -> 12`; the
|
||||
`0x0ce6/0x0ce7/0x0d49/0x0d4a/0x0e82/0x0e83` branches are bounded list or slider adjustments on
|
||||
staged setup fields; and the later `0x0dca/0x0dcb/0x0de9/0x0df3/0x0e81/0x0f6f/0x0f70` controls
|
||||
are the explicit selector-writing launch buttons rather than one anonymous validated-launch blob.
|
||||
The constructor-side callbacks are tighter too: control `0x0ce8` is a table-driven payload-label
|
||||
draw callback above `0x00502030`, not another launch root; that callback now clearly sits on top
|
||||
of the indexed string-table getter `0x0053de00` before handing the selected text span into the
|
||||
world-anchor marker queue path. Controls `0x0e86` and `0x0e87` do not select more setup roots;
|
||||
they update the persisted shell-state selector pairs at
|
||||
`[0x006cec74+0x233/+0x237]` and `[0x006cec74+0x23b/+0x23f]` through `0x00502160` and
|
||||
`0x005021c0`, then immediately save config through `0x00484910(1)`. The constructor body is
|
||||
tighter too: it seeds the initial `Setup.win` state by running `0x502910`, `0x502550`, and
|
||||
`0x502c00(1)` before the user interacts with the window, installs `0x0c80..0x0c86` and
|
||||
`0x0f6f..0x0f71` as homogeneous button bands, and treats `0x0e88` as one separate special
|
||||
control with retuned float fields rather than another ordinary launch root. The remaining
|
||||
optional constructor child `0x0bd0` is tighter now too: it is built from a separate template
|
||||
block and optional owned heap object before registration, not another hidden setup-root button.
|
||||
The generic shell helper layer beneath that constructor is tighter now too: `0x53fa50` is the
|
||||
shared resource-bind and child-list initialization helper, `0x53f830` is the child-control
|
||||
lookup-by-id helper over the intrusive list at `[this+0x70]`, `0x558130` is the child-control
|
||||
finalizer that stamps the owner pointer and resolves localized captions before the control goes
|
||||
live, and `0x53f9c0` is the ordered child-control registration helper used by the optional
|
||||
`0x0bd0` branch and other shell dialogs.
|
||||
The submode selector itself is tighter now too because its button-state lane is mostly decoded:
|
||||
`0xbc5` tracks submode `15`, `0xbc6` tracks `16`, `0xbba` tracks `2`, `0xbbb` tracks `3`,
|
||||
`0xbc3` tracks `13`, `0xbbc` groups `3/4/5`, `0xbbd` tracks `4`, `0xbbe` tracks `5`, `0xbbf`
|
||||
groups `6/12/14`, `0xbc0` tracks `7`, `0xbc1` tracks `8`, `0xbc2` tracks `9`, `0xe75` tracks
|
||||
`10`, and `0xf6e` tracks `17`. RT3.lng and the setup art families now also make several of those
|
||||
top-level roots read less like anonymous ids and more like real menu panels: submode `1` is the
|
||||
strongest current landing-panel fit, `2` is the `Single Player` root, `7/8/9` are the
|
||||
`Editor` / `New Map` / `Load Map` family, `15` is `Extras`, and `17` is `Tutorial`. By
|
||||
elimination against the separate shell `Credits.win` mode, the remaining top-level `Setup.win`
|
||||
roots now most safely read as `13 = Multi Player` and `16 = High Scores`.
|
||||
The file-backed side is tighter too: `0x502c00` now maps submodes exactly as `4 -> dataset 5`,
|
||||
`9 -> 4`, `6 -> 8`, `10 -> 6`, `12 -> 10`, and `14 -> 9`, so the saved-game-backed family is
|
||||
no longer one blurred list pane but a small set of stable dataset variants above
|
||||
`0x4333f0/0x4336a0`.
|
||||
The file-list builder under that pair is tighter too: `0x4333f0` no longer just emits unsorted
|
||||
`0x25a`-byte rows with raw names at `+0x0` and normalized labels at `+0x12d`. After the scan it
|
||||
now clearly re-enters `0x51dc60`, which is a generic adjacent-swap insertion sort over fixed
|
||||
records; in this setup-side caller it receives `record_size = 0x25a` and `key_offset = 0x12d`,
|
||||
so the resulting `Setup.win` rows are sorted by display label rather than by the raw filename.
|
||||
The file-backed header split is tighter too: `0x5027b0` now maps the top header-control ids as
|
||||
`4 -> 0xd4b`, `9 -> 0xde8`, `6/12/14 -> 0xdf2`, and `10 -> 0xe85`. RT3.lng closes one earlier
|
||||
mistake here: those values are local setup control or resource ids, not localized text ids.
|
||||
The setup art bundle now tightens that family split one step further too: under `rt3_2WIN.PK4`
|
||||
the distinct file-backed setup art families are the shared `Setup_Load` lane and the separate
|
||||
`Setup_Sandbox` lane, which matches the selector-side evidence that mode `10` is the
|
||||
sandbox-backed `new` list while mode `12` is its `load` sibling. Combined with the builder at
|
||||
`0x4333f0`, that shows only submodes `4` and `10` using the alternate localized-stem list-label
|
||||
path; `6`, `9`, `12`, and `14` keep the direct filename-normalization lane.
|
||||
The `rt3_2WIN.PK4` extraction path is grounded a bit more cleanly now too: current `pack4`
|
||||
samples use a shared `0x03eb` header, a fixed `0x4a`-byte directory entry stride, and a payload
|
||||
base at `8 + entry_count * 0x4a`. In the checked UI bundle the entry table is contiguous and
|
||||
`Campaign.win` extracts cleanly at directory index `3` with payload length `0x306a`.
|
||||
The extracted `.win` payload family now has one narrow shared shape too: `Campaign.win`,
|
||||
`CompanyDetail.win`, and `setup.win` all share the same first `0x50` bytes at offsets
|
||||
`0x00`, `0x0c`, `0x10`, `0x14`, `0x34`, `0x38`, `0x40`, and `0x48`, while
|
||||
`CompanyDetail.win` and `setup.win` also carry an inline root `.imb` name immediately at
|
||||
`0x51`. Current resource scans show `Campaign.win` embedding the `litCamp*/Ribbon*` family,
|
||||
`CompanyDetail.win` embedding mainly `CompanyDetail.imb`, `GameWindow.imb`, and `Portrait.imb`,
|
||||
and `setup.win` embedding the broader `Setup_Background/Buttons/New_Game/Load/Sandbox` art
|
||||
families. The control records between those strings are still undecoded, but the resource-record
|
||||
shell itself is tighter now: all checked `.win` samples use the same three-word prelude prefix
|
||||
`0x0bb8, 0x0, 0x0bb9`, and the fourth prelude word matches `resource_name_len + 1`. The next
|
||||
word after the terminating NUL then behaves like a per-record selector lane. In `setup.win`
|
||||
the dominant `Setup_Buttons.imb` family alternates between `0x00040000` and the incrementing
|
||||
`0x00010c1c..0x00010c86` series with dominant inter-record strides `0xb7/0xdb`; in
|
||||
`Campaign.win` the `litCamp*/Ribbon*` family carries the incrementing `0x0004c372..0x0004c38e`
|
||||
selector block with dominant `0x158/0x159` and `0xb2/0xb3` strides. That campaign lane is now
|
||||
directly aligned to the executable-side control ids too: the low 16 bits of
|
||||
`litCamp1..16` map exactly to `0xc372..0xc381`, and the low 16 bits of `Ribbon1..16` map
|
||||
exactly to `0xc382..0xc391`, matching the `Campaign.win` progress and selector control bases
|
||||
already grounded from `RT3.exe`. The fuller selector export now tightens the auxiliary families
|
||||
too: `litArrows.imb` covers `0xc36c..0xc371` exactly and `litExits.imb` covers
|
||||
`0xc397..0xc39a` exactly, matching the constructor-side six-control arrow strip and four-control
|
||||
exit strip. The second post-name dword is tighter now too: its middle 16-bit lane groups those
|
||||
same `Campaign.win` records under `0xc368`, `0xc369`, `0xc36a`, and `0xc36b`, which matches the
|
||||
four page-strip controls and cleanly buckets the first five campaign entries, the next five, the
|
||||
next three, and the final three under the same page families. There are still no `.imb`
|
||||
selector records for `0xc393..0xc396` or `0xc39b`, which makes those message-side page-write
|
||||
controls look more like structural buttons than art-backed repeated resource records.
|
||||
The grouped `3/4/5` family is narrower now too: `0x0ce5` is no longer part of it, because that
|
||||
control writes selector `3` and then selects submode `9` as the `Load Map` sibling. The nearby
|
||||
`0x0dcb` branch instead conditionally selects submode `5` or `3`, which keeps `3` as the
|
||||
strongest current `New Game` / `Options` companion and `5` as the strongest current `Sandbox`
|
||||
companion. The file-backed single-player side is tighter in the same way now: modes `4` and `10`
|
||||
are the only siblings using the alternate localized-stem row-label path, so they now read most
|
||||
safely as the two setup-local template or profile list variants rather than ordinary save lists.
|
||||
Mode `10` is the stronger one of the pair because neighboring validated launch control `0x0e81`
|
||||
both routes into selector `5` and sets sandbox byte `[0x006cec7c+0x82] = 1`, which makes it the
|
||||
strongest current fit for the setup-local `New Sandbox` list. The distinct `Setup_Sandbox` art
|
||||
family in `rt3_2WIN.PK4` now reinforces that same split one step further, which makes mode `12`
|
||||
the strongest closed fit for the paired `Load Sandbox` lane; mode `4` is therefore the strongest
|
||||
remaining non-sandbox peer in that same pair and now most safely reads as the setup-local `New
|
||||
Scenario`-style chooser. Modes `6`, `12`, and `14` now tighten one step further as the three
|
||||
selector-`3` direct-filename setup-local `load` siblings because they stay on the direct
|
||||
filename-normalization lane, share the same `0xdf2` header family, and clear the same
|
||||
presence-style latch `[0x006cec7c+0x97]`; mode `14` is the strongest current landing panel for
|
||||
that cluster because `0x0c24` jumps to it directly while `0x0c82` and `0x0c84` only reach the
|
||||
sibling modes `6` and `12` from inside the same load family. Current control-pairing and
|
||||
setup-art evidence now make `12 = Load Sandbox` the strongest closed per-submode assignment. The
|
||||
remaining non-sandbox pair is closed now too: the deeper bundle filter at `0x433260`
|
||||
distinguishes dataset `9` from dataset `8` by one extra nonzero payload-flag family, and the
|
||||
editor-side metadata path now grounds `[0x006cec78+0x66de]` as the direct `Campaign Scenario`
|
||||
checkbox bit because `editorDetail.win` ties control `0x5b6e` to localized ids `3160/3161`.
|
||||
That makes dataset `9` the campaign-designated load family and dataset `8` the ordinary scenario
|
||||
load family, so `14 = Load Campaign` and `6 = Load Scenario` now read as grounded rather than
|
||||
residual. `0x502910` is
|
||||
tighter in a
|
||||
corrective way too: it is not a mode-`3`-only helper after all, but the shared non-file-backed
|
||||
payload panel that formats
|
||||
`0xcf3/0xcf4/0xcf5/0xd4f`, mirrors option byte `[0x006cec7c+0x7d]` into `0x0ce9..0x0ced`,
|
||||
rebuilds row host `0x0ce8` from payload bytes `[+0x31b/+0x31c]`, and mirrors live row markers
|
||||
into `[0x006cec7c+0x87]`. That makes mode `3` just one user of the shared payload-driven panel,
|
||||
not the sole owner of it.
|
||||
The payload-helper cluster under that panel is tighter now too: `0x502220` does not just
|
||||
republish labels and the preview surface. It first re-enters
|
||||
`shell_setup_load_selected_profile_bundle_into_payload_record` `0x442400`, which clears one full
|
||||
`0x100f2`-byte setup payload record, builds a rooted path from the staged profile stem, opens the
|
||||
selected bundle through `0x530c80`, and then reads either the ordinary saved-profile chunk family
|
||||
or the map-style chunk family through `0x531150/0x531360` depending on the selected extension
|
||||
shape. Only after that does `0x502220` copy payload fields `+0x14/+0x3b2/+0x3ba/+0x20` into the
|
||||
staged runtime profile through `0x47be50`, which in turn normalizes the payload category bytes at
|
||||
`[payload+0x31a + row*9]` and the local marker-slot bytes at `[payload+0x2c9..]` through
|
||||
`0x47bc80`. The copied-field consumer split is tighter now too: payload `+0x14` becomes staged
|
||||
profile `[0x006cec7c+0x77]`, which is the visible setup scalar later formatted into controls
|
||||
`0x0e84` and `0x0d4f` and adjusted by the `0x0d49/0x0d4a` pair; payload `+0x3b2` becomes
|
||||
`[0x006cec7c+0x79]`, the live-row threshold that the payload-row draw callback uses to choose
|
||||
style slot `0x18` versus `0x19`; and payload `+0x3ba` becomes `[0x006cec7c+0x7b]`, the current
|
||||
scroll or row-index lane adjusted by the `0x0ce6/0x0ce7` pair. So the known setup-side payload
|
||||
consumers still stop well before the later candidate table, but the early copied lanes are no
|
||||
longer anonymous. The adjacent region-side worker family is tighter in a negative way too: the setup
|
||||
payload path now gives one useful upper bound on the newer candidate-availability source block
|
||||
too. The map-style setup loader is definitely pulling chunk families `0x0004/0x2ee0/0x2ee1`
|
||||
into one large `0x100f2`-byte payload record, and the fixed `0x6a70..0x73c0` candidate table
|
||||
clearly lives inside the same broad file family; but the grounded setup-side consumers we can
|
||||
actually name after that load still only touch earlier payload offsets such as `+0x14`,
|
||||
`+0x20`, `+0x2c9`, `+0x31a`, `+0x3ae`, `+0x3b2`, and `+0x3ba`. So the current evidence is
|
||||
strong enough to say the candidate table is probably housed inside the setup payload family, but
|
||||
not yet strong enough to name one setup-side consumer that reads the `0x6a70` header or the
|
||||
fixed-width entries directly. The newer fixed-offset compare pass tightens that lower setup slice
|
||||
too: across the checked map/save pairs `Alternate USA.gmp -> Autosave.gms`,
|
||||
`Southern Pacific.gmp -> p.gms`, and `Spanish Mainline.gmp -> g.gms`, the known setup payload
|
||||
lanes `+0x14` and `+0x3b2` are preserved map-to-save on the same scenario-family split as the
|
||||
later candidate-table headers (`0x0771/0x0001`, `0x0746/0x0006`, `0x0754/0x0001`), while
|
||||
`+0x3ae` stays fixed at `0x0186` and `+0x3ba` stays fixed at `1` across all six files. By
|
||||
contrast, `+0x20` does not survive as one shared source value (`0xd3 -> 0xb4`,
|
||||
`0x6f -> 0x65`, `0xe3 -> 0x78` across those same pairs). So the current best read is that the
|
||||
setup payload mixes preserved scenario metadata and later save-variant state well before the
|
||||
`0x6a70` candidate table, rather than acting as one uniformly copied prelude.
|
||||
The adjacent region-side worker family is tighter in a negative way too: the setup
|
||||
payload loader is now clearly separate from the broader region-building and placement cluster
|
||||
around `0x422320..0x423d30`, whose current grounded helpers now include `0x422320`, `0x4228b0`,
|
||||
`0x422900`, `0x422a70`, `0x422be0`, `0x422ee0`, `0x4234e0`, `0x4235c0`, and
|
||||
`world_region_refresh_cached_category_totals_and_weight_slots` `0x423d30` rather than one hidden
|
||||
setup-only panel. The leading region helper is no longer unnamed either: `0x422320` is now
|
||||
bounded as the recurring cached-structure-scalar normalization pass that writes
|
||||
`[region+0x2e2/+0x2e6/+0x2ea/+0x2ee]`, while `0x422a70` is the shared placement-validation and
|
||||
commit gate beneath both the per-region worker and one second world-side placement loop. The
|
||||
neighboring `0x4234e0` accessor is tighter too: it is the shared projected structure-count scalar
|
||||
query by category that later feeds both the per-region placement family and the map-editor city
|
||||
count stats report. So the remaining gap is no longer “what are these setup payload helpers
|
||||
doing,” but only how aggressive we want to be when naming the last top-level setup roots from
|
||||
mostly RT3.lng and asset-side evidence.
|
||||
The adjacent summary helper is tighter too: `0x502550` is not another hidden submode owner. It
|
||||
republishes the staged path tail in `0xe7f`, the scalar summary in `0xe84`, the two persisted
|
||||
selector lists in `0xe86/0xe87`, and the config toggles in `0xe88/0xe89/0xe8a`.
|
||||
The remaining top-level gap is cleaner now too: submode `16` still has no distinct downstream
|
||||
helper or launch branch in the local selector/refresh family, but it is no longer a blank bucket.
|
||||
Current evidence now bounds it as the `0x0bc3 -> 16` top-level button whose visual-state lane is
|
||||
surfaced through control `0xbc6`, and the strongest residual fit is `High Scores` because
|
||||
`Credits` already lives in separate shell mode `6`.
|
||||
The validated launch lane is tighter now too: it no longer just writes selector `3` or `5` as
|
||||
one undifferentiated blob, and it no longer includes `0x0ce5` or `0x0dcb`. `0x0ce5` is the
|
||||
`Load Map` selector because it writes selector `3` and then selects submode `9`, while `0x0dcb`
|
||||
is the later conditional `5/3` companion-selector sibling rather than a file launch. The actual
|
||||
validated staged-profile lane is now bounded more narrowly: `0x0cf6` and `0x0e81` are the
|
||||
selector-`5` siblings, while the neighboring selector-`3` validated lane is at least shared by
|
||||
`0x0de9` and `0x0df3`; the shared bridge at `0x4425d0` then forces `[0x006cec7c+0xc5] = 1`,
|
||||
mirrors payload bytes into `[profile+0xc4]`, `[profile+0x7d]`, and `[profile+0xc6..+0xd5]`, and
|
||||
only then issues shell request `0x0cc`. The file-side staging bytes in that bridge are now
|
||||
tighter too: across the checked `Alternate USA`, `Southern Pacific`, and `Spanish Mainline`
|
||||
map/save pairs, payload `+0x33` stays `0`, but payload `+0x22` and token block `+0x23..+0x32`
|
||||
do not preserve the earlier map-to-save pairing seen at `+0x14/+0x3b2`. Instead they split by
|
||||
the finer file family, with examples `Alternate USA.gmp = 0x53 / 0131115401...` versus
|
||||
`Autosave.gms = 0xae / 01439aae01...`, `Southern Pacific.gmp = 0xeb / 00edeeeb...` versus
|
||||
`p.gms = 0x21 / 0100892101...`, and `Spanish Mainline.gmp = 0x5b / 0044f05b...` versus
|
||||
`g.gms = 0x7a / 0022907a...`. So the validated launch bridge now looks more like a file-family
|
||||
staging lane than a simple copy-forward of the earlier setup summary fields. The destination-side
|
||||
consumers are tighter now too: `[profile+0xc4]` is no longer just an unnamed staged byte, but the
|
||||
campaign-progress slot later consumed by the numbered `%s%02d.gmc` save helper `0x00517c70`;
|
||||
`[profile+0x7d]` is the same compact option byte mirrored back into the `Setup.win` option
|
||||
controls `0x0ce9..0x0ced`; and the campaign-side selector block is no longer only a one-byte
|
||||
anchor. Direct `RT3.exe` disassembly of the campaign-side dispatcher at `0x004b89c0` now shows
|
||||
the exact mirror shape: when the local campaign-page selector `[window+0x78] <= 4`, the helper
|
||||
writes one highlighted progress control `0x0c372 + [profile+0xc4]` and then mirrors the full
|
||||
sixteen-byte band `[profile+0xc6..+0xd5]` into controls `0x0c382..0x0c391` through repeated
|
||||
`0x540120` calls. The same body also treats `[profile+0xc4]` as an index into the fixed
|
||||
sixteen-entry scenario-name table at `0x00621cf0`, whose observed entries include `Go West!`,
|
||||
`Germantown`, `Central Pacific`, `Texas Tea`, `War Effort`, `State of Germany`, `Britain`,
|
||||
`Crossing the Alps`, `Third Republic`, `Orient Express`, `Argentina Opens Up`,
|
||||
`Rhodes Unfinished`, `Japan Trembles`, `Greenland Growing`, `Dutchlantis`, and
|
||||
`California Island`. On the launch-side branch the same helper writes startup selector `6`,
|
||||
copies the resolved campaign filename into `[profile+0x11]`, forces `[profile+0xc5] = 1`, and
|
||||
then issues shell request `0x0cc`. The lower refresh tail at `0x004b8d49..0x004b8d69` also
|
||||
shows the observed page-band split over the same progress byte: values `< 5` pair with campaign
|
||||
page `1`, values `5..9` with page `2`, values `10..12` with page `3`, and values `>= 13` with
|
||||
page `4`. So `[profile+0xc6..+0xd5]` is no longer a generic opaque span at all, but the staged
|
||||
sixteen-byte per-scenario campaign selector or unlock band consumed directly by `Campaign.win`.
|
||||
The neighboring profile helpers are tighter now too: `0x0047bbf0` is the broad default reset for
|
||||
the staged `0x108`-byte runtime-profile record, clearing the whole record and then reseeding the
|
||||
visible setup and campaign anchors `[profile+0x77]`, `[profile+0x79]`, `[profile+0x7d]`,
|
||||
`[profile+0x83]`, `[profile+0x87]`, and `[profile+0x97]`; and `0x0047bc50` is the compact scan
|
||||
helper over `[profile+0xc6..+0xd5]` that returns the first selector byte below `2`, which keeps
|
||||
that band tied to staged campaign progress or unlock state rather than to the earlier setup-panel
|
||||
payload fields.
|
||||
The message-dispatch side is tighter now too. The local classifier at `0x004b91d8` routes the
|
||||
control range `0x0c352..0x0c39b` through five concrete case classes: `0x0c352..0x0c361` enter
|
||||
the shared selector or launch branch, `0x0c362..0x0c367` force local page `1`,
|
||||
`0x0c368..0x0c392` force local page `2`, `0x0c393..0x0c396` force local page `0`, and
|
||||
`0x0c39b` alone forces local page `3`. That matches the constructor and refresh shape: the
|
||||
sixteen scenario selector controls live at `0x0c352..0x0c361`, the four page-strip controls
|
||||
live at `0x0c368..0x0c36b`, the six-arrow auxiliary strip lives at `0x0c36c..0x0c371`, the
|
||||
progress band lives at `0x0c372..0x0c381`, the ribbon or selector band lives at
|
||||
`0x0c382..0x0c391`, the string or voice target lane lives at `0x0c392`, the four exit controls
|
||||
live at `0x0c397..0x0c39a`, and `0x0c39b` remains the one-off special action control that
|
||||
spawns or dismisses the campaign-side companion object. The `Campaign.win` blob now exposes that
|
||||
structural split directly too: alongside the art-backed `0x0004c3xx` records it carries
|
||||
anonymous zero-name records with the same `0x0bb8 / 0 / 0x0bb9` prelude but selector words in
|
||||
the `0x0010c3xx` family. Current local extraction shows exactly
|
||||
`0x0010c352..0x0010c361`, `0x0010c362..0x0010c367`, `0x0010c393..0x0010c396`, and one
|
||||
`0x0010c39b` record, which is the same non-`.imb` band the message dispatcher treats as the
|
||||
structural selector and page-write family. Their second selector word then buckets those records
|
||||
under the same page-strip ids as the art-backed records: `0xc368` for
|
||||
`0xc352..0xc356`, `0xc362`, `0xc393`, and `0xc39b`; `0xc369` for `0xc357..0xc35b`,
|
||||
`0xc363`, and `0xc394`; `0xc36a` for `0xc35c..0xc35e`, `0xc365..0xc366`, and `0xc395`; and
|
||||
`0xc36b` for `0xc367`, `0xc35f..0xc361`, and `0xc396`. One final anonymous record at
|
||||
`0x0002c392` has no page bucket at all, which fits the already-grounded read of `0xc392` as the
|
||||
one-off string or voice target lane rather than a normal page-cell control. The anonymous body
|
||||
layout is only partially named, but one file-side distinction is already stable: the ordinary
|
||||
structural selector/page records all carry footer words `0xd3000000` and `0xd2000007` at
|
||||
relative `+0x98/+0x9c`, while the lone `0xc392` record carries `0x00000000/0x00000000` there.
|
||||
So `0xc392` is no longer just “outside the page buckets”; it is also the only current
|
||||
anonymous-record outlier in that trailing structural footer pair. The structural selector side is
|
||||
tighter now too: `0xc352..0xc361` partition exactly as `5 + 5 + 3 + 3` across page buckets
|
||||
`0xc368..0xc36b`, matching the observed campaign page bands for scenario progress values `< 5`,
|
||||
`5..9`, `10..12`, and `>= 13`. That makes the anonymous selector records the file-side mirror of
|
||||
the same campaign page split, not a separate unrelated control family. The file-order adjacency
|
||||
is tighter in the same way: `0xc352..0xc361` each sit immediately after one `RibbonN.imb`
|
||||
record and before the next `litCamp` record, which makes them the strongest current file-side fit
|
||||
for the structural selector or click-target siblings of the visible campaign ribbon controls.
|
||||
The auxiliary anonymous bands line up the same way: `0xc362..0xc367` sit inside the local
|
||||
`litArrows.imb` clusters, `0xc393..0xc396` sit inside the `litExits.imb` clusters, and
|
||||
`0xc39b` sits once between the first arrow and first exit group. So the current best read is
|
||||
that those anonymous records are the structural hitbox or action siblings of the visible arrow,
|
||||
exit, and selector art families, not an unrelated hidden control layer. The generic record
|
||||
cadence is tighter now too: in the current `Campaign.win` dump the ordinary anonymous structural
|
||||
records all span `0x0a7` bytes, while the named art-backed records cluster at `0x0b1`, `0x0b2`,
|
||||
and `0x0b3`. The only two visible outliers are the leading `0x0080c351` record at `0x0041`
|
||||
with span `0x0a3`, and the trailing `0x0002c392` record at `0x2fb9` with span `0x0b1`. That
|
||||
reinforces the current read that `0xc351` and `0xc392` are one-off structural controls flanking
|
||||
the main selector or page family rather than ordinary repeated art-backed records.
|
||||
The setup-launch corpus now tightens one corrective caveat there too: when the checked
|
||||
`Alternate USA`, `Southern Pacific`, and `Spanish Mainline` `.gmp/.gms` files are decoded with
|
||||
that same campaign-side interpretation, payload byte `+0x22` is always outside the known
|
||||
campaign progress range `0..15` (`0x53`, `0xeb`, `0x5b`, `0xae`, `0x21`, `0x7a`), so the
|
||||
ordinary validated setup lane is not simply staging a normal campaign-screen state. The paired
|
||||
token block `+0x23..+0x32` is still structurally compatible with the campaign selector band, but
|
||||
in the checked files it only populates a small recurring subset of the sixteen scenario lanes,
|
||||
chiefly `Go West!`, `Germantown`, `Central Pacific`, `Texas Tea`, and `War Effort`, with
|
||||
scenario-family-specific byte values rather than a simple `0/1` unlock bitmap. That makes the
|
||||
setup-side staging bridge look more like one shared destination layout being reused for a broader
|
||||
launch token family, not a proof that ordinary setup-launched `.gmp/.gms` content is already in
|
||||
campaign-screen form.
|
||||
Only `0x0e81` also sets `[0x006cec7c+0x82] = 1`, which is currently the strongest sandbox-side
|
||||
anchor beneath the later `.gmx` load family.
|
||||
That launch band is slightly tighter now too: `0x0dca` is the grayscale-map picker branch above
|
||||
the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` helper at `0x004eb0b0`, not another ordinary
|
||||
save-file validator. That launch band is tighter in a second way too: `0x0ddf` is not a lobby
|
||||
branch after all, but the separate windowed-mode-gated grayscale-heightmap generation path. It
|
||||
reopens the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` picker through `0x0042a970`, validates
|
||||
the chosen image through `0x005411c0`, and only then writes startup selector `2`. The fixed-
|
||||
button side is tighter too: submode `15` now aligns with the `Extras` family because it sits
|
||||
above the Readme/Weblinks shell-open quartet and the return-to-extras sibling, while submode
|
||||
`17` now aligns with the tutorial chooser because it is the only branch that later exposes
|
||||
startup selectors `1` and `7`, which RT3 uses for `Tutorial_2.gmp` and `Tutorial_1.gmp`. The
|
||||
map-root family is tighter too: submodes `7/8/9` are the only setup branch that flips the file
|
||||
root into
|
||||
`maps\\*.gm*`, with mode `8` specifically the grayscale-heightmap picker and mode `9` the
|
||||
file-backed map-list sibling.
|
||||
Submode `13` is slightly tighter now too: current local evidence shows that it stays outside
|
||||
both the dedicated file-backed pane and the mode-`3` option-heavy pane, so it is best read for
|
||||
now as one top-level non-file-backed setup branch rather than another saved-game list variant.
|
||||
The setup-side file roots are tighter now too: the shared file-list builder at `0x4333f0`
|
||||
formats either `saved games\\*.smp` or `maps\\*.gm*` from the shell-state fields at
|
||||
`[0x006cec74+0x68/+0x6c]`, with the separate `data\\tutorial` root only appearing on the
|
||||
tutorial-side `[shell+0x6c] == 2` branch. That means the `Setup.win` submode families are no
|
||||
longer one generic file pane: the ordinary setup-backed `.smp` family is broader than the
|
||||
narrower file-list pane, because modes `3/4/5/6/10/12/14` stay on the ordinary saved-game side
|
||||
while only `4/6/9/10/12/14` re-enter the dedicated file-list panel at `0x5027b0`; the `maps`
|
||||
branch is the narrower setup or tutorial launch family above the same shared record builder.
|
||||
- Evidence: function-map map/scenario rows, analysis-context exports for `0x00445ac0`, `0x00445de0`,
|
||||
`0x00443a50`, `0x00434300`, and `0x00444dd0`, plus objdump string and mode-table evidence for
|
||||
`.gmp`, `.gmx`, `.gmc`, `.gms`, `.gmt`, `.smp`, `Quicksave`, the `0x004dd010` mode table at
|
||||
`0x005f3d58`, the auxiliary-owner presence check at `0x00434050`, and the `.gmt` handoff through
|
||||
`0x00469d30`, together with localized string evidence from ids `3018` and `3898`.
|
||||
- Direct shell stubs above that coordinator are tighter now too: `0x004408b0` is the pure
|
||||
zero-flag wrapper into `shell_map_file_world_bundle_coordinator`, while `0x004408d0` is the
|
||||
sibling wrapper with flag triplet `(0, 1, 0)`. The exact user-facing command names for those two
|
||||
wrappers are still open, but the dispatcher shape is no longer.
|
||||
- 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.
|
||||
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
## Multiplayer Session and Transport Flow
|
||||
|
||||
- Roots: `multiplayer_window_init_globals` at `0x004efe80`, `multiplayer_window_service_loop` at
|
||||
`0x004f03f0`, the Multiplayer.win session-event callback table built by
|
||||
`multiplayer_register_session_event_callbacks` at `0x0046a900`, the active session-event transport
|
||||
object at `0x006cd970`, the Multiplayer preview dataset object at `0x006cd8d8`, and the lower
|
||||
pending-template and text-stream helpers around `0x00597880..0x0059caf0`.
|
||||
- Trigger/Cadence: shell-owned Multiplayer.win frame service plus event-driven session callbacks and
|
||||
repeated transport pump steps.
|
||||
- Key Dispatchers: session-event wrappers for actions `1`, `2`, `4`, `7`, `8`;
|
||||
`multiplayer_register_session_event_callbacks`; `multiplayer_dispatch_requested_action`;
|
||||
`multiplayer_reset_preview_dataset_and_request_action`;
|
||||
`multiplayer_preview_dataset_service_frame`; `multiplayer_load_selected_map_preview_surface`;
|
||||
`multiplayer_flush_session_event_transport`; `multiplayer_transport_service_frame`;
|
||||
`multiplayer_transport_service_worker_once`;
|
||||
`multiplayer_transport_service_route_callback_tables`;
|
||||
`multiplayer_transport_service_status_and_live_routes`;
|
||||
`multiplayer_transport_text_stream_service_io`;
|
||||
`multiplayer_transport_dispatch_pending_template_node`;
|
||||
`multiplayer_transport_service_pending_template_dispatch_store`.
|
||||
- State Anchors: live session globals at `0x006d40d0`, active session-event transport object at
|
||||
`0x006cd970`, status latch at `0x006cd974`, session-event mode latch at `0x006cd978`, retry
|
||||
counter at `0x006cd984`, preview dataset object at `0x006cd8d8`, preview-valid flag at
|
||||
`0x006ce9bc`, staged preview strings at `0x006ce670` and `[0x006cd8d8+0x8f48]`, Multiplayer.win
|
||||
backing block at `0x006d1270`, selector-view store root at `[transport+0xab4]`, selector-view
|
||||
generation counters at `[transport+0xab8]..[transport+0xac0]`, selector-view backing arrays around
|
||||
`[transport+0xad0]..[transport+0xae4]`, selector-view entry probe-request id at `[entry+0x50]`,
|
||||
live-state gate at `[entry+0x58]`, third-slot flag word at `[entry+0x64]`, probe-schedule tick at
|
||||
`[entry+0x68]`, last-success tick at `[entry+0x6c]`, pending-probe latch at `[entry+0x74]`,
|
||||
success generation at `[entry+0x78]`, consecutive-failure counter at `[entry+0x7c]`, rolling
|
||||
average at `[entry+0x80]`, recent sample window at `[entry+0x84..]`, bounded sample-count at
|
||||
`[entry+0x94]`, total-success count at `[entry+0x98]`, pending-template list at
|
||||
`[transport+0x550]`, dispatch store at `[worker+0x54c]`, and text-stream buffers rooted under
|
||||
`[worker+0x1c]`.
|
||||
- Subsystem Handoffs: the Multiplayer.win initializer seeds the backing block at `0x006d1270`, later
|
||||
reset paths construct the separate preview dataset at `0x006cd8d8`, and the shell-owned
|
||||
active-mode frame services that dataset every frame through
|
||||
`multiplayer_preview_dataset_service_frame`. That preview side publishes roster and status
|
||||
controls through the Multiplayer window control paths, loads `.gmt` preview surfaces through
|
||||
`multiplayer_load_selected_map_preview_surface`, and is even reused by the map/save coordinator’s
|
||||
mode-`11` `.gmt` path when the dataset already exists. The preview owner now has a tighter
|
||||
internal request split too. The fixed selector-descriptor list at `[0x006cd8d8+0x8f28]` is built
|
||||
through `multiplayer_preview_dataset_append_named_callback_descriptor` `0x00469930`, whose
|
||||
current grounded owner is the fixed registration block
|
||||
`multiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_10` `0x00473a60`;
|
||||
the variable-size named UI-request list at `[0x006cd8d8+0x8f2c]` is built through
|
||||
`multiplayer_preview_dataset_append_named_ui_request_record` `0x00469a50`; and the staged
|
||||
profile/path strings live at `[0x006cd8d8+0x8e10]` and `[0x006cd8d8+0x8f48]`, with the broader
|
||||
action-`2` staging path now bounded by
|
||||
`multiplayer_preview_dataset_stage_profile_text_selected_path_and_sync_session_state`
|
||||
`0x0046a030`. The companion action-`3` commit path is tighter too:
|
||||
`multiplayer_preview_dataset_match_named_field_slot_copy_profile_text_and_probe_selection`
|
||||
`0x0046a110` now sits directly above the lazily initialized `0x80 * 0x11c` field-slot table at
|
||||
`[0x006cd8d8+0x10]`, whose owner is
|
||||
`multiplayer_preview_dataset_ensure_field_slot_table_initialized` `0x0046a1a0`. The shared
|
||||
submitter above those lists is now explicit too:
|
||||
`multiplayer_preview_dataset_submit_transport_request` `0x00469d30` accepts the caller’s
|
||||
request-id, selector, payload pointer and length, flag word, and optional auxiliary pointer,
|
||||
optionally allocates one sidecar object, and then either sends the request directly through the
|
||||
session-event transport or takes the slower packed branch through `0x00553000/0x00552ff0` into
|
||||
`0x00521dc0`. The constructor and teardown side are tighter now too.
|
||||
`multiplayer_preview_dataset_construct_reset_globals_and_seed_callback_owners` `0x0046be80`
|
||||
is the real reset owner above the action-`2/3` branches in
|
||||
`multiplayer_dispatch_requested_action`: it re-enters the paired release body
|
||||
`multiplayer_preview_dataset_release_owned_lists_transients_and_session_side_state`
|
||||
`0x0046bc40`, clears the surrounding launcher globals, allocates the field-slot table and the
|
||||
keyed request/descriptor owners, seeds the render target and staging buffers, and then calls
|
||||
`multiplayer_preview_dataset_reset_global_callback_state_and_register_selector_handlers`
|
||||
`0x00473280` plus the smaller fixed-name block
|
||||
`multiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_10` `0x00473a60`.
|
||||
That broader registration pass `0x00473280` now bounds the selector-handler family too: it
|
||||
zeroes the global scratch bands `0x006ce808..0x006ce994` and `0x006ce290`, seeds default
|
||||
callback root `[0x006cd8d8+0x08]` with `0x0046f4a0`, and populates the keyed selector table at
|
||||
`[0x006cd8d8+0x04]` through
|
||||
`multiplayer_preview_dataset_register_selector_callback_if_absent` `0x00468b10` for selectors
|
||||
`1..0x83`. The concrete callback bodies under that table are tighter now too. The small split is
|
||||
real rather than guessed: `0x0046c390` is the direct control-`0x109` publish leaf through
|
||||
`0x00469d30`; `0x0046c3c0` is the fixed-session-token plus launch-latch arm path; and the large
|
||||
sibling `0x0046c420` is the real apply-owner for one incoming session payload slab copied into
|
||||
`0x006cec74` before the shell-refresh follow-ons. A second callback cluster now owns live
|
||||
session-entry flags instead of just relaying transport payloads: `0x0046c7c0` rewrites the
|
||||
elapsed-pair dwords `[entry+0x54/+0x58]`, `0x0046c840` sets bit `0x08` in `[entry+0x5c]`,
|
||||
`0x0046c870` toggles bit `0x01` from payload byte `[msg+0x08]`, `0x0046cf10` sets bit `0x04`,
|
||||
and `0x0046cf70` is the list-wide clear of that same bit-`0x01` lane across every live session
|
||||
entry. The broader callback `0x0046c8c0` sits above those single-field leaves and applies either
|
||||
one or many embedded session-field update records before republishing the list. The pending-state
|
||||
side is separated now too: `0x0046cce0`, `0x0046cd10`, `0x0046ce90`, and `0x0046d230` are the
|
||||
current small latch/state owners under `0x006cd91c`, `0x006d1280`, `0x006d1284`, and
|
||||
`0x006ce9c8`, while `0x0046cd30` and `0x0046ce10` are the paired current-session string/scalar
|
||||
submit-and-apply handlers over `[entry+0x258c/+0x268c/+0x2690/+0x2694]`. The same callback table
|
||||
also owns one small fixed transfer-progress family rooted at `0x006ce2e8`: `0x0046cfe0`
|
||||
allocates the first free `0x5c` slot and optionally formats one label string, `0x0046d090`
|
||||
appends progress payload into the matched slot while publishing one percent string through
|
||||
`0x005387a0`, and `0x0046d1d0` clears the finished slot and frees that optional label. On the
|
||||
release side, `0x0046bc40` now clearly owns the request-list,
|
||||
descriptor-list, semicolon-name, pooled-span, render-target, and auxiliary-owner teardown, while
|
||||
`multiplayer_shutdown_preview_dataset_session_object_and_global_helper` `0x0046c230` is the
|
||||
final wrapper that additionally drops the live session object at `0x006d40d0` and the shared
|
||||
helper at `0x00d973b4`. The small tuple staging below that family is bounded too:
|
||||
`multiplayer_preview_dataset_touch_current_session_year_bucket_and_return_staged_tuple`
|
||||
`0x0046b6d0` now owns the keyed session-bucket touch under `[session+0x2580]` for the staged
|
||||
tuple at `[0x006cd8d8+0x9048]`, and the companion
|
||||
`multiplayer_preview_dataset_stage_optional_selected_token_from_source_ptr` `0x0046d610` writes
|
||||
the optional derived token into `[0x006cd8d8+0x8f38]`. The next service layer under the same
|
||||
owner is tighter too: `multiplayer_preview_dataset_prune_session_buckets_below_current_year_key`
|
||||
`0x0046b910` now bounds the older keyed-bucket drain under `[session+0x2580]`, while
|
||||
`multiplayer_preview_dataset_service_current_session_buckets_and_publish_selector0x67`
|
||||
`0x0046b9f0` owns the current-bucket walk, the local counters `[+0x987c/+0x9890]`, and the
|
||||
later selector-`0x67` publish branch back through `0x00469d30`. Its local batch-publish child
|
||||
`multiplayer_preview_dataset_publish_accumulated_selector0x71_record_batch` `0x00473bf0` is now
|
||||
bounded too: it packages the fixed-width records at `0x006cd990`, prefixes `(0, count)`, sends
|
||||
them as selector `0x71`, and then clears the live record count `0x006ce9a0`. The immediate local
|
||||
helpers under that same band are now explicit too: `0x0046d260` is the fixed-record append
|
||||
primitive that grows `0x006cd990` up to `0x80` entries, while `0x0046d240` is the paired release
|
||||
of the optional selector-`0x71` staging blob at `0x006ce994`. The first fixed named-descriptor
|
||||
block under `multiplayer_preview_dataset_register_fixed_named_callback_descriptors_1_to_10`
|
||||
`0x00473a60` is tighter in the same way now. Its concrete roots `0x0046db10`, `0x0046db50`,
|
||||
`0x0046db90`, `0x0046dbd0`, `0x0046dd10`, `0x0046de40`, `0x0046de80`, `0x0046e030`, and
|
||||
`0x0046e250` all return small static records rooted at `0x006ce9dc..0x006cea3c`; each record
|
||||
carries a leading tag byte plus the same derived world-year key from `[0x006cec78+0x0d]`, while
|
||||
the heavier siblings add collection-backed totals from `0x0062be10`, `0x006ceb9c`, `0x0062b26c`,
|
||||
`0x006cfca8`, or `0x006cfcbc` together with local overlay, geometry, or object-metric sample
|
||||
paths. The immediate helper strip beneath that same first named block is tighter now too.
|
||||
`0x0046d980` is the direct `0x006ceb9c` name-to-index lookup over `[entry+0x08]`, while
|
||||
`0x0046d9e0` is the heavier companion that first resolves the current live session from
|
||||
`0x006d40d0`, matches that session-side string against the same `0x006ceb9c` table, and returns
|
||||
the matching entry index or `0xff`. Above those, `0x0046f8f0` resolves and validates one
|
||||
`0x0062be10` entry, then keeps it only when its profile-index field `[entry+0x3b]` equals the
|
||||
live-session-backed `0x006ceb9c` index from `0x0046d9e0`; and `0x0046f870` is the paired
|
||||
rounded-delta leaf that takes one float plus one collection byte index and writes the rounded
|
||||
positive/negative pair into metric ids `0x0f` and `0x0d` through `0x0042a040`. So this first
|
||||
descriptor band is no longer just a bag of static record roots: it also owns a real local helper
|
||||
family for `0x006ceb9c` name matching, live-session profile matching, and one narrow metric-pair
|
||||
apply path beneath the higher callback bodies. The next selector layer above that helper strip is
|
||||
tighter now too. Selector `0x12` is the small validate-or-error wrapper above selector `0x13`,
|
||||
and selector `0x13` body `0x004706b0` resolves the same live-session company match, attempts the
|
||||
placed-structure apply path through `0x004197e0`, `0x004134d0`, `0x0040eba0`, `0x0052eb90`, and
|
||||
`0x0040ef10`, and otherwise falls back to a hashed selector-`0x6e` publish over the first
|
||||
`0x1c` payload bytes. The same pattern appears again one pair later: selector `0x16` is the
|
||||
thin validate-or-error wrapper above selector `0x17`, and selector `0x17` consumes a count plus
|
||||
`0x33`-stride adjunct record band, resolves one live train-side entry under `0x006cfcbc`,
|
||||
re-enters `0x004a77b0`, `0x0052e720`, `0x0040eba0`, `0x0052eb90`, and `0x0040ef10`, and again
|
||||
falls back to hashed selector `0x6e` publish when the live apply path does not land. The later
|
||||
status pair is bounded too: selector `0x19` is another thin wrapper, and selector `0x1a` either
|
||||
derives a status code from `0x0046ed30` and current live-session name matching or treats the
|
||||
four-byte payload directly as that status code before publishing localized status text
|
||||
`0x0b7f/0x0b80`. One selector-pair above the metric leaf is now explicit too:
|
||||
the earlier front edge of the same callback table is tighter now too. Selector `0x02` compares
|
||||
staged profile text against the shell profile band at `[0x006cec7c+0x11]`, can advance the
|
||||
requested-action fields `0x006d1280/0x006d1284`, can queue selector `0x53`, and on the success
|
||||
path syncs the larger shell profile block rooted at `[0x006cec7c+0x44]`. The next small strip is
|
||||
also grounded: selector `0x0a` clears `[world+0x19]`, seeds `[world+0x66ae]`, mirrors peer byte
|
||||
`[peer+0x2690]` into named-profile byte `[entry+0x15c]`, refreshes `0x006ce98c`, and optionally
|
||||
republishes one local status path. Selector `0x0b` is then the small token-staging wrapper above
|
||||
selector `0x0c`, and selector `0x0c` itself forwards one signed byte pair into `0x00434680`
|
||||
before adjusting dataset counter `0x006cd8e8`. The other small early leaves are now bounded too:
|
||||
selector `0x0f` pops one node from the current session queue at `[session+0x64]`, publishes that
|
||||
node through `0x00469d30`, and releases it; selector `0x10` looks one payload key up in the
|
||||
session-side store `[session+0x60]` and forwards the result plus dataset string root
|
||||
`[0x006cd8d8+0x8f48]` into `0x00521790`. One selector-pair above the metric leaf is now explicit too:
|
||||
selector-`0x15` body `0x00470950` consumes the same compact `(float delta, company-index byte)`
|
||||
payload shape, resolves the matching live-session company entry through `0x0046f8f0`, submits
|
||||
selector `0x6b` through `0x00469d30`, and then immediately re-enters `0x0046f870` for the local
|
||||
apply. The neighboring name-match lane is now explicit too:
|
||||
selector-`0x61` body `0x00472700` scans `0x0062be10` for a company-name match against the caller
|
||||
string at `[payload+0x08]` and then either submits selector `0x62` with the original payload or
|
||||
falls back to the paired error-style `0x21` branch. The next registered band around selectors
|
||||
`0x1c..0x5d` is tighter now too. Selector-adjacent helpers `0x00470ed0` and `0x00470fa0`
|
||||
are the paired global preset passes beneath that strip: both walk the guarded named-profile
|
||||
table `0x006ceb9c`, add opposite signed integer presets into qword field `[profile+0x154]`
|
||||
through `0x00476050`, then walk `0x0062be10` and write opposite preset
|
||||
scalars into metric id `0x0d` through `0x0042a040`. Above them, selectors `0x1d`, `0x1f`,
|
||||
`0x21`, `0x23`, `0x25`, `0x27`, `0x29`, `0x2b`, `0x2d`, `0x2f`, `0x31`, `0x33`, `0x35`,
|
||||
`0x37`, and `0x39` are now explicit token-staging forwarders into selectors `0x1e`, `0x20`,
|
||||
`0x22`, `0x24`, `0x26`, `0x28`, `0x2a`, `0x2c`, `0x2e`, `0x30`, `0x32`, `0x34`, `0x36`,
|
||||
`0x38`, and `0x3a`. The next live-train strip is also grounded: selectors `0x3b`, `0x3d`,
|
||||
`0x3f`, `0x41`, and `0x43` resolve live train ids from `0x006cfcbc`, stage the current token,
|
||||
and republish into selectors `0x3c`, `0x3e`, `0x40`, `0x42`, and `0x44`; selectors `0x3c`,
|
||||
`0x3e`, and `0x40` then dispatch directly into `0x004abd70`, `0x004b2f00`, and `0x004b3000`,
|
||||
while selector `0x42` is the heavier train-adjunct branch through `0x004b2b70`,
|
||||
`0x004b3160`, `0x004b2c10`, `0x004a9460`, and `0x004ab980`. The prompt or bitmap cluster is
|
||||
tighter too: selector `0x48` consumes one 12-byte record, either marks the current-session bit
|
||||
directly or opens localized prompt `0x0b81` through `0x00469a50` and callback `0x004719c0`,
|
||||
and then republishes selector `0x49`; selector `0x49` turns that 12-byte result into one keyed
|
||||
bitset object and republishes selector `0x47`; selector `0x47` consumes the resulting ten-slot
|
||||
masks and drops straight into `0x004eb230` plus
|
||||
`shell_resolve_merger_vote_and_commit_outcome` `0x004ebd10`. The same pattern repeats one
|
||||
size smaller for selectors `0x4c`, `0x4d`, and `0x4b`: selector `0x4c` consumes one 10-byte
|
||||
record, either marks the current-session bit directly or opens localized prompt `0x0b82`
|
||||
through `0x00469a50` and callback `0x00471d50`, selector `0x4d` folds that 10-byte result into
|
||||
the keyed bitset object, and selector `0x4b` turns the resulting masks into one ten-dword cache
|
||||
setter at `0x0050c4e0` rooted at `0x006d1a08` plus the paired outcome resolver
|
||||
`0x0050c940`. The direct setter strip after that is explicit too: selector `0x4f` republishes
|
||||
selector `0x6f` when the live session object exists and dataset
|
||||
gate `0x006cd91c` is clear, selector `0x50` copies `[dataset+0x9058]` into `[dataset+0x9054]`,
|
||||
selector `0x51` derives one small session-status code and either republishes selector `0x52` or
|
||||
shell control `0x109`, selectors `0x55`, `0x56`, and `0x57` directly store dataset field
|
||||
`0x9860`, a `0x006ceb9c` inline name, and guard field `[entry+0x1e1]`, selector `0x58` flushes
|
||||
the deferred 16-slot named-profile clear queue `[dataset+0x9864..+0x9873]`, selector `0x59`
|
||||
derives one roster or capacity status and republishes selector `0x5a`, selector `0x5b` is
|
||||
another token-staging forwarder into selector `0x5c`, selector `0x5c` gates a
|
||||
`0x00493960` dispatch plus optional local `0x0046f870` apply, and selector `0x5d` validates
|
||||
one payload string before republishing selector `0x5e`. The next registered band around selectors
|
||||
`0x5e..0x7d` is tighter too. Selector `0x5e` updates the named-profile side table
|
||||
`0x006ceb9c`, mirrors the same string into the resolved live session object, and when the
|
||||
session-side guard is active hashes that string back into `[session+0x48]` and dataset field
|
||||
`[0x006cd8d8+0x8f48]`; selector `0x5f` then stages the current year-derived token and republishes
|
||||
into selector `0x60`, whose body writes one guarded byte field into the same `0x006ceb9c` entry
|
||||
family. The `0x62..0x64` strip forms the same kind of pair over `0x0062be10`: selector `0x62`
|
||||
copies one fixed `0x32`-byte band into the matched company entry, selector `0x63` rejects
|
||||
duplicate field-`0x37` values before forwarding, and selector `0x64` applies that same dword
|
||||
field directly into the matched entry or one live fallback owner. The receive-side correction is
|
||||
explicit now too: selector `0x6b` is the tiny local metric-apply wrapper
|
||||
`0x00472db0 -> 0x0046f870`, selector `0x6c` is the separate train-record wrapper
|
||||
`0x00472dc0 -> 0x0046d780`, selector `0x6d` formats localized status `0x0f4e` into the grounded
|
||||
world outcome-text buffer `[world+0x4b47]`, and selector `0x6e` walks the current keyed bucket
|
||||
under `[session+0x2580]` and marks the first matching companion record by payload hash. The
|
||||
later wrappers are cleaner too: selectors `0x71`, `0x73`, `0x75`, `0x77`, `0x79`, `0x7b`, and
|
||||
`0x7d` are all token-staging forwarders into selectors `0x72`, `0x74`, `0x76`, `0x78`, `0x7a`,
|
||||
`0x7c`, and `0x7e`. Beneath them, selector `0x72` is the heavier counted live-world apply path
|
||||
over `0x0062b2fc`, `0x0062b26c`, and `0x0062bae0`; selector `0x74` dispatches a resolved
|
||||
company-entry id into `0x0062b26c` under the small latch `0x006ce9a8`; selectors `0x76`,
|
||||
`0x7a`, and `0x7c` resolve one company-style entry and then tail into narrower local handlers;
|
||||
and selector `0x78` is the broader projection-or-notify body over `0x0044b160`, `0x00538e00`,
|
||||
and the local-session refresh strip. The next adjacent owner `0x0046e5c0` is broader but still
|
||||
belongs to the same structural neighborhood: in mode `1` it serializes a dual-collection metric
|
||||
blob from `0x0062be10` and `0x006ceb9c`, writing the two row counts into local header bytes and
|
||||
then packing one `0x24`-stride band plus one `0x2c`-stride band behind a `0x10`-byte header; the
|
||||
opposite branch starts validating and applying those packed metrics back into live entries. So
|
||||
that first named block is no longer just a string-name registry; it already includes a real typed
|
||||
static-record family beneath the preview dataset, and it now sits directly beside one broader
|
||||
fixed-record callback-successor strip. Right after the selector-`0x71` batch publisher, the local
|
||||
`0x005ce418` fixed-record family becomes explicit: `0x00473ce0` constructs one `0x187`-byte
|
||||
record from two copied strings, shell-profile bytes `[0x006cec78+0x0d/+0x0f/+0x11]`, owner field
|
||||
`[this+0x04]`, and monotonic sequence dword `[this+0x14]` seeded from `0x006cea48`;
|
||||
`0x00473dc0`, `0x00473e70`, `0x00473ee0`, and `0x00473f30` are the max-sequence, min-sequence,
|
||||
previous-sequence, and next-sequence queries over the same live collection; `0x00473e20` is the
|
||||
boolean scan for any inactive record with a nonzero owner dword; and `0x00473f80` is the real
|
||||
allocator or constructor owner, trimming the collection down to `0x19` entries, allocating one
|
||||
live record through `0x00518900`, and then dispatching the heavier constructor. So the callback
|
||||
registry now leads directly into a concrete preview-side fixed-record collection, not into
|
||||
another anonymous transport helper band. The broader snapshot/apply owner still sits over the
|
||||
same multiplayer collection state. In parallel,
|
||||
`multiplayer_register_session_event_callbacks` allocates and registers the separate session-event
|
||||
transport object at `0x006cd970`. The shell-side bridge into that deeper transport cadence is now
|
||||
tighter: `multiplayer_window_service_loop` and neighboring reset or initializer branches call
|
||||
`multiplayer_flush_session_event_transport`, which forces one status flush and then drops into
|
||||
`multiplayer_transport_flush_and_maybe_shutdown`. That wrapper in turn runs
|
||||
`multiplayer_transport_service_frame`, the recurring pump that services one worker step through
|
||||
`multiplayer_transport_service_worker_once`, sweeps the transport-owned callback tables and field
|
||||
caches through `multiplayer_transport_service_route_callback_tables`, services both the auxiliary
|
||||
status route and the current live route through
|
||||
`multiplayer_transport_service_status_and_live_routes`, and then descends one layer farther into
|
||||
the shared GameSpy route helper `multiplayer_gamespy_route_service_frame` at `0x58d040`. The
|
||||
transport also owns a separate selector-view sidecar beneath that route cadence.
|
||||
`multiplayer_transport_ensure_selector_view_store` allocates the keyed selector-view store at
|
||||
`[transport+0xab4]`, `multiplayer_transport_find_selector_view_entry_by_name` resolves entries
|
||||
from that store, `multiplayer_transport_upsert_selector_name_entry` marks per-slot activity and
|
||||
flags inside each entry, and `multiplayer_transport_mark_selector_slot_views_dirty` plus
|
||||
`multiplayer_transport_reset_selector_view_entry_runtime_state` manage the dirty or refresh fields
|
||||
at `+0xa0`, `+0xa4`, and `+0xa8`. That selector-view maintenance path is now split more cleanly
|
||||
too. The recurring owner is `multiplayer_transport_service_selector_view_refresh_cycle`, which
|
||||
first runs the fast deferred-probe lane:
|
||||
`multiplayer_transport_collect_refreshable_selector_view_entries` walks the store through
|
||||
`multiplayer_transport_filter_insert_refreshable_selector_view_entry`, which now shows that
|
||||
`[entry+0x64]` is not a generic flag bucket but the third selector-slot flag word, parallel to
|
||||
`[entry+0x5c]` and `[entry+0x60]`. In that collector, the `g` and `a` mode-letter bits produced by
|
||||
`multiplayer_transport_parse_selector_mode_letters` become mask `0x0c` in the slot-local flag
|
||||
words, and any third-slot entry carrying those bits at `[entry+0x64]` is excluded from the
|
||||
refreshable set. Eligible entries then pass slot-aware retry timing on `[entry+0x68]`,
|
||||
`[entry+0x6c]`, `[entry+0x78]`, and `[entry+0x7c]`, after which the service loop schedules refresh
|
||||
probes through `multiplayer_transport_schedule_selector_view_entry_refresh_probe`. That fast lane
|
||||
is narrower now too: the entry-side match key `[entry+0x50]` is no longer just an opaque request
|
||||
field. The profile-key callback lanes feed
|
||||
`multiplayer_transport_parse_selector_view_probe_marker`, which decodes one local `X%sX|%d` marker
|
||||
into a probe request id plus displayed version/build integer, and
|
||||
`multiplayer_transport_arm_selector_view_probe_tracking` stores those into `[entry+0x50]` and
|
||||
`[entry+0x54]` before arming the live probe gate at `[entry+0x58]`. The current-selector callback
|
||||
root at `0x59f8b0` is now bounded as well: it resolves and upserts the active selector name,
|
||||
optionally reuses a cached `username` marker to arm probe tracking immediately, then submits the
|
||||
same profile-key bundle with selector context and forwards that selector through callback slot
|
||||
`17`, with the status-route side able to force route-mode transitions `2 -> 3 -> 4` afterward. One
|
||||
layer lower, `multiplayer_transport_handle_profile_key_query_result` at `0x596970` now bounds the
|
||||
per-key result path itself. It treats `username` as the probe-marker field, `b_flags` as the
|
||||
selector mode-letter field, and `(END)` as a real sentinel that publishes a zeroed slot-`22`
|
||||
payload instead of a marker pair. The same helper also hashes the selector-name, key-name, and
|
||||
resolved value text back into the caller table, so the profile-key bundle now looks like a real
|
||||
bounded handoff rather than an anonymous callback cloud. The deferred callback shim
|
||||
`multiplayer_transport_dispatch_selector_view_refresh_probe_result` then walks the keyed store
|
||||
through `multiplayer_transport_finish_selector_view_refresh_probe_if_matching`, which only
|
||||
completes entries whose pending latch `[entry+0x74]` is still armed and whose parsed marker
|
||||
request id `[entry+0x50]` matches the finished request. A failed result `-1` clears the pending
|
||||
latch and increments the consecutive-failure counter at `[entry+0x7c]`. A nonfailure result clears
|
||||
the pending latch, increments the success generation at `[entry+0x78]` and total-success count
|
||||
`[entry+0x98]`, clears `[entry+0x7c]`, stamps the last-success tick at `[entry+0x6c]`, appends the
|
||||
returned sample into the short rolling history at `[entry+0x84..]`, grows the bounded sample-count
|
||||
`[entry+0x94]` up to four, computes the current average into `[entry+0x80]`, and then publishes
|
||||
that averaged `ms` sample through `multiplayer_transport_enqueue_callback_slot24_record`. So the
|
||||
publication boundary is explicit and the request-id ownership is explicit: `[entry+0x80]` now
|
||||
reads as the averaged millisecond probe sample and `[entry+0x54]` as the displayed version/build
|
||||
companion integer. The adjacent route-callback side is tighter too, but it is now kept separate:
|
||||
the staged route-callback path at `0x5958e0` and the later compatibility gate at
|
||||
`multiplayer_transport_route_binding_matches_route_callback_descriptor_tuple` `0x595d00` operate
|
||||
on a compact GameSpy-style server or route descriptor family with a primary endpoint tuple at
|
||||
`[descriptor+0x00]/[+0x04]`, an optional secondary endpoint tuple at `[descriptor+0x08]/[+0x0c]`,
|
||||
string-key lookups such as `hostname` and `gamever`, and numeric-key lookups such as
|
||||
`numplayers`, `numservers`, `numwaiting`, and `gsi_am_rating`. The route-binding side uses that
|
||||
descriptor family's primary dword and host-order port plus the optional secondary tuple against
|
||||
route-binding offsets `+0x54/+0x58` and route word `+0x30`. Current evidence still does not prove
|
||||
that descriptor tuple is the same field family as the selector-view marker companion integer at
|
||||
`[entry+0x54]`. The higher compact decode owners are tighter now too: `0x5907d0` is the
|
||||
allocate-and-append lane for one self-consistent compact payload, while `0x590d00` is the keyed
|
||||
upsert-by-primary-endpoint lane that reuses an existing descriptor when possible and then notifies
|
||||
the owner callback. The route-callback-table runtime above that decode side is tighter now too:
|
||||
`multiplayer_transport_route_callback_table_construct` `0x5905e0` seeds one transport-owned
|
||||
table block, `multiplayer_transport_route_callback_table_release_decoded_schema_dictionary`
|
||||
`0x5906f0` tears down the decoded schema dictionary rooted at `[this+0x08]`,
|
||||
`multiplayer_route_callback_runtime_acquire_shared_string_copy` `0x590540` and
|
||||
`multiplayer_route_callback_runtime_release_shared_string_copy` `0x5905a0` now bound the shared
|
||||
string pool used by that decoded schema, and the higher bring-up owner `0x596090` now clearly
|
||||
splits between `[transport+0xba4]` with owner callback `0x595a40`, the local field-cache family
|
||||
`[transport+0x1724]` seeded through `0x5a08f0/0x595b60`, and `[transport+0x1164]` with owner
|
||||
callback `0x595bc0`, while
|
||||
`multiplayer_transport_route_callback_table_service_receive_decode_state_machine` `0x5908c0`
|
||||
is the current live receive/decode state machine serviced by `0x591290` in table states `2/3`.
|
||||
The callback-owner mode split above that runtime is now explicit too: append-notify `0x590370`
|
||||
publishes mode `0`, compact upsert `0x590d00` publishes mode `1`, remove-notify `0x590430`
|
||||
publishes mode `2`, and the live receive/decode path `0x5908c0` publishes modes `6`, `5`, and
|
||||
`3`.
|
||||
The route-handle lifecycle above that decode path is tighter now too: `0x590740` cleanly resets
|
||||
one table's live route plus decode-side runtime without destroying the outer object; `0x5907a0`
|
||||
is the broader destroy path that also releases the active descriptor collection; `0x590ed0`
|
||||
opens the live route handle into `[this+0x4a0]`, stages the initial outbound request, and seeds
|
||||
state `3` plus the staged receive buffer; `0x5911e0` is the state-`2/3` socket-service wrapper
|
||||
that reads new bytes, grows the staged buffer, and re-enters `0x5908c0`; `0x5912c0` is the
|
||||
one-shot send-with-reopen-retry helper; and `0x590ea0` is the shared disconnect publication and
|
||||
reset tail. The recurring service helper `0x591290` is tighter too: it now first clears the
|
||||
staged intrusive descriptor list through `0x590490` before entering the state-driven seed-or-
|
||||
receive branch. The upstream owners are tighter too: `0x5962e0` is now the field-subscription
|
||||
route-table opener above `[transport+0xba4]`, while `0x596530` is the `gsi_am_rating` reopen
|
||||
path above `[transport+0x18bc]`. On that latter branch, `0x590dc0` is now bounded as the
|
||||
state-`0` raw-endpoint seed pass over the live route handle, repeatedly pulling endpoint tuples
|
||||
through `0x58bc7e` record type `0x1f3` before stamping descriptor flag byte `0x15` with `0x11`.
|
||||
That makes the remaining source-flag meaning narrower too: current evidence now supports reading
|
||||
byte-`0x15` bit `0x1` as a primary-endpoint-seed or endpoint-only marker. In the `gsi_am_rating`
|
||||
dispatcher, clear-bit descriptors can take the richer direct transition lane, while set-bit
|
||||
descriptors are staged through the queued enrichment path and still suppress that direct
|
||||
transition even after the ready bit arrives.
|
||||
The adjacent capacity-descriptor side is tighter too: `0x595bc0` now clearly publishes a
|
||||
descriptor block from live descriptor properties `hostname`, `numwaiting`, `maxwaiting`,
|
||||
`numservers`, and `numplayers` plus three carried sidecar scalars. That sidecar at
|
||||
`[transport+0x1778]` is tighter now too: current evidence says it behaves as one cached pointer
|
||||
into the transient work-record family at `[transport+0x1780]`, because every meaningful branch
|
||||
in `0x595bc0` reads the same `+0x0c/+0x10/+0x18` metadata triplet and replay modes later consume
|
||||
the pointer through `0x5933a0`. The negative result is stronger too: local text-side xrefs still
|
||||
show no direct store to `[transport+0x1778]`, and a wider local sweep also failed to show any
|
||||
obvious `lea`-based replay-band writer. The transient-request lifecycle tightens that further:
|
||||
`0x593330/0x593370/0x593380/0x5934e0/0x5933a0` now fully bound `[transport+0x1780]`, `0x1784`,
|
||||
and `0x1788` without ever touching `[transport+0x1778]`, and the neighboring active-opcode reset
|
||||
helper `0x5929a0` is likewise scoped only to `[transport+0x17fc]`. A broader constructor and
|
||||
teardown pass tightened that further too: `0x596090`, `0x5961b0`, and `0x5962e0` all touch the
|
||||
neighboring replay-band fields without ever seeding `[transport+0x1778]`. A full-binary
|
||||
literal-offset sweep tightens it further still: the only direct `0x1778` hit in `RT3.exe` is
|
||||
the read in `0x595bc0`. One nearby ambiguity is now closed too: the
|
||||
mode-`5` mirror path in `0x595a40` and `0x595e10` does not target `[transport+0x1778]`; it
|
||||
writes `[transport+0x54]` and mirrors the same staged route companion dword only into queue-side
|
||||
slot `[transport+0x1724+0x24]` through `0x005a0940`. So the sidecar writer remains upstream of this
|
||||
leaf publisher. Mode `0` is now also tied more cleanly to the generic descriptor append-notify lane
|
||||
at `0x590370`, while mode `2` stays outside this helper as the separate
|
||||
remove-notify-and-stage path at `0x590430`. The opcode-`2` payload boundary is tighter too:
|
||||
`0x592ae0` now grounds that payload
|
||||
as a seven-dword block with an owned string slot at `+0x08`, so live mode supplies a populated
|
||||
payload while modes `3` and `5` deliberately enqueue an all-zero payload and reuse only
|
||||
the wrapper-side sidecar metadata. Those two modes are tighter now too: they are the live
|
||||
receive-state owner callbacks emitted by `0x5911e0 -> 0x5908c0`, not loose generic replay
|
||||
guesses. So those paths are better read as delayed metadata replays over one cached work record,
|
||||
not over a separate anonymous cache blob. The neighboring
|
||||
capacity-owner split is tighter now too: `0x595bc0` only stages descriptor records for modes
|
||||
`0`, `3`, and `5`; the upstream route-callback-table owner still delivers modes `1`, `2`, and
|
||||
`6`, but those are explicit no-ops in this capacity leaf. So the owner wiring itself is no
|
||||
longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring
|
||||
work queue is tighter too: `0x593330/0x593370/0x593380` now bound `[transport+0x1780]` as the
|
||||
construct/clear/destroy owner family, while `0x5933a0`, `0x5934e0`, and `0x593570` ground the
|
||||
remove, allocate, and completion side over that same collection. The small sibling `0x593400` is
|
||||
tighter too: it is a pure work-record uniqueness predicate over field `+0x0c`. Its caller is
|
||||
tighter now too: `0x58d720` is an immediate-drain quiescence gate over one transport context id,
|
||||
using `0x593400` for the queued work family at `[transport+0x1780]` and `0x592970` for the
|
||||
active opcode-record collection at `[transport+0x17fc]`. The strongest current read is that
|
||||
`0x5934c0` seeds that shared drain context id first, then the transport copies it into queued
|
||||
work field `+0x0c` and active opcode-record field `+0x14` before the immediate-drain roots wait
|
||||
on one shared disappearance test rather than on a vague settle loop. The currently grounded
|
||||
roots are `0x58df20`, the neighboring formatted selector-text publish path at `0x58dfb0`, and
|
||||
callback-table registration at `0x58e200`. The active-opcode side is tighter too: `0x5927b0`
|
||||
now bounds the per-record service-and-retire path, `0x592800` the wider context-or-idle sweep,
|
||||
`0x5929a0` the remove-by-opcode-type sweep, and `0x5929f0` the narrower opcode-`3`
|
||||
field-snapshot removal keyed by the subscribed callback-pair payload. That also corrects
|
||||
`0x595b80`, whose final cleanup is an active field-snapshot purge rather than a queued-work
|
||||
drain. The adjacent route-callback descriptor-table lifecycle is tighter too: `0x590410` now
|
||||
grounds `[table+0x5bc]` as the staged intrusive descriptor-list head, `0x590430` is the generic
|
||||
remove-notify-and-stage lane, `0x590490` releases the staged list, and `0x5904d0` releases the
|
||||
active descriptor collection before tearing that staged list down. That also makes the earlier
|
||||
`0x5962e0` “release active descriptors” step explicit. The callback-table attach side now constrains the
|
||||
same work-record metadata family a little further too: `0x593650` deliberately duplicates its
|
||||
first caller metadata dword into both fields `+0x0c` and `+0x10`, while carrying the second
|
||||
caller metadata dword in `+0x18`. The lower opcode wrappers are tighter now too: `0x592a40`
|
||||
turned out to be the explicit opcode-`1` trigger wrapper whose constructor is a no-op and whose
|
||||
active-side service is `0x5913c0`, while `0x592c40` is the real `0x08`-byte explicit opcode-`5`
|
||||
binding leaf. The earlier opcode-`4` read was just the table-indexing mistake in `0x5928a0`:
|
||||
selector `4` lands on the row at `0x5e2044`, not the row at `0x5e2034`. The producer side is tighter too:
|
||||
bound-route requests, selector-text route requests, and the type-`9` text fastpath also stage
|
||||
that same triplet through `0x5934e0`, and the fastpath shim `0x593d00` now gives the cleanest
|
||||
proof of the callback split by only re-emitting the follow-on lane when `+0x10` is nonnull and
|
||||
then forwarding `(+0x10, +0x18, +0x0c)` into `0x593170` as callback function, callback
|
||||
companion, and trailing drain context. So the replay-side triplet is clearly a broader
|
||||
transport callback-wrapper family, not one fixed route-only tuple. The nearby
|
||||
field-subscription side is tighter too: `0x592b50` now clearly uses `[transport+0x1774]` as a
|
||||
cached progress percentage under `[transport+0xba4]`, and `0x5962e0` seeds that percentage to
|
||||
`1` just before the first immediate mode-`3` snapshot. The nearby route-callback-table
|
||||
lifecycle is tighter now too: `0x596090` seeds `[transport+0xba0]` as the callback-plumbing
|
||||
enable latch, clears staged payload slot `[transport+0xb50]`, and constructs the three owner
|
||||
branches rooted at `[transport+0xba4]`, `[transport+0x1164]`, and `[transport+0x18bc]`;
|
||||
`0x596210` is the recurring service sweep over those same three tables plus the local field
|
||||
cache and queued-descriptor family; `0x596060` is the explicit `gsi_am_rating` reset; and
|
||||
`0x596530` is the reopen-from-stored-label sibling above that same am-rating table. The
|
||||
matching local cleanup is tighter too: `0x5962c0` is the explicit staged route-callback payload
|
||||
clear on `[transport+0xb50]`, while `0x595ce0` now clearly resets only the capacity-descriptor
|
||||
route callback table at `[transport+0x1164]`, not the field-subscription table at
|
||||
`[transport+0xba4]`. The remaining gap on the capacity side is therefore narrower: the carried
|
||||
sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet reused
|
||||
elsewhere (`drain context id +0x0c`, `callback fn +0x10`, `callback companion +0x18`), and the
|
||||
negative result is stronger too: nearby replay-band fields `[transport+0x176c]`,
|
||||
`[transport+0x1770]`, `[transport+0x1774]`, `[transport+0x177c]`, `[transport+0x1780]`, and
|
||||
`[transport+0x1784]` all have direct local owners while `[transport+0x1778]` still appears only
|
||||
as the single read in `0x595bc0`; even the broader callback-owner lifecycle now skips it while
|
||||
seeding, servicing, resetting, reopening, or tearing down those neighboring tables and caches.
|
||||
The constructor now closes that local search further: `0x58dc50` bulk-zeroes the full transport
|
||||
body and still never writes a nonzero value into `[transport+0x1778]` before later explicit
|
||||
neighbor initialization. The callback-binding owner stack now tightens that boundary too:
|
||||
`0x5934e0` stages the shared work-record metadata triplet, `0x593650` binds it into the
|
||||
callback-table worker path, and `0x593570` later consumes and republishes it, while
|
||||
`[transport+0x1778]` still appears only as the borrowed sidecar read in `0x595bc0`. So this
|
||||
edge is now locally closed, and the remaining producer looks like an upstream callback or worker
|
||||
handoff rather than one missing ordinary field store in the local cluster. The adjacent staged-route
|
||||
callback side is tighter too: `0x595860` is now bounded as the
|
||||
submit-result handler beneath `0x5958e0`, and the old `[transport+0xac0]` ambiguity there is now
|
||||
gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]`
|
||||
together with target `[0xb48]` to decide whether staged route-callback traffic can push the
|
||||
multiplayer route-mode ladder from `2` into `3` and later `4`. The selector-view counter beneath
|
||||
that gate is tighter now too: `0x594e30` counts slot-`2` entries whose flag dword carries bit
|
||||
`0x20`, optionally filtered by the current transport name buffer. The selector-view mutation
|
||||
family under that same lane is tighter too: `0x594a30` is now the direct keyed-store remover,
|
||||
`0x594fb0` clears one selector-slot ownership pointer plus its slot-local flag dword and drops
|
||||
the whole entry when no slots remain, `0x595010` rekeys one selector-view entry under a new name
|
||||
while preserving the `0x40..` runtime band, and callback root `0x59f9c0` now reads as the
|
||||
sibling lane that clears one named selector-view slot, publishes callback slot `18`, and may
|
||||
still re-enter the route-mode setter from the same slot-`2` status and generation gates. The
|
||||
neighboring callback roots are tighter now too: `0x5950a0` clears one selector slot from every
|
||||
selector-view entry in the keyed store, `0x59fab0` is the rename or relabel sibling above
|
||||
`0x595010`, `0x59faf0` updates one selector slot's fixed sample-text buffer and refreshes the
|
||||
active selector object when present, and `0x59fb60` replaces one selector slot's name set,
|
||||
requests the default profile-key bundle for that slot, and publishes callback slot `20`. Slot
|
||||
`16` is tighter now too: current grounded caller `0x59f440` forwards the staged route-callback
|
||||
payload handle from `[transport+0xb50]` through `0x592ea0` just before route mode `5`. The
|
||||
last adjacent callback root in that block is tighter now too: `0x59fbd0` is the built-in
|
||||
per-slot profile-key query sibling. It resolves the caller selector name into one slot index,
|
||||
forwards the caller trio into `0x596b90`, and then publishes callback slot `28`; that lower
|
||||
helper indexes one slot-specific built-in string pair from `[transport+0x189c]` and
|
||||
`[transport+0x18ac]`, reuses the generic per-key handler `0x596970`, and only republishes slot
|
||||
`28` when that lower query path succeeds.
|
||||
The compact-header side is tighter now too: `0x58fe20` and `0x58ff20` now show that compact
|
||||
payloads always carry the primary IPv4 dword and that header bit `0x10` only gates whether the
|
||||
primary port word is inline or inherited from the owner default port. `0x58fe90` now validates
|
||||
the `0x40` inline keyed-property vector against the owner schema, and `0x58fe50` validates the
|
||||
signed-`0x80` trailing string-pair tail before decode. `0x58ff60` then grounds bit `0x02` as
|
||||
the inline secondary IPv4 dword branch, bit `0x20` as the paired secondary-port word branch with
|
||||
owner-port fallback, bit `0x08` as one still-unresolved auxiliary dword stored at
|
||||
`[descriptor+0x10]`, bit `0x40` as one inline keyed-property vector decoded through the
|
||||
property-store writers, and signed bit `0x80` as one trailing string-pair tail. The
|
||||
descriptor-state side is tighter now too: the shared queue helper at
|
||||
`0x005a09a0` stamps pending state `0x4` for the local field-cache family `[transport+0x1724]`
|
||||
and pending state `0x8` for the `gsi_am_rating` queued-descriptor family `[transport+0x1e7c]`,
|
||||
while later service through `0x005a0c80` promotes those pending tags into ready bits `0x1` and
|
||||
`0x2` in descriptor byte `[entry+0x14]`. That makes the current transport-side tests cleaner:
|
||||
`0x58d1c0` is the field-cache ready gate, `0x58d1d0` is the `gsi_am_rating` queued-descriptor
|
||||
ready gate, and `0x58d230` is the remaining flag-byte split between direct primary-endpoint
|
||||
handling at `[transport+0x18bc]` and the queued path at `[transport+0x1e7c]`. That byte-`0x14`
|
||||
story is no longer queue-only either: `0x58ff60` can also OR in bit `0x1` after the inline
|
||||
keyed-property vector and bit `0x2` after the signed string-pair tail. The flag-byte split is no
|
||||
longer purely behavioral either: current evidence now says byte `[descriptor+0x15]` bit `0x1` is
|
||||
a source-side descriptor header bit, explicitly seeded during the primary-endpoint table refresh
|
||||
around `0x590dc0` and preserved by the compact descriptor decode path at `0x58ff60`, rather than
|
||||
a queue-generated runtime state. The `gsi_am_rating` dispatcher side is tighter too: that same
|
||||
bit no longer just looks like a direct-versus-queued routing split, because `0x595e10` also uses
|
||||
it to suppress the direct `0x595dc0` transition even after queued ready bit `0x2` is present.
|
||||
The descriptor body is tighter too: `[descriptor+0x20]` is now the intrusive next-link used by
|
||||
the transport-owned primary-endpoint list headed at `[table+0x5bc]`, and `[descriptor+0x1c]` is
|
||||
now the special numeric scalar behind the current `queryid`/`ping` fallback pair. The compact-only
|
||||
auxiliary dword at `[descriptor+0x10]` is tighter in a negative way too: local xref scans now
|
||||
only show it being preserved by later generic helpers like
|
||||
`generic_record_0x1c_deep_copy_with_owned_string_at_0x08` `0x591410` and the adjacent
|
||||
callback-marshaling wrappers `0x591480` and `0x591510`, not read through any dedicated semantic
|
||||
accessor yet. The route-event dispatcher side is tighter too: the mode-`5` tails in both
|
||||
callback families do not copy a descriptor-local field but instead mirror the transport-staged
|
||||
companion dword at `[this+0x490]` into `[this+0x54]` and queue-side slot `[this+0x1724+0x24]`. The
|
||||
`gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts
|
||||
the surviving primary-endpoint table through `0x590310` in mode `1` with key `gsi_am_rating`,
|
||||
then selects the new head through `0x590480` before re-entering the route-transition path. The
|
||||
same service loop also owns a slower sidecar lane keyed off `[entry+0xa4]`:
|
||||
`multiplayer_transport_select_stale_selector_view_progress_entry` walks the store through
|
||||
`multiplayer_transport_pick_stale_selector_view_progress_entry`, picks one stale entry whose
|
||||
progress latch `[entry+0x9c]` is clear and whose last progress tick `[entry+0x70]` is old enough,
|
||||
and then hands it to `multiplayer_transport_stage_selector_view_progress_snapshot`. That helper
|
||||
now looks more bounded too: it rebuilds the core `X%sX` marker text from `[entry+0x50]` through
|
||||
`multiplayer_transport_format_selector_view_probe_marker_core`, formats one `PNG %s %d` line
|
||||
around that marker and the entry-local averaged millisecond sample at `[entry+0x80]`, appends bounded
|
||||
selector-slot `PNG` fragments for live overlapping slots, marks progress-snapshot state in flight
|
||||
at `[entry+0x9c]`, and stamps both `[entry+0x70]` and the transport-wide throttle tick
|
||||
`[transport+0xaec]`. So the selector-view sidecar no longer looks like one undifferentiated
|
||||
refresh bucket: it has a faster deferred-probe lane plus a slower progress-snapshot lane, both
|
||||
still under the shell-owned multiplayer transport cadence. The two descriptor lanes installed by
|
||||
`multiplayer_transport_attach_callback_table_descriptor` are now tighter too. The first lane
|
||||
rooted at `0x59f5c0` can arm deferred-close state on the owner transport and then forward through
|
||||
callback slot `23`. The second lane is no longer just a loose selector-view bucket:
|
||||
`multiplayer_transport_callback_dispatch_selector_name_payload_lane` at `0x59f650` classifies
|
||||
selector payloads through `multiplayer_transport_is_selector_control_line`, routes `@@@NFO`
|
||||
control lines into `multiplayer_transport_sync_selector_view_nfo_r_flag`, and otherwise publishes
|
||||
either callback slot `13` or the split token-plus-tail callback slot `14` through
|
||||
`multiplayer_transport_split_selector_payload_token_and_tail`. That local `@@@NFO` helper is now
|
||||
bounded more tightly too: it only accepts lines ending in the literal `X\` tail, searches for the
|
||||
field marker `\$flags$\`, and then sets or clears bit `0x2` in the third selector-slot flag word
|
||||
at `[entry+0x64]` depending on whether that field contains the letter `r` before the next
|
||||
backslash. The sibling `multiplayer_transport_callback_dispatch_current_selector_payload_lane` at
|
||||
`0x59f720` first resolves the active selector through `0x5951a0`, then handles the
|
||||
current-selector variants of the same control vocabulary: `@@@NFO` continues into the same local
|
||||
`r`-flag sync helper, `@@@GML` plus mode-`3/4` payloads feed the shared control-token helper
|
||||
`multiplayer_transport_handle_gml_or_png_selector_control`, and the remaining non-control payloads
|
||||
publish callback slot `9`. That shared helper now narrows the `GML` and `PNG` split materially:
|
||||
when the token is `GML` and the tail is `Disconnected`, it requires the active selector-view entry
|
||||
to pass `multiplayer_transport_selector_view_entry_has_gml_disconnect_gate`, which currently means
|
||||
the entry participates in the third selector slot and has bit `0x20` set in `[entry+0x64]`, before
|
||||
it forces one status-pump pass, emits callback slot `16`, and re-enters route mode `5`. Its
|
||||
sibling `PNG` branch resolves a named selector-view entry from the tail text and, when that entry
|
||||
overlaps the active selector-slot ownership, refreshes selector-view runtime state through
|
||||
`0x5948f0` and republishes callback slot `25`. Alongside those dispatchers, one grounded branch at
|
||||
`0x59f560` still updates selector-view runtime state through `0x5948f0` and forwards the same
|
||||
selector-name pair through callback slot `25`, while `0x59f850` resets selector text state through
|
||||
`0x5954b0`, forwards through callback slot `19`, and when selector `2` is active in a nonterminal
|
||||
route mode re-enters `multiplayer_transport_set_route_mode` with mode `1`. The low-level route
|
||||
helper still looks like a two-part cycle:
|
||||
`multiplayer_gamespy_route_service_retry_and_keepalive_timers` handles challenge or retry pressure
|
||||
and periodic outbound control traffic around the `master.gamespy.com`, `PING`, `natneg`,
|
||||
`localport`, `localip%d`, and `statechanged` strings, while
|
||||
`multiplayer_gamespy_route_drain_inbound_packets` drains inbound datagrams and dispatches
|
||||
semicolon lines, backslash-delimited key bundles, and `0xfe 0xfd` GameSpy control packets. The
|
||||
transport-owned callback story is now narrower too. The shared route constructor
|
||||
`multiplayer_gamespy_route_construct_and_seed_callback_vector` seeds `[route+0x88]` through
|
||||
`[route+0x9c]` from the caller-supplied transport callback table, records the owner transport at
|
||||
`[route+0x104]`, and explicitly zeroes `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]` before
|
||||
any later patch-up. For the transport-owned status route,
|
||||
`multiplayer_transport_try_connect_status_route` then patches `[route+0xa0]` through
|
||||
`multiplayer_gamespy_route_set_extended_payload_callback` to point at
|
||||
`multiplayer_transport_forward_validated_extended_route_payload` `0x00597330`, which simply
|
||||
forwards the validated payload wrapper into the owner callback at `[transport+0x17f4]` with
|
||||
context `[transport+0x17f8]`. The grounded live-route connect path at
|
||||
`multiplayer_transport_try_connect_live_route` does not currently perform any matching
|
||||
post-construction patch for `[route+0xa0]`, `[route+0xa4]`, or `[route+0xd4]`, and the higher
|
||||
route-mode state machine now looks consistent with that: `multiplayer_transport_set_route_mode`
|
||||
latches the requested small mode at `[this+0x18b8]`, then uses mode `0` for the direct-versus
|
||||
queued `gsi_am_rating` split, mode `1` for the ready-bit plus queued fallback, mode `2` for
|
||||
pending-descriptor cleanup, mode `3` for the empty-table fallback, mode `4` for deferred
|
||||
route-status recovery, and mode `5` for copying the staged route companion dword into `[this+0x54]`
|
||||
and queue-side slot `[this+0x1724+0x24]`. The current grounded mode transitions still switch by releasing route
|
||||
objects through `multiplayer_gamespy_route_release_and_free` and rebuilding them through
|
||||
`multiplayer_transport_try_connect_live_route`, not by mutating callback slots in place. The
|
||||
parser behavior is now tighter as well: semicolon lines only dispatch when
|
||||
`[route+0xd4]` is non-null, and the subtype-`6` raw fallback only dispatches when `[route+0xa4]`
|
||||
is non-null. For the currently grounded transport-owned status and live routes, those two branches
|
||||
therefore remain optional and can cleanly no-op instead of implying a hidden mandatory callback
|
||||
path. Inside the packet parser, subtype `4` is no longer an unknown callback hop: after cookie
|
||||
validation it dispatches through `[route+0x9c]`, which the transport-owned status and live routes
|
||||
seed to `multiplayer_transport_handle_validated_route_cookie_event` `0x005972c0`. That helper
|
||||
either marks route progress and re-enters `multiplayer_transport_set_route_mode`, or forwards the
|
||||
event id plus payload into the owner callback at `[transport+0x17f0]` with context
|
||||
`[transport+0x17f8]`. The surrounding status-route callback vector is tighter now too:
|
||||
`0x005970e0` publishes either the active selector text or the averaged probe sample at
|
||||
`[entry+0x80]` and otherwise falls back to owner callback `[transport+0x17e0]`;
|
||||
`0x00597180` is a straight owner-forwarding lane through `[transport+0x17e4]`;
|
||||
`0x005971b0` seeds the local status-control id list and can then notify owner callback
|
||||
`[transport+0x17e8]`; and `0x00597270` returns the third selector-slot generation counter
|
||||
`[transport+0xac0]` on its bounded local branch before falling back to owner callback
|
||||
`[transport+0x17ec]`. Subtype `6` still validates the same cookie, dedupes one 32-bit cookie or
|
||||
packet id, and then dispatches the trailing payload through the natneg-or-raw callback layer
|
||||
rooted at `[route+0xa0]` and `[route+0xa4]`. This separates the shell-frame preview refresh at
|
||||
`0x006cd8d8` from the actual transport cadence at `0x006cd970`, and also separates that transport
|
||||
cadence from the lower GameSpy route-service and packet-parser layer beneath it.
|
||||
- Evidence: `function-map.csv`, `pending-template-store-management.md`,
|
||||
`pending-template-store-functions.csv`, plus objdump caller traces showing
|
||||
`multiplayer_window_service_loop` reaching `multiplayer_flush_session_event_transport` and the
|
||||
transport pump chain `multiplayer_flush_session_event_transport ->
|
||||
multiplayer_transport_flush_and_maybe_shutdown -> multiplayer_transport_service_frame ->
|
||||
multiplayer_transport_service_worker_once -> multiplayer_transport_drain_request_text_queue`.
|
||||
- Open Questions: unresolved request-id semantics for `1`, `2`, `4`, and `7`; whether any
|
||||
non-mode-path helper outside the currently grounded release-and-rebuild flow ever patches
|
||||
`[route+0xa4]` and `[route+0xd4]` for the transport-owned status/live routes, or whether those
|
||||
packet families are simply unused in this stack; and how far the Multiplayer preview-dataset
|
||||
machinery is reused outside `Multiplayer.win` beyond the currently grounded `.gmt` save-mode hook.
|
||||
|
||||
9
docs/control-loop-atlas/next-mapping-passes.md
Normal file
9
docs/control-loop-atlas/next-mapping-passes.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
## Next Mapping Passes
|
||||
|
||||
- Resolve the owner-side callback roles behind the validated GameSpy packet branches, especially
|
||||
`[route+0x9c]`, `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]`.
|
||||
- Determine whether any later world-mode branch beneath the frame owner bypasses the shell
|
||||
controller input path for non-cursor gameplay input, since the current grounded overlay and cursor
|
||||
helpers still reuse `0x006d4018`.
|
||||
- Keep detailed pending-template or transport work scoped to the specific atlas edges that remain
|
||||
unresolved.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
## Presentation, Overlay, and Frame Timing
|
||||
|
||||
- Roots: the bootstrap-owned `shell_service_pump_iteration` at `0x00483f70`, the shell-state service
|
||||
pass `shell_state_service_active_mode_frame` at `0x00482160`, the installed global shell
|
||||
controller at `0x006d4024`, the pending frame-cycle owner `shell_service_frame_cycle` at
|
||||
`0x00520620`, and the frame-time history path under `shell_update_frame_time_history` at
|
||||
`0x0051fd70`.
|
||||
- Trigger/Cadence: recurring bootstrap-owned shell service work once the active mode, controller
|
||||
window, and display runtime are live; special modal or content-building paths can also force
|
||||
immediate frame servicing inside that broader cadence.
|
||||
- Key Dispatchers: `shell_service_pump_iteration`, `shell_state_service_active_mode_frame`,
|
||||
`shell_service_frame_cycle`, `shell_refresh_presentation_frame`,
|
||||
`shell_update_frame_time_history`, `shell_get_smoothed_frame_scalar`,
|
||||
`shell_set_gamma_ramp_scalar`, and overlay builders such as
|
||||
`shell_queue_single_world_anchor_overlay`, `shell_queue_world_anchor_overlay_list`, and
|
||||
`shell_queue_indexed_world_anchor_marker`.
|
||||
- State Anchors: shell state at `0x006cec74`, active mode pointer `0x006cec78`, shell frame history
|
||||
ring at `0x006d403c`, display/runtime state inside the controller block near `[this+0x114282]` and
|
||||
`[this+0x11428a]`, presentation service pointer at `[this+0x0c]`, and the native controller window
|
||||
handle at `[this+0x00]`.
|
||||
- Subsystem Handoffs: the bootstrap-owned pump runs shell-bundle polling and shell-state maintenance
|
||||
before the shell-state pass synchronizes active-mode helpers and modal-status work and dispatches
|
||||
the controller frame cycle, which then consumes world/object state for overlays and presentation
|
||||
refresh, uses the controller window handle for one-time `ShowWindow` and related UI interaction,
|
||||
and drains the deferred work queues at the end of the cycle.
|
||||
- Evidence: function-map rows for `shell_service_pump_iteration`,
|
||||
`shell_state_service_active_mode_frame`, `shell_service_frame_cycle`,
|
||||
`shell_flush_deferred_work_queues`, frame history, gamma ramp, world-anchor overlay builders, and
|
||||
graphics runtime helpers.
|
||||
- Open Questions: how simulation cadence rendezvous with the shell presentation cadence once
|
||||
gameplay takes over and whether gameplay stepping exits this shell-owned controller path entirely.
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
## Shell UI Command and Deferred Work Flow
|
||||
|
||||
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
|
||||
- Trigger/Cadence: event-driven UI command dispatch plus deferred-message queue flushing during
|
||||
shell activity.
|
||||
- Key Dispatchers: `shell_dispatch_ui_command`, `shell_enqueue_deferred_work_message`,
|
||||
`shell_post_deferred_message_type5`, `shell_post_deferred_message_type6`,
|
||||
`shell_enqueue_deferred_message_type4`, `shell_enqueue_deferred_message_type1`.
|
||||
- State Anchors: shell object at `0x0062be68`, queue roots around `[this+0x11369d]`,
|
||||
`[this+0x1136a1]`, and `[this+0x1136a5]`, global routing gates at `0x006d4034`, and the separate
|
||||
detail-panel controller rooted at `0x006d0818`.
|
||||
- Subsystem Handoffs: routes into graphics config, scenario-text export, overlay generation,
|
||||
multiplayer UI, shell detail windows such as `EditorPanel.win` and `TrainDetail.win`, and
|
||||
presentation-facing deferred work later drained by `shell_service_frame_cycle`.
|
||||
- Evidence: function-map shell rows around `0x00464410`, `0x004d4500`, `0x004ddbd0`, and
|
||||
`0x0051f1d0..0x0051f460`.
|
||||
- Open Questions: whether the shell command dispatcher is also used by hotkeys or only by UI
|
||||
callback tables; the current `0x006d0818` detail-panel path looks shell-only and does not yet
|
||||
explain gameplay world entry.
|
||||
|
||||
|
|
@ -6,7 +6,9 @@ confidence.
|
|||
|
||||
The handbook index in `docs/README.md` should stay high-level. Per-pass function additions and
|
||||
renames belong here and in the derived export artifacts, not in the README. High-level control-loop
|
||||
and subsystem flow belongs in `docs/control-loop-atlas.md`, with this file staying function-centric.
|
||||
and subsystem flow belongs in the split atlas under `docs/control-loop-atlas/`, with
|
||||
`docs/control-loop-atlas.md` serving as the compatibility index and this file staying
|
||||
function-centric.
|
||||
|
||||
## Canonical Schema
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ handoffs, then feeds the function-by-function Rust rewrite and later DLL-based r
|
|||
|
||||
1. Confirm the target binary and record its hash.
|
||||
2. Refresh the exported baseline artifacts.
|
||||
3. Update `docs/control-loop-atlas.md` with newly grounded loop roots, dispatchers, cadence points,
|
||||
state anchors, or subsystem handoffs.
|
||||
3. Update the relevant file under `docs/control-loop-atlas/` with newly grounded loop roots,
|
||||
dispatchers, cadence points, state anchors, or subsystem handoffs, and touch
|
||||
`docs/control-loop-atlas.md` only if the top-level section layout changes.
|
||||
4. Analyze in Ghidra.
|
||||
5. Cross-check suspicious findings in Rizin or with CLI tools.
|
||||
6. Update the function map with names, prototypes, ownership, confidence, and loop-relevant notes.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue