Add headless runtime tooling and Campaign.win analysis
This commit is contained in:
parent
57bf0666e0
commit
27172e3786
37 changed files with 11867 additions and 302 deletions
|
|
@ -142,17 +142,69 @@ transition.
|
|||
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 constructor jump table now resolves mode `1` to `Game.win`, mode `2` to `Setup.win`,
|
||||
mode `3` to `Video.win`, mode `4` to `LoadScreen.win`, mode `5` to `Multiplayer.win`, mode `6`
|
||||
to `Credits.win`, and mode `7` to `Campaign.win`. The strongest current load-side owner inside
|
||||
that table remains the mode-`4` branch around `0x4830ca`, which publishes the new active mode
|
||||
object into `0x006cec78` and then calls `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
as `thiscall(active_mode, 1, 0)`. The caller split above that owner is tighter now too:
|
||||
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
|
||||
|
|
@ -222,6 +274,37 @@ transition.
|
|||
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
|
||||
|
|
@ -265,7 +348,33 @@ transition.
|
|||
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 adjacent region-side worker family is tighter in a negative way too: the setup
|
||||
`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
|
||||
|
|
@ -295,8 +404,95 @@ transition.
|
|||
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`. Only `0x0e81` also sets `[0x006cec7c+0x82] = 1`, which
|
||||
is currently the strongest sandbox-side anchor beneath the later `.gmx` load family.
|
||||
only then issues shell request `0x0cc`. The file-side staging bytes in that bridge are now
|
||||
tighter too: across the checked `Alternate USA`, `Southern Pacific`, and `Spanish Mainline`
|
||||
map/save pairs, payload `+0x33` stays `0`, but payload `+0x22` and token block `+0x23..+0x32`
|
||||
do not preserve the earlier map-to-save pairing seen at `+0x14/+0x3b2`. Instead they split by
|
||||
the finer file family, with examples `Alternate USA.gmp = 0x53 / 0131115401...` versus
|
||||
`Autosave.gms = 0xae / 01439aae01...`, `Southern Pacific.gmp = 0xeb / 00edeeeb...` versus
|
||||
`p.gms = 0x21 / 0100892101...`, and `Spanish Mainline.gmp = 0x5b / 0044f05b...` versus
|
||||
`g.gms = 0x7a / 0022907a...`. So the validated launch bridge now looks more like a file-family
|
||||
staging lane than a simple copy-forward of the earlier setup summary fields. The destination-side
|
||||
consumers are tighter now too: `[profile+0xc4]` is no longer just an unnamed staged byte, but the
|
||||
campaign-progress slot later consumed by the numbered `%s%02d.gmc` save helper `0x00517c70`;
|
||||
`[profile+0x7d]` is the same compact option byte mirrored back into the `Setup.win` option
|
||||
controls `0x0ce9..0x0ced`; and the campaign-side selector block is no longer only a one-byte
|
||||
anchor. Direct `RT3.exe` disassembly of the campaign-side dispatcher at `0x004b89c0` now shows
|
||||
the exact mirror shape: when the local campaign-page selector `[window+0x78] <= 4`, the helper
|
||||
writes one highlighted progress control `0x0c372 + [profile+0xc4]` and then mirrors the full
|
||||
sixteen-byte band `[profile+0xc6..+0xd5]` into controls `0x0c382..0x0c391` through repeated
|
||||
`0x540120` calls. The same body also treats `[profile+0xc4]` as an index into the fixed
|
||||
sixteen-entry scenario-name table at `0x00621cf0`, whose observed entries include `Go West!`,
|
||||
`Germantown`, `Central Pacific`, `Texas Tea`, `War Effort`, `State of Germany`, `Britain`,
|
||||
`Crossing the Alps`, `Third Republic`, `Orient Express`, `Argentina Opens Up`,
|
||||
`Rhodes Unfinished`, `Japan Trembles`, `Greenland Growing`, `Dutchlantis`, and
|
||||
`California Island`. On the launch-side branch the same helper writes startup selector `6`,
|
||||
copies the resolved campaign filename into `[profile+0x11]`, forces `[profile+0xc5] = 1`, and
|
||||
then issues shell request `0x0cc`. The lower refresh tail at `0x004b8d49..0x004b8d69` also
|
||||
shows the observed page-band split over the same progress byte: values `< 5` pair with campaign
|
||||
page `1`, values `5..9` with page `2`, values `10..12` with page `3`, and values `>= 13` with
|
||||
page `4`. So `[profile+0xc6..+0xd5]` is no longer a generic opaque span at all, but the staged
|
||||
sixteen-byte per-scenario campaign selector or unlock band consumed directly by `Campaign.win`.
|
||||
The message-dispatch side is tighter now too. The local classifier at `0x004b91d8` routes the
|
||||
control range `0x0c352..0x0c39b` through five concrete case classes: `0x0c352..0x0c361` enter
|
||||
the shared selector or launch branch, `0x0c362..0x0c367` force local page `1`,
|
||||
`0x0c368..0x0c392` force local page `2`, `0x0c393..0x0c396` force local page `0`, and
|
||||
`0x0c39b` alone forces local page `3`. That matches the constructor and refresh shape: the
|
||||
sixteen scenario selector controls live at `0x0c352..0x0c361`, the four page-strip controls
|
||||
live at `0x0c368..0x0c36b`, the six-arrow auxiliary strip lives at `0x0c36c..0x0c371`, the
|
||||
progress band lives at `0x0c372..0x0c381`, the ribbon or selector band lives at
|
||||
`0x0c382..0x0c391`, the string or voice target lane lives at `0x0c392`, the four exit controls
|
||||
live at `0x0c397..0x0c39a`, and `0x0c39b` remains the one-off special action control that
|
||||
spawns or dismisses the campaign-side companion object. The `Campaign.win` blob now exposes that
|
||||
structural split directly too: alongside the art-backed `0x0004c3xx` records it carries
|
||||
anonymous zero-name records with the same `0x0bb8 / 0 / 0x0bb9` prelude but selector words in
|
||||
the `0x0010c3xx` family. Current local extraction shows exactly
|
||||
`0x0010c352..0x0010c361`, `0x0010c362..0x0010c367`, `0x0010c393..0x0010c396`, and one
|
||||
`0x0010c39b` record, which is the same non-`.imb` band the message dispatcher treats as the
|
||||
structural selector and page-write family. Their second selector word then buckets those records
|
||||
under the same page-strip ids as the art-backed records: `0xc368` for
|
||||
`0xc352..0xc356`, `0xc362`, `0xc393`, and `0xc39b`; `0xc369` for `0xc357..0xc35b`,
|
||||
`0xc363`, and `0xc394`; `0xc36a` for `0xc35c..0xc35e`, `0xc365..0xc366`, and `0xc395`; and
|
||||
`0xc36b` for `0xc367`, `0xc35f..0xc361`, and `0xc396`. One final anonymous record at
|
||||
`0x0002c392` has no page bucket at all, which fits the already-grounded read of `0xc392` as the
|
||||
one-off string or voice target lane rather than a normal page-cell control. The anonymous body
|
||||
layout is only partially named, but one file-side distinction is already stable: the ordinary
|
||||
structural selector/page records all carry footer words `0xd3000000` and `0xd2000007` at
|
||||
relative `+0x98/+0x9c`, while the lone `0xc392` record carries `0x00000000/0x00000000` there.
|
||||
So `0xc392` is no longer just “outside the page buckets”; it is also the only current
|
||||
anonymous-record outlier in that trailing structural footer pair. The structural selector side is
|
||||
tighter now too: `0xc352..0xc361` partition exactly as `5 + 5 + 3 + 3` across page buckets
|
||||
`0xc368..0xc36b`, matching the observed campaign page bands for scenario progress values `< 5`,
|
||||
`5..9`, `10..12`, and `>= 13`. That makes the anonymous selector records the file-side mirror of
|
||||
the same campaign page split, not a separate unrelated control family. The file-order adjacency
|
||||
is tighter in the same way: `0xc352..0xc361` each sit immediately after one `RibbonN.imb`
|
||||
record and before the next `litCamp` record, which makes them the strongest current file-side fit
|
||||
for the structural selector or click-target siblings of the visible campaign ribbon controls.
|
||||
The auxiliary anonymous bands line up the same way: `0xc362..0xc367` sit inside the local
|
||||
`litArrows.imb` clusters, `0xc393..0xc396` sit inside the `litExits.imb` clusters, and
|
||||
`0xc39b` sits once between the first arrow and first exit group. So the current best read is
|
||||
that those anonymous records are the structural hitbox or action siblings of the visible arrow,
|
||||
exit, and selector art families, not an unrelated hidden control layer. The generic record
|
||||
cadence is tighter now too: in the current `Campaign.win` dump the ordinary anonymous structural
|
||||
records all span `0x0a7` bytes, while the named art-backed records cluster at `0x0b1`, `0x0b2`,
|
||||
and `0x0b3`. The only two visible outliers are the leading `0x0080c351` record at `0x0041`
|
||||
with span `0x0a3`, and the trailing `0x0002c392` record at `0x2fb9` with span `0x0b1`. That
|
||||
reinforces the current read that `0xc351` and `0xc392` are one-off structural controls flanking
|
||||
the main selector or page family rather than ordinary repeated art-backed records.
|
||||
The setup-launch corpus now tightens one corrective caveat there too: when the checked
|
||||
`Alternate USA`, `Southern Pacific`, and `Spanish Mainline` `.gmp/.gms` files are decoded with
|
||||
that same campaign-side interpretation, payload byte `+0x22` is always outside the known
|
||||
campaign progress range `0..15` (`0x53`, `0xeb`, `0x5b`, `0xae`, `0x21`, `0x7a`), so the
|
||||
ordinary validated setup lane is not simply staging a normal campaign-screen state. The paired
|
||||
token block `+0x23..+0x32` is still structurally compatible with the campaign selector band, but
|
||||
in the checked files it only populates a small recurring subset of the sixteen scenario lanes,
|
||||
chiefly `Go West!`, `Germantown`, `Central Pacific`, `Texas Tea`, and `War Effort`, with
|
||||
scenario-family-specific byte values rather than a simple `0/1` unlock bitmap. That makes the
|
||||
setup-side staging bridge look more like one shared destination layout being reused for a broader
|
||||
launch token family, not a proof that ordinary setup-launched `.gmp/.gms` content is already in
|
||||
campaign-screen form.
|
||||
Only `0x0e81` also sets `[0x006cec7c+0x82] = 1`, which is currently the strongest sandbox-side
|
||||
anchor beneath the later `.gmx` load family.
|
||||
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
|
||||
|
|
@ -793,6 +989,15 @@ transition.
|
|||
earlier hidden prepass is tighter now too: `0x00437743` is the scenario-side named
|
||||
candidate-availability seeding pass over the live candidate pool `0x0062b268`, feeding the
|
||||
override collection at `[state+0x66b2]` through `0x00434f20` before any visible banner is posted.
|
||||
That grounded write path is narrower than the static file evidence, though: the startup reset
|
||||
helper `world_runtime_reset_startup_dispatch_state_bands` `0x004336d0` explicitly clears
|
||||
`[state+0x66b2]` before the dispatch path begins, and the current exported write-side callers
|
||||
we can name after that reset are still just the live-pool preseed `0x00437743`, the sibling
|
||||
startup lane `0x00436ad7`, and the editor-side `Industry (Overall)` toggle handler
|
||||
`0x004cf430` through `0x00434f20`. So while the fixed `0x6a70..0x73c0` candidate-availability
|
||||
block is now well grounded as bundled map/save source data, a direct bulk-import path from that
|
||||
block into the live runtime collection `[state+0x66b2]` is still not grounded by the current
|
||||
disassembly notes.
|
||||
That candidate-side table now has a grounded fixed record layout too: each entry is a `0x22`-byte
|
||||
blob with a zero-terminated candidate-name slot at `[entry+0x00..+0x1d]` and one trailing
|
||||
availability dword at `[entry+0x1e]`, read through `0x00434ea0` and mirrored later into
|
||||
|
|
@ -1952,6 +2157,106 @@ transition.
|
|||
bytes `[world+0x66de]` and `[world+0x66f2]`, restores the selected year/profile lane through
|
||||
`[profile+0x77]` into `[world+0x05/+0x09/+0x15]` through
|
||||
`world_set_selected_year_and_refresh_calendar_presentation_state` `0x00409e80`; that restore now
|
||||
also has one concrete file-side correlation in the classic `.gms` family: local save inspection
|
||||
now consistently finds `0x32dc` at `0x76e8`, `0x3714` at `0x76ec`, and `0x3715` at `0x77f8` in
|
||||
`Autosave.gms`, `kk.gms`, and `hh.gms`, leaving one exact `0x108`-byte span from `0x76f0` to
|
||||
`0x77f8` between `0x3714` and `0x3715`. That span already carries staged-profile-looking payload
|
||||
text such as `British Isles.gmp`, so the current static-file evidence now supports the atlas-side
|
||||
`0x108` packed-profile note for the classic save family even though the exact field layout inside
|
||||
that block is still unresolved. The same classic corpus is tighter now too: inside that
|
||||
`0x108` span the map-path C string begins at relative offset `0x13`, the display-name C string
|
||||
begins at `0x46`, the block is otherwise almost entirely zeroed, and the three local samples are
|
||||
byte-identical except for the leading dword at `+0x00` (`3` in `Autosave.gms` and `hh.gms`,
|
||||
`5` in `kk.gms`). The currently atlas-tracked bytes `[profile+0x77]`, `[profile+0x82]`,
|
||||
`[profile+0x97]`, and `[profile+0xc5]` are all `0` in that classic sample set, so the current
|
||||
file-side evidence grounds the block boundaries and the embedded strings but does not yet show
|
||||
live examples of those branch-driving latches being set. One 1.05-era file-side analogue is now
|
||||
visible too, but only as an inference from repeated save structure rather than a disassembly-side
|
||||
field map: local `.gms` files in `rt3_105/Saved Games` carry one compact string-bearing block at
|
||||
`0x73c0` with the same broad shape as the classic profile slab, including a leading dword at
|
||||
`+0x00`, one map-path string at `+0x10`, one display-name string at `+0x43`, and a small
|
||||
nonzero tail around `+0x76..+0x88`. In that 1.05 corpus the analogue bytes at relative `+0x77`
|
||||
and `+0x82` are now nonzero in every checked sample (`Autosave.gms`/`nom.gms` show `0x07` and
|
||||
`0x4d`; `p.gms`/`q.gms` show `0x07` and `0x90`; `g.gms` shows `0x07` and `0xa3`), while
|
||||
relative `+0x97` and `+0xc5` remain `0`. The compared 1.05 save set is tighter now too:
|
||||
`Autosave.gms` and `nom.gms` cluster together on `Alternate USA.gmp` with `+0x82 = 0x4d`,
|
||||
`g.gms` carries `Spanish Mainline.gmp` with `+0x82 = 0xa3`, and `p.gms`/`q.gms` cluster on
|
||||
`Southern Pacific.gmp` with `+0x82 = 0x90`; across all five files the same inferred analogue
|
||||
lane at `+0x77` stays fixed at `0x07`, while the same map- or scenario-sensitive tail word at
|
||||
`+0x80` tracks those `0x4d/0xa3/0x90` byte lanes (`0x364d0000`, `0x29a30000`, `0x1b900000`).
|
||||
The leading dword at `+0x00` also splits the same corpus, with `Autosave.gms` alone at `3` and
|
||||
the other four checked 1.05 saves at `5`. That is enough to say the wider save corpus does
|
||||
contain nonzero candidates for two of the atlas-tracked profile lanes, and that one of them
|
||||
varies coherently with the loaded scenario family, but not yet enough to claim that the 1.05
|
||||
block reuses the exact same semantic field assignments as the classic one. The loader-side
|
||||
family split is tighter now too: `p.gms` and `q.gms` no longer live under a generic fallback;
|
||||
their save headers now classify as one explicit `rt3-105-scenario-save` branch with preamble
|
||||
words `0x00040001/0x00018000/0x00000746` and the early secondary window
|
||||
`0x00130000/0x86a00100/0x21000001/0xa0000100`, while `g.gms` now classifies as a second
|
||||
explicit `rt3-105-alt-save` branch with the different preamble lane
|
||||
`0x0001c001/.../0x00000754` and early window
|
||||
`0x00010000/0x49f00100/0x00000002/0xa0000000`. That branch now carries the same bootstrap,
|
||||
anchor-cycle, named 1.05 trailer, and narrow profile-block extraction path as the other 1.05
|
||||
saves. The bridge just below that trailer is now explicit too: the common 1.05 save branch
|
||||
carries selector/descriptor `0x7110 -> 0x7801` in `Autosave.gms` and `0x7110 -> 0x7401` in
|
||||
`nom.gms`, and both still reach the same first later candidate at
|
||||
`span_target + 0x189c`, well before the packed profile at `span_target + 0x3d48`; the
|
||||
`rt3-105-alt-save` branch instead carries `0x54cd -> 0x5901` and its first later candidate
|
||||
lands at `packed_profile + 0x104`, essentially on the profile tail; the scenario-save branch
|
||||
still diverges locally with `0x0001 -> 0x0186` and never enters that later `0x32c8`-spanned
|
||||
bridge at all. The common-branch bridge payload is narrower now too: both checked base saves
|
||||
expose the same 0x20-byte primary block at `0x4f14` followed by the same denser secondary block
|
||||
at `0x671c`, `0x1808` bytes later, and that secondary block now appears to run intact up to the
|
||||
packed-profile start at `0x73c0` for a total observed span of `0xca4` bytes. The trailing slice
|
||||
of that secondary block is now typed one level further: a small header at `secondary+0x354`
|
||||
carries the observed stride `0x22`, capacity `0x44`, and count `0x43`, followed by a fixed-width
|
||||
67-entry name table starting at `secondary+0x3b5` and running through names like
|
||||
`AluminumMill`, `AutoPlant`, `Bakery`, `Port00..11`, and `Warehouse00..11`, with a short footer
|
||||
(`dc3200001437000000`) after the last entry. The trailing per-entry word is now surfaced too:
|
||||
most entries carry `0x00000001`, while the currently observed zero-trailer subset is
|
||||
`Nuclear Power Plant`, `Recycling Plant`, and `Uranium Mine`. That footer is tighter now too:
|
||||
it parses directly as `0x32dc`, `0x3714`, and one trailing zero byte, so the shared
|
||||
map/save catalog currently ends on the same two grounded late-rehydrate progress ids that the
|
||||
classic staged-profile band already exposed. The strongest structural read is therefore that the
|
||||
entire `0x6a70..0x73c0` catalog region is shared verbatim between `Alternate USA.gmp` and the
|
||||
derived `Autosave.gms`, not rebuilt independently during save. Combined with the earlier
|
||||
grounded record-layout work under `0x00437743`, `0x00434ea0`, and `0x00434f20`, the current
|
||||
safest semantic read is that this shared catalog is the bundled source form of the scenario-side
|
||||
named candidate-availability table later mirrored into `[state+0x66b2]`, with each entry's
|
||||
trailing dword now reading as the same availability override bit later copied into
|
||||
`[candidate+0x7ac]`. The loader-side coverage is tighter now too: the same table parser now
|
||||
attaches both to the common-save bridge payload and directly to the fixed source range in
|
||||
`.gmp` files and the non-common `rt3-105-scenario-save` / `rt3-105-alt-save` branches. That
|
||||
makes the scenario variation explicit instead of anecdotal. `Alternate USA` keeps only three
|
||||
zero-availability names in this table (`Nuclear Power Plant`, `Recycling Plant`, `Uranium
|
||||
Mine`), `Southern Pacific` widens the zero set to twelve (`AutoPlant`, `Chemical Plant`,
|
||||
`Electric Plant`, `Farm Rubber`, `FarmRice`, `FarmSugar`, `Nuclear Power Plant`, `Plastics
|
||||
Factory`, `Recycling Plant`, `Tire Factory`, `Toy Factory`, `Uranium Mine`), and `Spanish
|
||||
Mainline` widens it again to forty-two, including `Bauxite Mine`, `Logging Camp`, `Oil Well`,
|
||||
`Port00`, and the `Warehouse00..11` run while also flipping `Recycling Plant` back to
|
||||
available. The header lanes just ahead of the table vary coherently with those scenario
|
||||
branches too: `Alternate USA` carries `header_word_0 = 0x10000000`, `Southern Pacific`
|
||||
carries `0x00000000`, and `Spanish Mainline` carries `0xcdcdcdcd`, while the structural
|
||||
fields from `header_word_2` onward remain stable (`0x332e`, `0x1`, `0x22`, `0x44`, `0x43`)
|
||||
and the 9-byte footer still decodes as `0x32dc`, `0x3714`, `0x00` in all three checked maps.
|
||||
A wider corpus scan over the visible `.gmp`/`.gms` files makes those two anonymous header
|
||||
lanes less mysterious too: the parser currently sees only three stable
|
||||
`(header_word_0, header_word_1)` pairs across 79 files with this table shape, namely
|
||||
`(0x00000000, 0x00000000)`, `(0x10000000, 0x00009000)`, and
|
||||
`(0xcdcdcdcd, 0xcdcdcdcd)`. The zero-availability count varies widely underneath the first and
|
||||
third pairs (`0..56` under the zero pair, `14..67` under the `0xcdcdcdcd` pair), so those two
|
||||
lanes no longer look like counts or direct availability payload; the safest current read is
|
||||
that they are coarse scenario-family or source-template markers above the stable
|
||||
`0x332e/0x22/0x44/0x43` table header, with `0xcdcdcdcd` still plausibly acting as one reused
|
||||
filler or sentinel lane rather than a meaningful numeric threshold. Current exported
|
||||
disassembly notes still do not ground one direct loader-side or editor-side consumer of
|
||||
`header_word_0` or `header_word_1` themselves, so that family-marker read remains an
|
||||
inference from corpus structure rather than a named field assignment.
|
||||
The new loader-side compare command makes the save-copy claim sharper too: for the checked
|
||||
pairs `Alternate USA.gmp -> Autosave.gms`, `Southern Pacific.gmp -> p.gms`, and
|
||||
`Spanish Mainline.gmp -> g.gms`, the parsed candidate-availability table contents now match
|
||||
exactly entry-for-entry, with the only reported differences being the outer container family
|
||||
(`map` vs `save`) and source-kind path (`map-fixed-catalog-range` vs the save-side branch).
|
||||
has the explicit companion `world_refresh_selected_year_bucket_scalar_band` `0x00433bd0`, which
|
||||
rebuilds the dependent selected-year bucket floats after the packed year changes; and then
|
||||
rehydrates the named locomotive availability collection at `[world+0x66b6]` through
|
||||
|
|
@ -2407,7 +2712,57 @@ transition.
|
|||
loop; inside sandbox it raises localized id `3899` `The ledger is not available in sandbox mode.`
|
||||
instead. That narrows the remaining `LoadScreen.win` gap again: `TRAIN DETAIL` and
|
||||
`STATION DETAIL` now read as dormant title-table entries unless some still-unrecovered nonstandard
|
||||
selector reaches them.
|
||||
selector reaches them. The live auto-load boundary is tighter now too: hook-driven
|
||||
`shell_transition_mode(4, 0)` now completes old-mode teardown, reconstructs and republishes
|
||||
`LoadScreen.win`, and returns cleanly, but the later post-transition service ticks still keep
|
||||
`[0x006cec78] = 0`, `[shell_state+0x0c]` on the same `LoadScreen.win` singleton, and
|
||||
`[LoadScreen.win+0x78] = 0` through at least counts `2..8`. So the next runtime edge is no
|
||||
longer the old mode-`4` teardown or publish band; it is the `LoadScreen.win` message owner
|
||||
`0x004e3a80` itself, because later service alone is not promoting the plain load screen into the
|
||||
separate startup-runtime object path. One later runtime probe did not actually reach that edge:
|
||||
the `0x004e3a80` hook installed, but the run never produced any ready-count, staging,
|
||||
transition, post-transition, or load-screen-message lines, only ordinary shell node-vcall
|
||||
traffic. So that result is now treated as a gate-or-cadence miss rather than as evidence against
|
||||
the `LoadScreen.win` message path itself. The newer shell-state service trace tightens it again:
|
||||
on a successful staged run the later service ticks do execute in `mode_id = 4`, but the
|
||||
`0x004e3a80` message hook still stays silent while the state remains frozen in the plain
|
||||
`LoadScreen.win` shape. So the next runtime boundary is now the shell-runtime prime call
|
||||
`0x00538b60` beneath `shell_state_service_active_mode_frame` `0x00482160`, not the message owner
|
||||
alone. The first direct `0x00538b60` probe run is not trustworthy yet, though: it stopped
|
||||
immediately after the first shell-state service-entry line, before any ready-count or
|
||||
runtime-prime entry logs. So that result is currently treated as probe validation work, not as a
|
||||
real boundary move inside RT3. The static service branch is conditional too: `0x00482160` only
|
||||
enters `0x00538b60` when `[shell_state+0xa0] == 0`, so silence from the runtime-prime hook does
|
||||
not yet prove the shell stops before that call unless the service-entry logs also show the `+0xa0`
|
||||
gate open. The newer run now closes that condition: `[shell_state+0xa0]` is `0`, and the
|
||||
`0x00538b60` runtime-prime hook enters and returns cleanly. The newer run closes the next owner
|
||||
too: `0x00520620` `shell_service_frame_cycle` also enters and returns cleanly on the same frozen
|
||||
mode-`4` path, and the logged fields match its generic branch rather than a startup-promotion
|
||||
lane (`[+0x1c] = 0`, `[+0x28] = 0`, `flag_56 = 0`, `[+0x58]` pulsed then cleared, and
|
||||
`0x006cec78` stayed `0`). So the next runtime boundary under the same shell-state service pass is
|
||||
now one level deeper: the per-object service walker `0x0053fda0` beneath `0x00538b60`. The newer
|
||||
run closes that owner too: it enters and returns cleanly while servicing the `LoadScreen.win`
|
||||
object itself, with `field_1d = 1`, `field_5c = 1`, and a stable child list under
|
||||
`[obj+0x70/+0x74]`, and its first child-service vcall target at slot `+0x18` stays
|
||||
`0x005595d0`. Since `0x006cec78` still stays `0` through those clean object-service passes, the
|
||||
next runtime boundary is now the child-service target `0x005595d0`, not the higher object
|
||||
walker. The newer child-service run narrows that again: the first sixteen `0x005595d0` calls are
|
||||
stable child lanes under the same `LoadScreen.win` parent, with `[child+0x86]` pointing back to
|
||||
the load-screen object, `field_b0 = 0`, and a split where earlier children carry
|
||||
`flag_68 = 0x03` and return `4` while later siblings carry `flag_68 = 0x00` and return `0`. The
|
||||
static body matches that read too: `0x005595d0` is gated by `0x00558670` and then spends most of
|
||||
its work in draw or overlay helpers `0x54f710`, `0x54f9f0`, `0x54fdd0`, `0x53de00`, and
|
||||
`0x552560`, so it now reads as another presentation-side service lane rather than the missing
|
||||
startup-runtime promotion. The widened allocator-window trace then reconciled the runtime with
|
||||
the static mode-`4` branch one step further: the first transition-window allocation is `0x7c`,
|
||||
which matches the static pre-construct `0x48302a -> 0x53b070` alloc exactly, and the later
|
||||
`0x111/0x84/0x3a/0x25...` allocations all occur before `LoadScreen.win` construct returns, so
|
||||
they now read as constructor-side child or control setup. That means the allocator probe did not
|
||||
disprove the still-silent startup-runtime slice; it simply exhausted its log budget inside the
|
||||
constructor before the post-construct block. The later reset-at-return run is now the decisive
|
||||
one: after `LoadScreen.win` construct returns there are still no further allocator hits before
|
||||
publish and transition return, which matches the corrected jump-table decode because mode `4`
|
||||
does not own the `0x46c40 -> 0x4336d0 -> 0x438890` startup-runtime path.
|
||||
- Editor breadth: the broader map-editor page owner is now bounded through
|
||||
`map_editor_panel_select_active_section` `0x004ce070` and
|
||||
`map_editor_panel_dispatch_active_section_message` `0x004cf700`, which switch among the grounded
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue