UM20147 holds a 72,560-byte event. chunk_params() wrote the offset as a uint16
at params[2:4], because every offset THOR was observed to send fits in two bytes
(largest 0x3400 = 13,312), which caps a download at 65,536 B -- so that event
could not have been fetched at all.
params[0:4] is demonstrably ONE 4-byte field: chunk 0 puts the 4-byte event key
there. Writing the offset as a uint32 BE in the same slot is BYTE-IDENTICAL for
every offset below 65,536, so nothing verified against THOR's 74 captured frames
changes -- the replay test still matches all of them -- and the range extends to
4 GB.
Above 64 KB this is inference and the docstring says so: the field's width is
established, the device's handling of a non-zero high byte is not.
Also newly reachable: at offset 1 MiB params[1] is 0x10, which must go out as
`10 10`. Nothing below 64 KB can produce that, so the uint32 change is what
first makes the case possible -- and an unescaped 0x10 in 5A params is the exact
bug that cost the Series III walk a release. Tested.
THIS IS THE SERIES III 64 KB PAGE-BOUNDARY BUG WEARING A DIFFERENT HAT. There,
parse_strt_end_offset() discards the key's page byte and the walk crashes once a
unit's buffer crosses 64 KB; that one is still open. The transferable lesson:
an address field whose high bytes are zero in every capture is not a narrow
field, it is an untested one. Same mistake, found twice, in code written years
apart.
Confirmed in the same run: 0x06 content[0:4] IS the event count -- UM20147 holds
5 events and reads 5, making it three for three across both firmware lines
(0->0, 6->6, 5->5). And content[4:8] is a CONSTANT, not a count: it reads 9 on
a unit with 6 events and on a unit with 5. list_events() still walks to the
sentinel; the count is worth adopting as a pre-check, not a replacement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL