analyse-pcap: count UDP, because ROM streaming is UDP/FSP not TCP
The 850 KB/s dump measurement does not bound ROM streaming, and the tool could not have measured streaming at all: it only counted TCP payload, so a streaming capture would have reported ~zero throughput. Swiss's ROM streaming runs in the game via the patch driver's own stack (cube/patches/bba/tcpip.c), which contains no TCP whatsoever -- UDP + FSP, plus ICMP and ARP. Reads are sized to the path MTU rather than 1024: _fsp.data_length = MIN(length, env->pmtu - (fsp->data - eth->data)); so ~1446 B of payload per datagram at a 1500 MTU, with QUEUE_SIZE 2 outstanding and udp->checksum = 0x0000 (no payload checksum). None of the menu path's TCP window, lwIP overhead or 1024-byte writes apply. The analyser now counts UDP payload and picks the bulk direction by which way the payload actually flows -- streaming is PC -> GC, the opposite of a disc dump -- with --bulk to_gc to steer the gap analysis. Self-test H replays the streaming shape (1446 B datagrams, 2 per request) and checks the bulk direction is identified and the payload counted. Recorded in the TODO that "can it stream?" is still OPEN, but open on the GC's side: the dump established the adapter has headroom (zero retransmissions, ~17% W5100 bus utilisation, 26% of the EXI ceiling). The hard ceiling is 3375 KB/s and the GC's disc delivers ~2000-3100 KB/s, so full disc rate has no margin -- but games read in bursts, not continuously at peak. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,40 @@ For a **window-bound** flow they are not: delaying the ACK's interrupt
|
||||
lengthens the round trip, and throughput is bytes-per-round-trip ÷ RTT. The
|
||||
measurement is what showed this — the guess was backwards.
|
||||
|
||||
### ⚠️ The 850 KB/s figure does NOT bound ROM streaming — different stack
|
||||
|
||||
The dump was Swiss's **menu** path: libogc2 + lwIP + TCP, 1024-byte
|
||||
application writes, a 2920-byte window. **ROM streaming uses a completely
|
||||
different code path** — Swiss's in-game patch driver and its own stack in
|
||||
`cube/patches/bba/tcpip.c`, which has **no TCP in it at all**:
|
||||
|
||||
- **UDP + FSP** (File Service Protocol), with ICMP and ARP. No TCP, no lwIP,
|
||||
no congestion control, no window.
|
||||
- Reads are sized to the path MTU, not 1024:
|
||||
`_fsp.data_length = MIN(length, env->pmtu - (fsp->data - eth->data))`
|
||||
— about 1446 B of payload per datagram at a 1500 MTU.
|
||||
- `QUEUE_SIZE 2` requests outstanding.
|
||||
- `udp->checksum = 0x0000` — no payload checksum, which is real CPU saved
|
||||
versus TCP.
|
||||
- It runs in the game's context via the patch driver, which uses the leaner
|
||||
4-byte command-space form (see the command-space note in CLAUDE.md).
|
||||
|
||||
So the TCP window × RTT limit is not the operative one there, and the measured
|
||||
850 KB/s says nothing about it. What the dump DID establish, and which carries
|
||||
over, is that the adapter has headroom: **zero retransmissions**, ~17% W5100
|
||||
bus utilisation, 26% of the EXI ceiling.
|
||||
|
||||
**Not yet measured.** `analyse-pcap.py` now counts UDP payload and picks the
|
||||
bulk direction automatically (streaming flows PC → GC, the opposite of a
|
||||
dump), so `--bulk to_gc` on a streaming capture gives the real number. Until
|
||||
that is run, "can it stream?" is open — but it is open on the GC's side, not
|
||||
on the adapter's.
|
||||
|
||||
The hard ceiling remains **3375 KB/s** (27 MHz × 1 bit/clock), and the GC's
|
||||
own disc delivers roughly 2000–3100 KB/s, so there is no headroom to spare if
|
||||
a game wants full disc rate — but games read in bursts, not continuously at
|
||||
peak.
|
||||
|
||||
## 3. `NCRB` (0x01) is not decoded at all
|
||||
|
||||
**Status: ✅ DECODED 2026-10-10** — stored, readable, and exported as
|
||||
|
||||
Reference in New Issue
Block a user