Pokemon GTS Protocol

This is basically going to be an extension of this article -> http://projectpokemon.org/wiki/GTS_protocol - I just wanted to make a few notes for my own convinience. I intend to write a basic GTS query tool so that I don't need to log on in-game just to see if a certain pokemon is available.

A lot of this is going to assume you know how a request works (send pid, receive token, send sha1(salt+token) as hash with pid and data, receive result). See the article above for details.

Searching

A basic search of the GTS goes sort of along these lines:

GET .../worldexchange/search.asp
data is as follows:
  NOTE: All data is little endian
  pid              - 4bytes
  species          - 2bytes (0x0001 - 0x01ED/1-493)
  gender           - 1byte (1=male,2=female,3=either)
  level range from - 1byte
  level range to   - 1byte
  location?        - 1byte (usually 0x00)
  max no.results   -1byte (0x01-0x07/1-7)

The result of .../worldexchange/search.asp will be either empty if there were no results, or a number of 292byte structures each containing a standard 236 byte party-style pokemon structure (ie: 136 bytes of standard pkmn data, 100 bytes of pre-calculated stats), followed by 56bytes of GTS-specific data containing basic information displayed when searching in the game. See the above article for more details.

Reading Currently Deposited Pokemon

This works pretty much exactly the same way as searching, except the page to query is .../worldexchange/get.asp, and you only need to supply the PID in the encrypted data. The result is a single 292 byte pokemon as with the above. If there is no pokemon deposited there will be no data returned (I believe).

Example

I have a couple of working examples of this.

GenIV PRNG PHP Implimentation

PID from Friend Code

As it turns out, the PID used in the GTS transactions is part of the 12 digit Friend Code issued when you first connect to Pokemon's Wifi Club services. The Friend Code is actually the PID of the individual game card with a random byte added to it.

To calculate the PID from a Friend Code, simple take the 12 digit number then just bitwise AND it with 0x7FFFFFFF.

$pid = $friendcode & 0x7FFFFFFF;

Obviously as this is above the upper bound of signed 32-bit integers, PHP falls over on this too. To compensate, I've used the same BCHEXDEC function as my PRNG implimentation:

function FCtoPID($fca,$fcb,$fcc) {
  $fc = bcdechex("$fca$fcb$fcc");
  $pid = hexdec("0x$fc") & 0x7FFFFFFF;
  return $pid;
}

Example GTS Transaction

Just thought I'd include an example of how a simple search would work:

I'm going to use a PID of 12345 for this example.

So first we ask the GTS for a token:

>> .../worldexchange/search.asp?pid=12345

The server should respond with a 32 character token

<< 1234567890ABCDEF1234567890ABCDEF

We just need to take this and run this, prepended with 'sAdeqWo3voLeC5r16DYv',
through SHA1 to generate a hash, which we'll include in our search request:

  667b78b65cdc3660f55a3930014a3d7ff0fba414

Now I'm going to search for a male Bulbasaur, lvl 1-10, so I've got 
to pack together the following bits of data:

  My PID - 32-bit
  Pokemon species, 16-bit
  Gender - 8-bit
  Min Level - 8-bit
  Max Level - 8-bit
  0 - 8-bit (don't know what this is actually)
  Number of results (1-7) 8-bit
  Country 8-bit
  
Since I'm only concerned with Species, Gender and level, I'm ignoring Country.
Anything larger than a byte has to be packed in little-endian byte order, so
my search query will look something like this:

  0x39 0x30 0x00 0x00 <- My PID (which in hex is 0x00003039)
  0x01 0x00 0x01 0x01 <- Species (0x0001), Gender (0x01 - male), Min lvl (0x01)
  0x0A 0x00 0x07 0x00 <- Max lvl (0x0A), Unknown (0x00), results (0x07) and 
                         since I'm not specifying country, blank (0x00)

or

  39300000010001010A000700

Obviously we don't just send it like this because the GTS likes it well and truly 
obfuscated.  First, we need to encrypt it:

  88F27DDDAF187D875AD2BBD2 

Now prepend it with the checksum XOR'd with 0x4A3B2C1D and Base64 encode it, 
replacing any dashes with underscores and plus signs with forward slashes in the 
results.  This just makes it safer to send via URL.

  CheckSum of 125 XOR'd with 0x4A3B2C1D, EncryptedData
  ->
  Base64 URL Encode (  4A3B2C60 + 88F27DDDAF187D875AD2BBD2  )
  ->
  SjssYIjyfd2vGH2HWtK70g==

and replace characters:

  SjssYIjyfd2vGH2HWtK70g== (no characters to replace this time)

So now we have all the information we need to request a search from the GTS.

>> ../worldexchange/search.asp?pid=12345
  &hash=667b78b65cdc3660f55a3930014a3d7ff0fba414
  &data=SjssYIjyfd2vGH2HWtK70g==

   (split onto several lines for clarity)

If all goes according to plan, The server should respond with N x 292 bytes 
where N is the number of results specified/available.  If it responds with 0 bytes
there are no results.  If it responds with a HTTP error code something went wrong. 

I hope this helped clear up any confusion of what happens when exactly.

Generation V

The method of searching in Black and White is more-or-less the same, with the following differences:

  • The directory is now /syachi2ds/web/ rather than /pokemondpds/
  • The salt used to create the hash is now HZEdGCzcGGLvguqUEKQN rather than sAdeqWo3voLeC5r16DYv
  • The search data is no longer ecrypted when sending to the server
  • The checksum is now XOR'd with 0x2db842b2 rather than 0x4a3b2c1d
  • Data is now in 296 byte structures, with the end 58 bytes relating to the GTS search
  • The Trainer Name is now in Unicode rather than using a pokemon-specific character lookup table
  • The structure of the GTS data is slightly different, both sending and receiving:
    • Sending: An additional 32bit integer is send after the PID and before requested species with a value of 8 - this is actually the length of the following data but will always be 8 when performing a search
    • Receiving: byte 0x20-0x33 container Trainer ID (16 bit), Secret ID (16 bit) and Trainer Name (16 bytes) rather than 0x20-0x31 containing Trainer Name (16 bytes) and Trainer ID (16 bit) - this bumps everything following forward 2 bytes
Last updated 02:27pm 12/04/2012 by User
blog comments powered by Disqus