Let’s Go Pikachu and Eevee
In Pokemon Let’s Go Pikachu and Let’s Go Eevee (2018), Pia is statically linked into main (269 nn::pia classes in the RTTI) and the game’s C++ code sits on it with protocol-buffer messages through gflnet3, the middleware Sword and Shield use a year later.
The static reading is Let’s Go Pikachu 1.0.2 (010003f003a34000, update NSP v131072, SDK 5.4.151.0); hardware measurements are against a French Let’s Go Pikachu and a Let’s Go Eevee. Let’s Go Eevee uses Pikachu’s local communication id, so both roles trade with an Eevee unchanged.
Local trading and battling ask both players for a link code: three Pokemon in order from a fixed ten, shown in two rows: Pikachu, Eevee, Bulbasaur, Charmander, Squirtle; Pidgey, Caterpie, Rattata, Jigglypuff, Diglett. The code sets the advertisement’s scene id (the session page); hosting and joining both work under any code.
Pages
| page | contents |
|---|---|
| The Let’s Go cartridge and session | what the title is built from, the LDN passphrase and Pia game key, Pia header version 3, the session key, the station and clone protocols as a real session runs them, the gate that starts the game’s own messages, and the four game messages of a trade |
What works
bin/lgpe_join.py joins a console’s session and bin/lgpe_host.py hosts one the console joins. A seat carries one trade after another: after each trade’s normal save the trade dispatcher goes from state 7 back to state 1 unless the save reports that the link ends (0x886908), and the save re-creates the party-offer object on a new channel (0x8375a4) (the session page). Both launchers take a queue of records, one per trade on the seat. The game checks no field of a received box structure: a shiny level-100 Imposter Ditto with 31 in every IV and 200 in every AV reads back on the summary screen. The Clone Protocol’s take-over exchange passes the game’s 0x11b080 gate, and the Reliable Protocol carries identity, offer, commit and kind 4 (the next trade’s selection); pokeldn.lgpe.pb7 reads and writes the 232-byte box structure the offer and kind 4 carry.
Three queued trades complete on one seat in both roles. Each completed entry is saved as a checksummed 260-byte file, and the host exits with code 0 after the console leaves. A record built by pokeldn.pokemon (level, gender, nature, ball, IVs and AVs chosen) and offered by bin/lgpe_host.py --fresh-pid shows those fields on the receiving summary screen.
A console leaves the seat when its player presses Retour. bin/lgpe_join.py --leave-after SECONDS runs the same exit that long after its first answered trade step, and bin/lgpe_host.py answers a console’s Retour (A joiner leaving). tests/test_lgpe_host_commit.py pins the host’s commit stage against a scripted console: a wrong commit leaves the console on its confirmation screen with the save’s trade lock set (no trades for ten minutes of counted play time), then the fatal error screen.
Unresolved
- What left a retail console silent on both of the host’s
0xa1after a host answered its withdrawn vote with A 2. The host’s publishes: A 2 with trailing word 1, then A 2 with trailing word 2 one second later; the console republished0 2 3under trailing word 2 and announced its commit clone 5.0 s after the first A 2. The host answered the announcement with82, 91, 91, 84, 81, a1, a1, seven datagrams within 1 ms, all acknowledged by the console’s radio. The console re-announced the clone on clone type 1 under a new clock, which only the0x81on clone type 2 does (0x51ca48->0x520c30, after the0x91on clone type 2 unlinked the announcement at0x520d30; the0x82on clone type 1 reaches only0x5223c0and sends nothing). It answered neither0xa1. The clone type 10xa1handler (0x51c714,0x522350) sends on every outcome and0x51e3d0spends a message count before it can fail; the console spent none. Every gate before the handlers (the receive loop’s station mask and frame-counter filter0x51ae20..0x51ae84, the destination check0x51ce80, the count filter0x51c1e0, the+0x3cmask0x51c6b0) passed for the0x81with the same sender, frame counter and mask.0x522a60returning 2 would silence the clone type 40xa1alone. An emulated Let’s Go 1.0.2 given the same inputs answered both0xa1with0xa2within 30 ms and completed the trade: it withdrew its vote (2 2 3) 0.81 s after its own1 2 2, the host published A 2 with trailing word 1 0.16 s later and trailing word 2 1.0 s after that, the console republished0 2 3under trailing word 2, announced its commit clone 5.4 s after the first A 2 and drew the same seven-message burst. The retail console runs 1.0.2 as well; what differs on retail is unknown.