GTS protocol
The following is wild conjecture based on LordLandon's sendpkm.py.
Communication with the GTS is done over regular HTTP with http://gamestats2.gs.nintendowifi.net/. The same protocol is used for all five Gen IV games.
HTTP headers
The games don't seem to care about these at all. The GTS sends back a bunch of boilerplate response headers, but the game happily accepts a response with only a Content-Length.
Protocol
All requests to the server are GET requests of the form page.asp?pid=pid&hash=hash&data=data
.
pid
The pid is an unsigned 32-bit integer that appears to uniquely identify a game cartridge. When the pid is obtained and whether the pid has any relation to the Pal Pad friend code are unknown.
For the mathematically inclined: Eevee's Platinum pid is 192615460 (0x0b7b1424) and his Pal Pad code is 0904 2026 4621.
Challenge/response
Before each "real" request, the game sends a request of the form page.asp?pid=pid
and the server responds with a 32-byte challenge token. The game computes sha1("sAdeqWo3voLeC5r16DYv" + token)
and uses that as the hash value which it sends to the server. The data parameter is encrypted, as explained further down.
That is, each request looks like the following:
- Game requests
GET /pokemondpds/page.asp?pid=pid
- Server responds with token
- Game requests
GET /pokemondpds/page.asp?pid=pid&hash=sha1(...)&data=data
- Server responds with payload
The exact details of the game's requests are not currently known, but we are working on it!
Checksum
A 32-bit checksum of the data is used as the key for the encryption. The checksum is simply the sum of every byte of the data.
The first 4 bytes of the sent data are the checksum, xor
ed with 0x4a3b2c1d
, and sent in network byte order (big-endian).
Encryption
The data sent to the server is encrypted with a stream cipher, and urlsafe-base64-encoded.
This encryption algorithm, like others used in the game, uses a Linear Congruential Generator (not a very strong choice). The multiplicative constant is 0x45
, and the additive constant is 0x1111
. It appears that the game uses a signed dword to store the seed, which doesn't really matter; it shouldn't affect anything. (It means that the modulus is effectively 0x80000000
.)
GRNG[n+1] = (GRNG[n] * 0x45 + 0x1111) & 0x7fffffff
The GRNG is seeded with the checksum, like so:
GRNG[0] = checksum | (checksum << 16)
The keystream is composed of the lower byte of the high word of successive GRNG values.
keystream[n] = (GRNG[n] >> 16) & 0xff
Xor
the keystream with the plaintext to get the ciphertext. Xor
the keystream with the ciphertext to get the plaintext.
Conversation
The first request the game makes is to /pokemondpds/worldexchange/info.asp
. The server responds with 0x0001.
Platinum, Heart Gold, and Soul Silver will then make a request to /pokemondpds/common/setProfile.asp
. The server responds with eight NULs (0x00000000 0x00000000).
After the above step(s) or performing any of the tasks below other than searching, the game makes a request to /pokemondpds/worldexchange/result.asp
. If the game has had a Pokémon sent to it (via a successful trade?), the server responds with the entire encrypted Pokémon save struct. If there is a Pokémon deposited in the GTS, it responds with 0x0004. Otherwise, it responds with 0x0005.
Receiving a Pokémon
If the game receives a Pokémon from a successful trade as a response from result.asp
, it next requests /pokemondpds/worldexchange/delete.asp
. The server responds with 0x0001.
A note on sendpkm.py
After doing the above, some Platinum, Heart Gold, and Soul Silver games will report a communication error and dump the player back to the title screen. The Pokémon is still successfully received. At least one person with HG/SS has received a Pokémon from a fake server without getting the error, and Diamond/Pearl have never been reported to have the problem. It's possible that the server should respond with something other than 0x0001.
Depositing a Pokémon
Pokémon are offered on the GTS by requesting /pokemondpds/worldexchange/post.asp
. The sent data is 300 bytes long, and includes the Pokémon struct. If the Pokémon is rejected by the server, the response is 0x000c; otherwise, if the deposit is successful, 0x0001.
The game then saves. After the save is complete, it issues a request to /pokemondpds/worldexchange/post_finish.asp
.
Retrieving the deposited Pokémon
Checking on the deposited Pokémon is apparently done by /pokemondpds/worldexchange/get.asp
. The response appears to be a Pokémon save struct.
Retrieving the deposited Pokémon is done by /pokemondpds/worldexchange/return.asp
. The response is merely 0x0001; the actual Pokémon data is taken from the get.asp
request.
Searching
Searching is done through /pokemondpds/worldexchange/search.asp
. The sent data is either 15 or 16 bytes long.
The server responds with a full 292-byte Pokémon struct for each result. If there are n results, the response will be 292 * n bytes long. If there are no results, the server will give an empty response (0 bytes).
Pokémon struct
The Pokémon data for the GTS is 292 bytes—56 bytes larger than a party Pokémon struct. The extra 56 bytes are GTS-specific data, such as the player's name & country, and what Pokémon they are requesting.
They are as follows:
0x00-0x01 Current Pokémon 0x02 Gender 1=male; 2=female; 3=either/neither 0x03 Level 0x04-0x05 Requested Pokémon 0x06 Gender 0x07 Min level 0x08 Max level 0x09 always 0? 0x0A Trainer Gender 0=male; 1=female 0x0B always 0? 0x0C-0x13 Timestamp - Deposited time 0x14-0x1B Timestamp - Time traded away? 0x1c-0x1F pid - also 0x4c in the sav 0x20-0x2F Trainer Name 0x30-0x31 Trainer ID 0x32 Country 0x33 City 0x34 Trainer Sprite 0x35 Exchanged flag 0x36 Version 0x0A=Diamond; 0x0B=Pearl; ... 0x37 Language 1=jp; 2=en; 3=fr; ...
The timestamps are set by the server, and are always PST (UTC-8). Timestamp format:
0x00-0x01 Year 0x02 Month 0x03 Day 0x04 Hour 0x05 Minute 0x06 Second 0x07 always 0?