Hacking - DS Generation: Nintendo Wi-Fi Connection

What follows is my attempt to document the Nintendo Wi-Fi Connection functionality of Pokémon Pearl, which uses Nitro SDK 1.2. Diamond very probably works the same way. It is less obvious whether Platinum, HeartGold, and SoulSilver follow the same protocol. This protocol is different for Generation V (not only are the servers different, but the GameSpy login protocol has changed).

Much of what is in this document depends on previous work by others on decoding the GameSpy protocol, including Wiimm's work on the Mario Kart Wii protocol, teknogods's partial implementation of the GameSpy protocol, and Vetle's work on workaround for, and partial documentation of, the SSL communications with nas.nintendowifi.net.

Note that the following documentation is necessarily incomplete and is based on only partial understanding of the protocols involved, as it is based on observing network traffic originating from the DS and behavior observed when attempting to re-implement the following behaviors. As the protocols depend on two GameSpy protocols, it is possible that work done by others on GameSpy protocols (e.g. that done by Luigi Auriemma) may offer additional insights into the GameSpy protocols and how the Nintendo Wi-Fi Connection servers might work beyond those offered here (e.g. with respect to NAT negotiation), although it is equally possible that such work may be misleading. What I attempt to document here is primarily with respect to the Nintendo Wi-Fi Connection, and not other implementations of the GameSpy protocols and is based solely on the traffic I have observed.

Due to lack of time and effort (as well as the vast variety of possible fail states), I cannot document every possible server-side error state here, but instead focus on successful interactions in the protocol. To the extent that I have encountered error states during work related to the creation of this document, I have listed them. However, I cannot safely state that anywhere close to every error state is listed here. However, it seems plausible to assume that the Pokémon games themselves are "perfect" implementations, so error states are unlikely to be relevant except in so far as a "perfect" server implementation is desirable to help debug novel clients other than the games themselves.

Finally, I would like to note that the example packets (particularly the PCAP traces) have been modified to remove identifying MAC addresses and IP addresses in the data. If you have reason to believe that external IP addresses or MAC addresses are relevant to aspects of the protocol I have presented here, please e-mail me at (my domain) at (my domain).com for the unmodified traces. Also, please note that the DNS queries and HTTPS connection have been made through a proxy host (10.0.1.4) executing a man-in-the-middle attack to obtain the SSL session keys the Nintendo DS (as bridged through the packet capturing interface 10.0.1.6) is using.

TODO: Document Platinum, HeartGold/SoulSilver, Black/White, Black 2/White 2

Table of Contents

General Notes

In all cases, the Nintendo DS performs a DNS lookup of all domains before connecting to them. These queries are typical DNS lookups of the A record, and can be overridden with your hosts file if you are sharing your computer’s wi-fi (for example).

Friend Codes

Friend codes of DS games are 48-bit integers. The lowest 32 bits of a friend code indicate the user's profile ID on the GameSpy service. The highest 8 bits are a checksum of the profile ID combined with the 3-character prefix of the game cart code (Pokémon Pearl and Diamond share a prefix: ADA). This checksum algorithm is fully described by CaitSith2's PHP implementation of the verification algorithm (mirror).

SSL Certificate Validation and Workarounds

All network access uses GameSpy to manage accounts. These accounts are created and managed by the HTTP server nas.nintendowifi.net. Pokémon will not connect to nas.nintendowifi.net without a valid server-side SSL certificate. Such a certificate must be issued by a certificate authority identified as C=US, ST=Washington, O=Nintendo of America Inc, OU=NOA, CN=Nintendo CA/emailAddress=ca@noa.nintendo.com (for American carts at least) and the certificate must be signed by the private keys corresponding to the following public key included in the ROM:

B3 CD 79 97 77 5D 8A AF 86 A8 E8 D7 73 1C 77 DF
10 90 1F 81 F8 41 9E 21 55 DF BC FC 63 FB 19 43
F1 F6 C4 72 42 49 BD AD 44 68 4E F3 DA 1D E6 4D
D8 F9 59 88 DC AE 3E 9B 38 09 CA 7F FF DC 24 A2
44 78 78 49 93 D4 84 40 10 B8 EC 3E DB 2D 93 C8
11 C8 FD 78 2D 61 AD 31 AE 86 26 B0 FD 5A 3F A1
3D BF E2 4B 49 EC CE 66 98 58 26 12 C0 FB F4 77
65 1B EA FB CB 7F E0 8C CB 02 A3 4E 5E 8C EA 9B

Pokémon verifies the server's certificate using this public key and will fail if the server’s certificate is not signed with it. The certificate currently presented by nas.nintendowifi.net may be found here. It is unlikely that the server certificate itself must have the subject C=US, ST=Washington, O=Nintendo of America Inc., OU=Nintendo Wifi Network, CN=nas.nintendowifi.net.

Several workarounds exist to avoid the SSL issue, however all workable solutions require modification of the ROM:

  1. Create a new copy of the NOA root certificate (using 1024-bit RSA keys) and replace the two copies of the above public key in the ROM with the newly generated public key. Then, create a server certificate, signed by your new root certificate to use for hosting your copy of nas.nintendowifi.net.
  2. All copies of the string https://nas.nintendowifi.net/ac may be replaced with the string http://nas.nintendowifi.net/ac\x00 (i.e. the same string with "https" substituted with "http", and a null byte added at the end of the string to ensure that the offsets of other strings are not changed). This change (surprisingly) results in Pokémon contacting the server in cleartext over HTTP instead of encrypted over HTTPS.

GameSpy UDP Protocol

Much of this section is an interpretation of code written by teknogods to implement the GameSpy master servers.

At several places in its Wi-Fi communications, Pokémon uses the GameSpy UDP protocol. This protocol appears to be a variant of the GameSpy Query Protocol in which the roles of client and server are reversed (i.e. the master server to which the DS connects acts as a passive "client" in the query). This protocol, which uses UDP port 27900, appears to consist of two distinct UDP datagrams, depending on whether the data is sent by the client (DS) or the server (in the gs.nintendowifi.net domain):

struct gamespy_client_datagram {
    char message_type;                       // See Message Types, below.
    long client_id;                          // An ID provided by the client.
                                             // It is unknown if this is unique per user or per session.
    struct gamespy_client_payload payload[]; // The payload of the datagram.
};

struct gamespy_server_datagram {
    short ack;                            // Always 0xFEFD in network byte order (i.e. "\xfe\xfd")
    char message_type;                    // See Message Types, below.
    long client_id;                       // The ID previously provided by the client.
    union gamespy_server_payload payload; // The payload of the datagram.
};

It is unknown how the client_id is generated, but any server response to a client datagram contains the client_id originally sent in the client datagram.

The payload of the struct gamespy_client_datagram is a sequence of NULL-delimited string values (i.e. non-terminated string values separated by the byte 0x00), which may typically be interpreted as key-value pairs.

The payload of the struct gamespy_server_datagram depends on the message type.

Message Types

GameSpy TCP Protocol

Much of this section is an interpretation of code written by teknogods to implement the GameSpy TCP protocol. Definitions of some record types were originally described by Wiimm in his documentation of the Mario Kart Wii protocol

With the significant exception of the Global Trading System, almost all Pokémon network traffic uses the GameSpy TCP protocol. This protocol, which uses TCP port 29900, consists of a series of "records" transmitted between client and server. These records consist of a series of key-value pairs with (string) key and (string) value each preceded by a single backslash (C-string "\\"). Empty string values are allowed

A record consists of an initial "record type" key-value pair, where the key indicates the type of record, and the value (if non-empty) is an stringified integer which appears to obliquely indicate the version or subtype of the record type. Additional key-value pairs follow until a final key, with empty value is read, which signifies the end of the record.

Most records are identified by an id key, which indicates the sequence number of the request or response. Responses to requests should contain the same id value as the original request.

It is unknown if order of keys besides the record type and final is significant.

Record Types

The following are known record types (with the record type key-value pair given in parentheses):

Nintendo Wi-Fi Connection Test

In a number of places throughout the networking code (usually when starting the networking stack), the Nintendo DS will perform a test of the Nintendo Wi-Fi Connection by making a simple HTTP request of http://conntest.nintendowifi.net/ and checking for a 200 OK response. The content of the response is not checked outside of the presence of a 200 OK.

If the HTTP connection was refused by the server or invalid data is returned by the server, the DS will do a DNS lookup of conntest.nintendowifi.net three more times (making four attempts total) before giving up with error code 52203. Presumably, in doing these lookups, the DS is hoping to receive another IP address to try connecting to. However, it will not try recontacting the original server if its IP address happens to be returned in any of the subsequent DNS responses.

If a connection is made and dropped by the server or the server responds with an unexpected HTTP response code (e.g. 404 Not Found), the DS will immediately respond with error code 52203 without trying the server again.

Error Codes

Example Data

An example PCAP file is available.

HTTP Request
47 45 54 20 2F 20 48 54 54 50 2F 31 2E 30 0D 0A
48 6F 73 74 3A 20 63 6F 6E 6E 74 65 73 74 2E 6E
69 6E 74 65 6E 64 6F 77 69 66 69 2E 6E 65 74 0D
0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 63 6C 6F
73 65 0D 0A 0D 0A
GET / HTTP/1.0..
Host: conntest.n
intendowifi.net.
.Connection: clo
se....
HTTP Response
48 54 54 50 2F 31 2E 30 20 32 30 30 20 4F 4B 0D
0A 43 6F 6E 74 65 6E 74 2D 74 79 70 65 3A 20 74
65 78 74 2F 68 74 6D 6C 0D 0A 58 2D 4F 72 67 61
6E 69 7A 61 74 69 6F 6E 3A 20 4E 69 6E 74 65 6E
64 6F 0D 0A 53 65 72 76 65 72 3A 20 42 69 67 49
50 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 63
6C 6F 73 65 0D 0A 43 6F 6E 74 65 6E 74 2D 4C 65
6E 67 74 68 3A 20 32 34 36 0D 0A 0D 0A 3C 21 44
4F 43 54 59 50 45 20 68 74 6D 6C 20 50 55 42 4C
49 43 20 22 2D 2F 2F 57 33 43 2F 2F 44 54 44 20
58 48 54 4D 4C 20 31 2E 30 20 54 72 61 6E 73 69
74 69 6F 6E 61 6C 2F 2F 45 4E 22 20 22 68 74 74
70 3A 2F 2F 77 77 77 2E 77 33 2E 6F 72 67 2F 54
52 2F 78 68 74 6D 6C 31 2F 44 54 44 2F 78 68 74
6D 6C 31 2D 74 72 61 6E 73 69 74 69 6F 6E 61 6C
2E 64 74 64 22 3E 0A 3C 68 74 6D 6C 3E 0A 20 20
20 20 0A 3C 68 65 61 64 3E 0A 09 09 3C 74 69 74
6C 65 3E 48 54 4D 4C 20 50 61 67 65 3C 2F 74 69
74 6C 65 3E 0A 3C 2F 68 65 61 64 3E 0A 0A 3C 62
6F 64 79 20 62 67 63 6F 6C 6F 72 3D 22 23 46 46
46 46 46 46 22 3E 0A 54 68 69 73 20 69 73 20 74
65 73 74 2E 68 74 6D 6C 20 70 61 67 65 0A 3C 2F
62 6F 64 79 3E 0A 20 20 20 20 0A 3C 2F 68 74 6D
6C 3E 0A
HTTP/1.0 200 OK.
.Content-type: t
ext/html..X-Orga
nization: Ninten
do..Server: BigI
P..Connection: c
lose..Content-Le
ngth: 246....<!D
OCTYPE html PUBL
IC "-//W3C//DTD 
XHTML 1.0 Transi
tional//EN" "htt
p://www.w3.org/T
R/xhtml1/DTD/xht
ml1-transitional
.dtd">.<html>.  
  .<head>...<tit
le>HTML Page</ti
tle>.</head>..<b
ody bgcolor="#FF
FFFF">.This is t
est.html page.</
body>.    .</htm
l>.

Setting up new Nintendo Wi-Fi Connection User Information

Nintendo Wi-Fi Connection for DS and Wii games uses GameSpy to manage account information. These accounts are managed automatically in the process of setting up your user information in a game and are why friend codes are not portable (as the GameSpy account is created specific to a game card and console, and can not be shared between multiple games). For similar reasons, the Pal Pad must be erased between consoles ("friends" are managed through the GameSpy protocol).

The process of setting up new Nintendo Wi-Fi Connection User Information, then, is a function of the GameSpy protocol and proceeds as follows:

  1. A connection test is performed. (See above for more details)
  2. An AVAILABLE datagram is sent to pokemondpds.available.gs.nintendowifi.net UDP port 27900.
  3. pokemondpds.available.gs.nintendowifi.net responds with an AVAILABLE_RESPONSE datagram. These two datagrams do not appear to be critical, as, if the UDP port was not open, the DS will try to resend the AVAILABLE datagram a second time before giving up and moving on to the next step.
  4. The game will make an HTTPS POST request for a new GameSpy account to https://nas.nintendowifi.net/ac. If the server refuses connections, offers an invalid certificate, or closes the connection without responding, Pokemon will attempt to connect to the server up to three more times (making four attempts total) before giving up with the error code 20100.
  5. nas.nintendowifi.net responds with a challenge and authtoken to use to identify the user with GameSpy.
  6. The game connects to gpcm.gs.nintendowifi.net TCP port 29900 and waits for a response.
  7. gpcm.gs.nintendowifi.net sends a LOGIN_CHALLENGE record when the game connects.
  8. The game responds with a LOGIN record which includes the authtoken generated by nas.nintendowifi.net
  9. gpcm.gs.nintendowifi.net responds with a LOGGED_IN record
  10. The game then requests their own profile with a GETPROFILE record
  11. gpcm.gs.nintendowifi.net responds with a PROFILEINFO record
  12. The game then requests their own profile again and appends an UPDATE_PROFILE record to modify the user's "lastname" field to the same packet.
  13. gpcm.gs.nintendowifi.net responds with an (unmodified) PROFILEINFO record
  14. Until the modified "lastname" field appears in a PROFILEINFO record, the game continue to place GETPROFILE requests.
  15. Once the "lastname" is recorded, the game sends a LOGOUT record and gpcm.gs.nintendowifi.net will close the connection.
  16. The game then reconnects and logs in (i.e. receives a LOGIN_CHALLENGE, responds with a LOGIN, and receives a LOGIN_REQUEST) to gpcm.gs.nintendowifi.net. It will reuse the same challenge and authtoken for this login.
  17. While keeping the TCP connection to gpcm.gs.nintendowifi.net open, the game sends a HEARTBEAT datagram to pokemondpds.master.gs.nintendowifi.net UDP port 27900
  18. As the game waits for a response to the HEARTBEAT datagram, it sends a STATUS(1) record to gpcm.gs.nintendowifi.net
  19. pokemondpds.master.gs.nintendowifi.net will eventually respond with a CHALLENGE_RESPONSE datagram.
  20. The game will respond with a CHALLENGE_RESPONSE datagram of its own.
  21. pokemondpds.master.gs.nintendowifi.net will then respond with a RESPONSE_CORRECT datagram.
  22. If pokemondpds.master.gs.nintendowifi.net fails to respond to the HEARTBEAT datagram, it will try to send it a second time.
  23. Whether or not the RESPONSE_CORRECT datagram is received, the game will send a STATUS(6) record to gpcm.gs.nintendowifi.net, completing registration.
  24. Until the user dismisses the message regarding registration with Nintendo Wi-Fi Connection, the client will continue to send KEEPALIVE datagrams to pokemondpds.master.gs.nintendowifi.net.
  25. Once the user has dismissed the message, a final HEARTBEAT message will be sent to pokemondpds.master.gs.nintendowifi.net and, immediately afterwards, a LOGOUT record will be sent to gpcm.gs.nintendowifi.net which will close the connection.

NOTE: On rare occasions, the DS can enter a locked state if connections are refused to both pokemondpds.available.gs.nintendowifi.net and nas.nintendowifi.net, necessitating a hard reset.

It is also possible to get the DS into an inconsistent state if gpcm.gs.nintendowifi.net fails to return a valid LOGGED_IN response (e.g. if the proof-of-login is incorrect). In this case, the user must restart the DS with a soft reboot before Wi-Fi connections can be made. Until the user does so, all attempts to connect will fail at the connection test stage, as if the connection test server did not respond.

Error Codes

A number of error codes may be encountered at registration time, in addition to the codes that may arise from the connection test, including:

Example Data

An example PCAP file is available (including SSL session keys).

GameSpy AVAILABLE Datagram

(See GameSpy UDP Protocol for more details)

09 00 00 00 00 70 6F 6B 65 6D 6F 6E 64 70 64 73
00
.....pokemondpds
.
GameSpy AVAILABLE_RESPONSE Datagram

(See GameSpy UDP Protocol for more details)

FE FD 09 00 00 00 00                            .......
HTTP POST to nas.nintendowifi.net

Credit for interpreting the key-value pairs goes in part to Vetle, whose partial documentation of the login POST helped to explain some of the fields.

NOTE: This is normally encrypted with SSL.

50 4F 53 54 20 2F 61 63 20 48 54 54 50 2F 31 2E
30 0D 0A 43 6F 6E 74 65 6E 74 2D 74 79 70 65 3A
20 61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 2D 77
77 77 2D 66 6F 72 6D 2D 75 72 6C 65 6E 63 6F 64
65 64 0D 0A 48 6F 73 74 3A 20 6E 61 73 2E 6E 69
6E 74 65 6E 64 6F 77 69 66 69 2E 6E 65 74 0D 0A
55 73 65 72 2D 41 67 65 6E 74 3A 20 4E 69 74 72
6F 20 57 69 46 69 20 53 44 4B 2F 31 2E 32 0D 0A
48 54 54 50 5F 58 5F 47 41 4D 45 43 44 3A 20 41
50 41 45 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A
20 63 6C 6F 73 65 0D 0A 43 6F 6E 74 65 6E 74 2D
4C 65 6E 67 74 68 3A 20 33 30 34 0D 0A 0D 0A 61
63 74 69 6F 6E 3D 62 47 39 6E 61 57 34 2A 26 67
73 62 72 63 64 3D 51 55 52 42 53 6A 49 77 62 54
4E 73 61 6A 4D 2A 26 73 64 6B 76 65 72 3D 4D 44
41 78 4D 44 41 79 26 75 73 65 72 69 64 3D 4D 54
55 34 4D 6A 41 77 4E 6A 55 34 4F 44 41 77 4D 51
2A 2A 26 70 61 73 73 77 64 3D 4E 54 4D 77 26 62
73 73 69 64 3D 4D 44 41 77 5A 44 42 69 5A 6A 67
31 4D 7A 63 77 26 61 70 69 6E 66 6F 3D 4D 44 4D
36 4D 44 41 77 4D 44 41 77 4D 43 30 77 4D 41 2A
2A 26 67 61 6D 65 63 64 3D 51 56 42 42 52 51 2A
2A 26 6D 61 6B 65 72 63 64 3D 4D 44 45 2A 26 75
6E 69 74 63 64 3D 4D 41 2A 2A 26 6D 61 63 61 64
72 3D 4D 44 41 77 4F 57 4A 6D 59 7A 4D 35 4D 32
4D 34 26 6C 61 6E 67 3D 4D 44 45 2A 26 62 69 72
74 68 3D 4D 44 63 78 59 67 2A 2A 26 64 65 76 74
69 6D 65 3D 4D 54 51 77 4D 7A 45 79 4D 44 41 7A
4E 6A 41 32 26 64 65 76 6E 61 6D 65 3D 54 51 42
70 41 47 73 41 5A 51 41 2A 26 69 6E 67 61 6D 65
73 6E 3D 54 51 42 70 41 47 73 41 5A 51 41 2A
POST /ac HTTP/1.
0..Content-type:
 application/x-w
ww-form-urlencod
ed..Host: nas.ni
ntendowifi.net..
User-Agent: Nitr
o WiFi SDK/1.2..
HTTP_X_GAMECD: A
PAE..Connection:
 close..Content-
Length: 304....a
ction=bG9naW4*&g
sbrcd=QURBSjIwbT
NsajM*&sdkver=MD
AxMDAy&userid=MT
U4MjAwNjU4ODAwMQ
**&passwd=NTMw&b
ssid=MDAwZDBiZjg
1Mzcw&apinfo=MDM
6MDAwMDAwMC0wMA*
*&gamecd=QVBBRQ*
*&makercd=MDE*&u
nitcd=MA**&macad
r=MDAwOWJmYzM5M2
M4&lang=MDE*&bir
th=MDcxYg**&devt
ime=MTQwMzEyMDAz
NjA2&devname=TQB
pAGsAZQA*&ingame
sn=TQBpAGsAZQA*

The payload of this POST request is an application/x-www-form-urlencoded collection of key-value pairs. The values are encoded using base64 with the special character = replaced by *, presumably to avoid escaping it.

The key-value pairs in a new account request are:

  1. action - This is always the string login
  2. gsbrcd - Usage unknown, but probably stands for "GameSpy [something] CoDe". Appears to be constant with respect to a given save file, but it's at least partly dynamically generated (the first four characters are fixed to the original game code). It's possible that this code is randomly generated when the game detects that it is part of a new game-pak/DS pair (it does not, however, appear to be constant with respect to the same game-pak and DS). This field is left empty if logging into GTS.
  3. sdkver - The version of the Nitro SDK used by the game, formatted using the format string "%03d%03d" % (major, minor) (e.g. 001002 for Nitro SDK 1.2)
  4. userid - Usage unknown. A 13-digit string formatted using the format string "%013llu". Appears to be constant with respect to a given save file, but may be constant with respect to all identical ROM files.
  5. passwd - Usage unknown. A 3-digit string formatted using the format string "%03u". Appears to be constant with respect to a given save file, but may be constant with respect to all identical ROM files.
  6. bssid - The BSSID of the Wi-Fi AP that the DS is connected to as lower-case hex. (e.g. BSSID 00:0d:0b:f8:53:70 would be encoded as 000d0bf85370)
  7. apinfo - The index of the access point in the Nintendo Wi-Fi Connection Settings used for this connection, formatted using the format string "%02d:0000000-00". (e.g. 00:0000000-00 is used if this is the first AP in the DS settings) The special value 03:0000000-00 is used if the access point is a Nintendo Wi-Fi USB Connector.
  8. gamecd - The game's code, as issued by Nintendo (e.g. APAE)
  9. makercd - Probably the code for the company that made the game, probably formatted using the format string "%02d" (Nintendo uses 01)
  10. unitcd - Unknown. Always 0
  11. macadr - The MAC address of the Nintendo DS (e.g. 00:09:bf:c3:93:c8 would be encoded as 0009bfc393c8)
  12. lang - The language code (of the game? of the system?), probably formatted using the format string "%02d" (English is 01)
  13. birth - The birth date of the user of the Nintendo DS, formatted using the format string "%02x%02x" % (month, day) (e.g. 071b for July 27)
  14. devtime - The local time of the Nintendo DS, as if formatted using the Unicode date format pattern "yyMMddHHmmss" (e.g. 140312003606 for ISO date "2014-03-12T00:36:06")
  15. devname - The name of the user of the Nintendo DS encoded in little-endian UTF-16 (UTF-16LE) (e.g. {0x4D, 0x00, 0x69, 0x00, 0x6B, 0x00, 0x65, 0x00} for Mike)
  16. ingamesn - Same as devname, omitted when logging into GTS

It is unclear which of these exactly serves as the key to lookup the newly created account later, although it appears that userid, passwd and gamecd are sufficient (userid and passwd alone appear to only identify the DS device).

HTTP Response from nas.nintendowifi.net

Credit for interpreting the key-value pairs goes in part to Vetle, whose partial documentation of the login POST helped to explain some of the fields.

NOTE: This is normally encrypted with SSL.

48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D
0A 4E 4F 44 45 3A 20 77 69 66 69 61 70 70 65 32
0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 65 3A 20
74 65 78 74 2F 70 6C 61 69 6E 0D 0A 43 6F 6E 74
65 6E 74 2D 4C 65 6E 67 74 68 3A 20 32 38 37 0D
0A 44 61 74 65 3A 20 57 65 64 2C 20 31 32 20 4D
61 72 20 32 30 31 34 20 30 35 3A 33 35 3A 31 32
20 47 4D 54 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E
3A 20 63 6C 6F 73 65 0D 0A 53 65 72 76 65 72 3A
20 4E 69 6E 74 65 6E 64 6F 20 57 69 69 20 28 68
74 74 70 29 0D 0A 0D 0A 63 68 61 6C 6C 65 6E 67
65 3D 55 6B 35 53 4D 55 68 4D 51 56 4D 2A 26 6C
6F 63 61 74 6F 72 3D 5A 32 46 74 5A 58 4E 77 65
53 35 6A 62 32 30 2A 26 72 65 74 72 79 3D 4D 41
2A 2A 26 72 65 74 75 72 6E 63 64 3D 4D 44 41 78
26 74 6F 6B 65 6E 3D 54 6B 52 54 57 44 42 36 65
56 6B 32 56 32 4D 32 55 31 45 32 52 32 35 32 57
46 4E 30 51 55 4A 33 59 6B 5A 44 51 6D 70 6E 64
43 74 4E 56 6C 46 35 61 48 4D 78 64 6B 31 50 4E
58 46 7A 54 57 35 43 5A 56 42 73 59 32 35 48 54
32 70 71 55 46 52 6A 62 47 39 76 5A 31 64 59 4D
44 4E 35 53 46 5A 51 4F 56 45 31 65 47 35 56 62
58 4D 34 61 6C 70 56 65 6E 6C 6B 4D 6C 63 35 65
58 52 58 52 6E 52 73 64 31 56 50 61 45 46 6A 54
7A 42 34 4F 56 64 6D 52 6E 59 79 63 56 42 4F 52
6B 35 79 4F 55 38 77 5A 57 68 72 64 46 4A 5A 55
6D 4E 32 4F 44 6B 2A 26 64 61 74 65 74 69 6D 65
3D 4D 6A 41 78 4E 44 41 7A 4D 54 49 77 4E 54 4D
31 4D 54 49 2A 0D 0A
HTTP/1.1 200 OK.
.NODE: wifiappe2
..Content-Type: 
text/plain..Cont
ent-Length: 287.
.Date: Wed, 12 M
ar 2014 05:35:12
 GMT..Connection
: close..Server:
 Nintendo Wii (h
ttp)....challeng
e=Uk5SMUhMQVM*&l
ocator=Z2FtZXNwe
S5jb20*&retry=MA
**&returncd=MDAx
&token=TkRTWDB6e
Vk2V2M2U1E2R252W
FN0QUJ3YkZDQmpnd
CtNVlF5aHMxdk1PN
XFzTW5CZVBsY25HT
2pqUFRjbG9vZ1dYM
DN5SFZQOVE1eG5Vb
XM4alpVenlkMlc5e
XRXRnRsd1VPaEFjT
zB4OVdmRnYycVBOR
k5yOU8wZWhrdFJZU
mN2ODk*&datetime
=MjAxNDAzMTIwNTM
1MTI*..

Like the payload of the POST request, the body of this HTTP response is an application/x-www-form-urlencoded collection of key-value pairs. Again, the values are encoded using base64 with the special character = replaced by *.

The key-value pairs in a new account request response are:

  1. challenge - A random 8-character challenge string (upper-case ASCII only) to be used as the password when logging in to GameSpy for this session
  2. locator - Usage unknown. Always gamespy.com
  3. retry - Probably a flag to retry creating the account due to an error. In practice, this is always 0, presumably signifying that no retry was needed. If the request data is incorrect (e.g. if the password doesn't match Nintendo's records), this value will be 1 and only returncd and datetime will be included in the response.
  4. returncd - Probably a return code to signify success or failure. In practice, this is always 001, presumably signifying success. It is possible to get a value 109 if there was an error in interpreting the request (e.g. if the password presumably doesnt match the username) but the exact meaning of this code is unknown. In this case, retry will be 1 and all other fields except datetime will be missing.
  5. token - An authentication token to present to the GameSpy servers as the username for this session. Appears to be a random string of 96 bytes, which is then base64-encoded and prefixed by "NDS"
  6. datetime - The time of the server in GMT, as if formatted using the Unicode date format pattern "yyyyMMddHHmmss" (e.g. 20140312053512 for ISO date "2014-03-12T05:35:12")

NOTE: Pokémon Pearl does not appear to check the NODE, Content-type, or Server headers, so they need not be correct (or present).

GameSpy LOGIN_CHALLENGE Record

(See GameSpy TCP Protocol for more details)

5C 6C 63 5C 31 5C 63 68 61 6C 6C 65 6E 67 65 5C
53 45 58 48 49 59 45 43 59 59 5C 69 64 5C 31 5C
66 69 6E 61 6C 5C
\lc\1\challenge\
SEXHIYECYY\id\1\
final\
GameSpy LOGIN Record

(See GameSpy TCP Protocol for more details)

NOTE: This record is not actually sampled from the same sequence as the previous LOGIN_CHALLENGE record, so the correct response value for that record may differ.

5C 6C 6F 67 69 6E 5C 5C 63 68 61 6C 6C 65 6E 67
65 5C 54 6B 45 72 34 4E 46 76 41 34 66 73 45 52
70 63 49 67 69 44 72 55 50 31 51 79 4D 43 74 6C
4A 6F 5C 61 75 74 68 74 6F 6B 65 6E 5C 4E 44 53
58 30 7A 79 59 36 57 63 36 53 51 36 47 6E 76 58
53 74 41 42 77 62 46 43 42 6A 67 74 2B 4D 56 51
79 68 73 31 76 4D 4F 35 71 73 4D 6E 42 65 50 6C
63 6E 47 4F 6A 6A 50 54 63 6C 6F 6F 67 57 58 30
33 79 48 56 50 39 51 35 78 6E 55 6D 73 38 6A 5A
55 7A 79 64 32 57 39 79 74 57 46 74 6C 77 55 4F
68 41 63 4F 30 78 39 57 66 46 76 32 71 50 4E 46
4E 72 39 4F 30 65 68 6B 74 52 59 52 63 76 38 39
5C 72 65 73 70 6F 6E 73 65 5C 63 63 31 64 61 38
39 34 38 35 62 37 37 61 64 65 65 32 33 31 61 36
61 38 37 38 33 33 62 39 39 36 5C 66 69 72 65 77
61 6C 6C 5C 31 5C 70 6F 72 74 5C 30 5C 70 72 6F
64 75 63 74 69 64 5C 31 30 37 32 37 5C 67 61 6D
65 6E 61 6D 65 5C 70 6F 6B 65 6D 6F 6E 64 70 64
73 5C 6E 61 6D 65 73 70 61 63 65 69 64 5C 30 5C
69 64 5C 31 5C 66 69 6E 61 6C 5C
\login\\challeng
e\TkEr4NFvA4fsER
pcIgiDrUP1QyMCtl
Jo\authtoken\NDS
X0zyY6Wc6SQ6GnvX
StABwbFCBjgt+MVQ
yhs1vMO5qsMnBePl
cnGOjjPTcloogWX0
3yHVP9Q5xnUms8jZ
Uzyd2W9ytWFtlwUO
hAcO0x9WfFv2qPNF
Nr9O0ehktRYRcv89
\response\cc1da8
9485b77adee231a6
a87833b996\firew
all\1\port\0\pro
ductid\10727\gam
ename\pokemondpd
s\namespaceid\0\
id\1\final\
GameSpy LOGGED_IN Record

(See GameSpy TCP Protocol for more details)

5C 6C 63 5C 32 5C 73 65 73 73 6B 65 79 5C 32 31
30 33 37 35 32 38 37 5C 70 72 6F 6F 66 5C 66 38
34 65 64 39 34 64 38 38 66 31 39 37 32 32 65 64
65 34 65 30 32 33 65 61 65 35 34 61 30 30 5C 75
73 65 72 69 64 5C 34 34 33 33 35 37 32 30 32 5C
70 72 6F 66 69 6C 65 69 64 5C 34 37 35 34 37 35
39 35 36 5C 75 6E 69 71 75 65 6E 69 63 6B 5C 31
65 31 62 66 31 6B 6A 31 41 44 41 4A 32 30 6D 33
6C 6A 33 5C 6C 74 5C 4E 65 5B 45 69 61 4C 62 43
79 64 44 68 59 6D 4D 5D 4F 48 58 61 63 5F 5F 5C
69 64 5C 31 5C 66 69 6E 61 6C 5C
\lc\2\sesskey\21
0375287\proof\f8
4ed94d88f19722ed
e4e023eae54a00\u
serid\443357202\
profileid\475475
956\uniquenick\1
e1bf1kj1ADAJ20m3
lj3\lt\Ne[EiaLbC
ydDhYmM]OHXac__\
id\1\final\
GameSpy GETPROFILE Record

(See GameSpy TCP Protocol for more details)

5C 67 65 74 70 72 6F 66 69 6C 65 5C 5C 73 65 73
73 6B 65 79 5C 32 31 30 33 37 35 32 38 37 5C 70
72 6F 66 69 6C 65 69 64 5C 34 37 35 34 37 35 39
35 36 5C 69 64 5C 32 5C 66 69 6E 61 6C 5C
\getprofile\\ses
skey\210375287\p
rofileid\4754759
56\id\2\final\
GameSpy PROFILEINFO Record

(See GameSpy TCP Protocol for more details)

5C 70 69 5C 5C 70 72 6F 66 69 6C 65 69 64 5C 34
37 35 34 37 35 39 35 36 5C 6E 69 63 6B 5C 31 65
31 62 66 31 6B 6A 31 41 44 41 4A 32 30 6D 33 6C
6A 33 5C 75 73 65 72 69 64 5C 34 34 33 33 35 37
32 30 32 5C 65 6D 61 69 6C 5C 31 65 31 62 66 31
6B 6A 31 41 44 41 4A 32 30 6D 33 6C 6A 33 40 6E
64 73 5C 73 69 67 5C 38 30 34 38 66 30 32 30 39
32 32 31 34 61 35 32 37 34 32 37 62 35 31 62 39
31 63 33 39 38 30 34 5C 75 6E 69 71 75 65 6E 69
63 6B 5C 31 65 31 62 66 31 6B 6A 31 41 44 41 4A
32 30 6D 33 6C 6A 33 5C 70 69 64 5C 31 31 5C 6C
6F 6E 5C 30 2E 30 30 30 30 30 30 5C 6C 61 74 5C
30 2E 30 30 30 30 30 30 5C 6C 6F 63 5C 5C 69 64
5C 32 5C 66 69 6E 61 6C 5C
\pi\\profileid\4
75475956\nick\1e
1bf1kj1ADAJ20m3l
j3\userid\443357
202\email\1e1bf1
kj1ADAJ20m3lj3@n
ds\sig\8048f0209
2214a527427b51b9
1c39804\uniqueni
ck\1e1bf1kj1ADAJ
20m3lj3\pid\11\l
on\0.000000\lat\
0.000000\loc\\id
\2\final\

This is the default state of a profile when it has been initially created.

GameSpy GETPROFILE & UPDATE_PROFILE Record

(See GameSpy TCP Protocol for more details)

5C 67 65 74 70 72 6F 66 69 6C 65 5C 5C 73 65 73
73 6B 65 79 5C 32 31 30 33 37 35 32 38 37 5C 70
72 6F 66 69 6C 65 69 64 5C 34 37 35 34 37 35 39
35 36 5C 69 64 5C 33 5C 66 69 6E 61 6C 5C 5C 75
70 64 61 74 65 70 72 6F 5C 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 32 38 37 5C 6C 61 73 74
6E 61 6D 65 5C 31 65 31 62 66 31 6B 6A 31 41 44
41 4A 32 30 6D 33 6C 6A 33 5C 66 69 6E 61 6C 5C
\getprofile\\ses
skey\210375287\p
rofileid\4754759
56\id\2\final\\u
pdatepro\\sesske
y\210375287\last
name\1e1bf1kj1AD
AJ20m3lj3\final\
GameSpy PROFILEINFO Record with lastname field

(See GameSpy TCP Protocol for more details)

5C 70 69 5C 5C 70 72 6F 66 69 6C 65 69 64 5C 34
37 35 34 37 35 39 35 36 5C 6E 69 63 6B 5C 31 65
31 62 66 31 6B 6A 31 41 44 41 4A 32 30 6D 33 6C
6A 33 5C 75 73 65 72 69 64 5C 34 34 33 33 35 37
32 30 32 5C 65 6D 61 69 6C 5C 31 65 31 62 66 31
6B 6A 31 41 44 41 4A 32 30 6D 33 6C 6A 33 40 6E
64 73 5C 73 69 67 5C 38 30 34 38 66 30 32 30 39
32 32 31 34 61 35 32 37 34 32 37 62 35 31 62 39
31 63 33 39 38 30 34 5C 75 6E 69 71 75 65 6E 69
63 6B 5C 31 65 31 62 66 31 6B 6A 31 41 44 41 4A
32 30 6D 33 6C 6A 33 5C 70 69 64 5C 31 31 5C 6C
61 73 74 6E 61 6D 65 5C 31 65 31 62 66 31 6B 6A
31 41 44 41 4A 32 30 6D 33 6C 6A 33 5C 6C 6F 6E
5C 30 2E 30 30 30 30 30 30 5C 6C 61 74 5C 30 2E
30 30 30 30 30 30 5C 6C 6F 63 5C 5C 69 64 5C 34
5C 66 69 6E 61 6C 5C
\pi\\profileid\4
75475956\nick\1e
1bf1kj1ADAJ20m3l
j3\userid\443357
202\email\1e1bf1
kj1ADAJ20m3lj3@n
ds\sig\8048f0209
2214a527427b51b9
1c39804\uniqueni
ck\1e1bf1kj1ADAJ
20m3lj3\pid\11\l
astname\1e1bf1kj
1ADAJ20m3lj3\lon
\0.000000\lat\0.
000000\loc\\id\4
\final\
GameSpy LOGOUT Record

(See GameSpy TCP Protocol for more details)

5C 6C 6F 67 6F 75 74 5C 5C 73 65 73 73 6B 65 79
5C 32 31 30 33 37 35 32 38 37 5C 66 69 6E 61 6C
5C
\logout\\sesskey
\210375287\final
\
GameSpy HEARTBEAT Datagram

(See GameSpy UDP Protocol for more details)

03 82 2B 3E 31 6C 6F 63 61 6C 69 70 30 00 31 39
32 2E 31 36 38 2E 31 2E 32 00 6C 6F 63 61 6C 70
6F 72 74 00 35 32 34 32 35 00 6E 61 74 6E 65 67
00 31 00 73 74 61 74 65 63 68 61 6E 67 65 64 00
31 00 67 61 6D 65 6E 61 6D 65 00 70 6F 6B 65 6D
6F 6E 64 70 64 73 00 70 75 62 6C 69 63 69 70 00
30 00 70 75 62 6C 69 63 70 6F 72 74 00 30 00 6E
75 6D 70 6C 61 79 65 72 73 00 30 00 6D 61 78 70
6C 61 79 65 72 73 00 30 00 75 6E 6B 6E 6F 77 6E
00 34 37 35 34 37 35 39 35 36 00 75 6E 6B 6E 6F
77 6E 00 30 00 75 6E 6B 6E 6F 77 6E 00 30 00 75
6E 6B 6E 6F 77 6E 00 33 00 75 6E 6B 6E 6F 77 6E
00 31 00 00 00 00 00 00 00 00
..+>1localip0.19
2.168.1.2.localp
ort.52425.natneg
.1.statechanged.
1.gamename.pokem
ondpds.publicip.
0.publicport.0.n
umplayers.0.maxp
layers.0.unknown
.475475956.unkno
wn.0.unknown.0.u
nknown.3.unknown
.1........

The payload of this request is as expected for an initial HEARTBEAT datagram, with the publicip and publicport properties initially set to 0.

The properties that are initially set include:

  1. localip0 - The local IP address of the DS
  2. localport - Unknown. May be the UDP port on the DS that the packet was sent from, but it is unclear if this is the case.
  3. natneg - Unknown. May always be 1.
  4. statechanged - Unknown. May always be 1.
  5. gamename - The GameSpy game name. For Pokémon Pearl (and presumably Diamond), this is pokemondpds.
  6. publicip - Initially 0.
  7. publicport - Initially 0.
  8. numplayers - Initially 0.
  9. maxplayers - Initially 0.
  10. unknown - Five unknown keys are eventually replaced with dwc_pid, dwc_mtype, dwc_mresv, and dwc_eval, in that order, but take their respective values immediately, except for dwc_mtype, which is initially 0. The meaning of their values is unknown, except for dwc_pid, which is the profileid of the user.
GameSpy STATUS(1) Record

(See GameSpy TCP Protocol for more details)

5C 73 74 61 74 75 73 5C 31 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 73 74 61 74
73 74 72 69 6E 67 5C 5C 6C 6F 63 73 74 72 69 6E
67 5C 5C 66 69 6E 61 6C 5C
\status\1\sesske
y\210375303\stat
string\\locstrin
g\\final\
GameSpy CHALLENGE_RESPONSE from Server Datagram

(See GameSpy UDP Protocol for more details)

FE FD 01 82 2B 3E 31 30 4C 7B 6F 7E 5A 30 30 30
41 30 30 30 30 30 36 39 46 31 36 00
....+>10L{o~Z000
A0000069F16.
GameSpy CHALLENGE_RESPONSE from Client Datagram

(See GameSpy UDP Protocol for more details)

01 82 2B 3E 31 55 71 35 72 45 70 68 79 75 30 48
38 4F 30 7A 6F 43 2F 55 2F 51 38 6B 6B 52 66 51
41 00
..+>1Uq5rEphyu0H
8O0zoC/U/Q8kkRfQ
A.
GameSpy RESPONSE_CORRECT Datagram

(See GameSpy UDP Protocol for more details)

FE FD 0A 3D B0 A8 D6 00 00 00 00 00 00 00 00 00
00 00
...=............
..
GameSpy STATUS(6) Record
5C 73 74 61 74 75 73 5C 36 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 73 74 61 74
73 74 72 69 6E 67 5C 2F 53 43 4D 2F 32 2F 53 43
4E 2F 31 2F 56 45 52 2F 33 5C 6C 6F 63 73 74 72
69 6E 67 5C 5C 66 69 6E 61 6C 5C
\status\6\sesske
y\210375303\stat
string\/SCM/2/SC
N/1/VER/3\locstr
ing\\final\
GameSpy KEEPALIVE Datagram

(See GameSpy UDP Protocol for more details)

08 82 2B 3E 31                                  ..+>1
GameSpy Final HEARTBEAT Datagram

(See GameSpy UDP Protocol for more details)

03 82 2B 3E 31 6C 6F 63 61 6C 69 70 30 00 31 39
32 2E 31 36 38 2E 31 2E 32 00 6C 6F 63 61 6C 70
6F 72 74 00 35 32 34 32 35 00 6E 61 74 6E 65 67
00 31 00 73 74 61 74 65 63 68 61 6E 67 65 64 00
32 00 67 61 6D 65 6E 61 6D 65 00 70 6F 6B 65 6D
6F 6E 64 70 64 73 00 70 75 62 6C 69 63 69 70 00
31 30 30 36 36 33 33 30 36 00 70 75 62 6C 69 63
70 6F 72 74 00 34 30 37 32 36 00 00
..+>1localip0.19
2.168.1.2.localp
ort.52425.natneg
.1.statechanged.
2.gamename.pokem
ondpds.publicip.
100663306.public
port.40726..

The payload of this request is shorter than the initial HEARTBEAT, perhaps because it means that the game is logging out.

The properties that are set include:

  1. localip0 - The local IP address of the DS
  2. localport - Unknown. May be the UDP port on the DS that the packet was sent from, but it is unclear if this is the case.
  3. natneg - Unknown. May always be 1.
  4. statechanged - Unknown. Changed to 2, perhaps to signal logging out.
  5. gamename - The GameSpy game name. For Pokémon Pearl (and presumably Diamond), this is pokemondpds.
  6. publicip - The public IP previously returned in the initial CHALLENGE_RESPONSE datagram from the server, reinterpreted as a little-endian 4-byte integer (e.g. 10.0.0.6 = 0A000006 = 0x0600000A = 100663306, not 0x0A000006 = 167772166 as might be assumed).
  7. publicport - The public port previously returned in the initial CHALLENGE_RESPONSE datagram from the server.
  8. Note that the final message is terminated with two NULL bytes.

Global Trading System

The basics of this protocol have been previously documented on the Project Pokemon Wiki and can be derived from a number of applications designed to re-implement GTS. As such, I do not intend to detail this protocol here until other, currently undocumented protocols have been documented.

That said, it should be noted that initial contact to http://gamestats2.gs.nintendowifi.net/pokemondpds/worldexchange/info.asp?pid=[PID] is preceded by the standard login protocol of first contacting conntest.nintendowifi.net and then contacting nas.nintendowifi.net. The POST payload to nas differs from that used when connecting to the Wi-Fi Club in that the gsbrcd value is left empty (because the GameSpy TCP protocol is not used)

Wi-Fi Club

Logging In and Out

The registration mechanism above is effectively a special variant of the more general-purpose login mechanism, which is as follows:

  1. A connection test is performed. (See above for more details)
  2. An AVAILABLE datagram is sent to pokemondpds.available.gs.nintendowifi.net UDP port 27900.
  3. pokemondpds.available.gs.nintendowifi.net responds with an AVAILABLE_RESPONSE datagram.
  4. The game will make an HTTPS POST request to https://nas.nintendowifi.net/ac. This request is identical to the request for a new account, except that the gsbrcd field is not specified if the game is logging into GTS.
  5. nas.nintendowifi.net responds with a challenge and authtoken to use to identify the user with GameSpy.
  6. NOTE: This and the following steps do not apply for connections to GTS. The game connects to gpcm.gs.nintendowifi.net TCP port 29900 and waits for a response.
  7. gpcm.gs.nintendowifi.net sends a LOGIN_CHALLENGE record when the game connects.
  8. The game responds with a LOGIN record which includes the authtoken generated by nas.nintendowifi.net
  9. gpcm.gs.nintendowifi.net responds with a LOGGED_IN record
  10. Once this is complete, the user is authenticated with the gpcm server. Additional communications follow depending on the server state and game state.
  11. In addition, at this time, the game begins to login to the master UDP server pokemondpds.master.gs.nintendowifi.net at port 27900, sending a HEARTBEAT datagram
  12. When pokemondpds.master.gs.nintendowifi.net responds with a CHALLENGE_RESPONSE datagram, the game will respond with its own CHALLENGE_RESPONSE datagram
  13. pokemondpds.master.gs.nintendowifi.net will respond with a RESPONSE_CORRECT datagram if the game's CHALLENGE_RESPONSE datagram was correct.
  14. If pokemondpds.master.gs.nintendowifi.net fails to respond to the HEARTBEAT datagram, it will try to send it a second time.
  15. At the same time as this UDP login progresses, gpcm.gs.nintendowifi.net will send any BUDDYMESSAGE records that are pending on the server (e.g. ADDBUDDY_REQUEST records), following the LOGGED_IN record. These records are sent in order of the version/subtype ID (so ADDBUDDY_AUTHORIZED records will be received before ADDBUDDY_REQUEST records)
  16. If the user has successfully registered any friends with the server, the server will then send BUDDY_STATUS records for each registered friend in the order they were fully registered (not in the order in the Pal Pad).
  17. So long as the server has sent BUDDY_STATUS records for an incomplete subset of the buddies registered in the Pal Pad, the game appears to wait for up to ~2 seconds from the last BUDDY_STATUS record received. If, after these ~2 seconds have passed with no further BUDDY_STATUS records received:
    1. The game appears to open a connection to gpsp.gs.nintendowifi.net TCP port 27901 to query further status regarding these buddies.
    2. The game will send a SEARCH record to gpsp.gs.nintendowifi.net indicating the missing buddy by lastname (not profileid)
    3. gpsp.gs.nintendowifi.net will then return any matching records as BUDDY_SEARCH_RECORD records. (It is not clear if this search requires the buddy to be present in the server-side buddy list of the user)
    4. Once any such records are returned (if any), gpsp.gs.nintendowifi.net will return a BUDDY_SEARCH_REQUEST_DONE record.
    5. The game will disconnect from the server at this time. It is possible that the game will automatically remove buddies who are not found this way from the Pal Pad (but this is not known for certain, as this behavior is rarely observed.).
  18. Independent of any wait or connection to gpsp.gs.nintendowifi.net, after waiting a short amount of time (about 250 milliseconds) for the initial BUDDYMESSAGE records to arrive, the game will then send its initial STATUS(1) record to the server. This record has an empty locstring and is always sent to the server.
  19. The game will then handle all ADDBUDDY_REQUEST records received (either completing or ignoring the friend requests according to whether the requesting users are in the game's Pal Pad. More on this in the next section.).
  20. If there have been any changes to the contents of the game's Pal Pad since the last login (or if there are newly added buddies which have not been confirmed through the server), the game will send the appropriate ADDBUDDY and DELBUDDY records to the server notifying the server of those changes (and unconfirmed buddies). DELBUDDY records appear to be sent first, followed by ADDBUDDY records in the order that they appear(ed) in the Pal Pad.
  21. Finally, appended to these records (if any), the client will send a second STATUS(1) record and a STATUS(6) record to the server including a populated locstring, for the server to notify any known buddies of the game's status. These two records will always be sent to the server.

Logging out is simpler, and follows the last step of the registration mechanism. Namely:

  1. A final HEARTBEAT datagram is sent to pokemondpds.master.gs.nintendowifi.net
  2. A LOGOUT record is sent to gpcm.gs.nintendowifi.net and the server will close the connection. When the LOGOUT record is received by the server, the server will send a BUDDY_STATUS record to all online buddies notify them that the user has logged out.

If there is an error in the LOGIN record (e.g. if the challenge value is incorrect), an ERROR record (error code 266) will be sent to the client and the server will close the connection.

Several additional records are common to all connections. gpcm.gs.nintendowifi.net will send the client a KEEPALIVE record to the game if there are no communications between the two for approximately 120 seconds. No record is sent in response (a TCP ACK is sufficient to notify the server that the connection is still alive).

In addition, a LOGIN_TICKET record will be sent to the game approximately every 250 seconds following a successful login.

(TODO: What is the use of gpsp? \search and \bsrdone\ records on gpsp)

GameSpy STATUS(1) Record at login
5C 73 74 61 74 75 73 5C 31 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 73 74 61 74
73 74 72 69 6E 67 5C 5C 6C 6F 63 73 74 72 69 6E
67 5C 6C 51 47 43 41 65 30 42 39 41 42 6A 41 57
4D 42 41 41 41 41 41 41 41 41 41 41 41 41 41 41
41 41 41 41 45 42 45 41 44 2D 41 41 41 41 41 51
45 41 5C 66 69 6E 61 6C 5C
\status\1\sesske
y\210375303\stat
string\\locstrin
g\lQGCAe0B9ABjAW
MBAAAAAAAAAAAAAA
AAAAEBEAD-AAAAAQ
EA\final\

At login time, any DELBUDDY records and ADDBUDDY records will be prepended to this record. Also, the following record will be appended.

GameSpy STATUS(6) Record at login
5C 73 74 61 74 75 73 5C 36 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 73 74 61 74
73 74 72 69 6E 67 5C 2F 53 43 4D 2F 32 2F 53 43
4E 2F 31 2F 56 45 52 2F 33 5C 6C 6F 63 73 74 72
69 6E 67 5C 6C 51 47 43 41 65 30 42 39 41 42 6A
41 57 4D 42 41 41 41 41 41 41 41 41 41 41 41 41
41 41 41 41 41 41 45 42 45 41 44 2D 41 41 41 41
41 51 45 41 5C 66 69 6E 61 6C 5C
\status\6\sesske
y\210375303\stat
string\/SCM/2/SC
N/1/VER/3\locstr
ing\lQGCAe0B9ABj
AWMBAAAAAAAAAAAA
AAAAAAEBEAD-AAAA
AQEA\final\

At login time, the previous STATUS(1) record will be prepended.

GameSpy ERROR 266 Record
5C 65 72 72 6F 72 5C 5C 65 72 72 5C 32 36 36 5C
66 61 74 61 6C 5C 5C 65 72 72 6D 73 67 5C 54 68
65 72 65 20 77 61 73 20 61 6E 20 65 72 72 6F 72
20 76 61 6C 69 64 61 74 69 6E 67 20 74 68 65 20
70 72 65 2D 61 75 74 68 65 6E 74 69 63 61 74 69
6F 6E 2E 5C 69 64 5C 31 5C 66 69 6E 61 6C 5C
\error\\err\266\
fatal\\errmsg\Th
ere was an error
 validating the 
pre-authenticati
on.\id\1\final\
GameSpy KEEPALIVE Record
5C 6B 61 5C 5C 66 69 6E 61 6C 5C \ka\\final\
GameSpy LOGIN_TICKET Record
5C 6C 74 5C 52 70 69 48 43 47 42 4A 5B 61 73 54
45 33 57 4A 68 72 44 59 48 65 5F 5F 5C 66 69 6E
61 6C 5C
\lt\RpiHCGBJ[asT
E3WJhrDYHe__\fin
al\

Adding and Removing Friends

Adding and removing friends through the Pal Pad is a process that requires confirmation not only from the server but from the desired friend as well before the friend can be considered "fully registered". For the purpose of this explanation, I will refer to two friends, named Alice and Bob.

The process begins when Alice adds Bob's friend code to her Pal Pad. Once Bob has been registered on her Pal Pad, at her next login to the Nintendo Wi-Fi Connection (and every subsequent login until Bob is either "fully registered" or Alice removes Bob's entry from the Pal Pad), Alice's game will send an ADDBUDDY record regarding Bob to gpcm.gs.nintendowifi.net together with her initial STATUS(1) and STATUS(6) records (i.e. those with the locstring set). This ADDBUDDY record includes Bob's profile ID (i.e. the lower 32 bits of his friend code).

Upon receiving the ADDBUDDY record, the server will check to see if Bob is online. If so, Bob will immediately receive an ADDBUDDY_REQUEST record (i.e. a BUDDYMESSAGE_2 record). If Bob is not online, he will receive an ADDBUDDY_REQUEST record at his next login. This delayed record will include the timestamp of the first time that Alice sent the server an ADDBUDDY_REQUEST since the previous time Bob logged in. Only one such delayed record will be received by Bob regardless of the number of times that Alice logs in and sends an ADDBUDDY_REQUEST about Bob.

After Bob receives the ADDBUDDY_REQUEST record, he will send the server a GETPROFILE record requesting Alice's profile, to which the server will respond with her PROFILEINFO record. He will always request such a record, even if Alice is not listed in his Pal Pad.

If Alice is also listed in Bob's Pal Pad, Bob's game will then send an AUTHADD and an ADDBUDDY record to the server to authorize Alice's request and then request that Alice add Bob as a buddy as well.

Once the server processes the AUTHADD, the registration process is partially complete. Alice will now be able to see Bob's status until she removes Bob from her Pal Pad (even if Bob removes Alice from his Pal Pad). The server will immediately send a BUDDY_STATUS record including Bob's most recent status to Alice.

In addition, the server will send an ADDBUDDY_AUTHORIZED record to Alice after which Alice's game will officially mark Bob as registered (her game will no longer send an ADDBUDDY record for Bob on any subsequent login unless she has removed Bob from her Pal Pad). Appended to this record will be an ADDBUDDY_REQUEST record from Bob. Like the original ADDBUDDY_REQUEST record sent to Bob, these records will be held on the server if Alice is not logged in, and will be sent to Alice dated with the date they were initially sent to the server at her next login. This allows for Bob and Alice to mark each other as buddies without having to be on at the same time.

Alice will respond like Bob did, first requesting a GETPROFILE record for Bob and receiving a PROFILEINFO record from the server containing Bob's information. For an unknown reason, as the second person to receive an ADDBUDDY_REQUEST, Alice will actually place two GETPROFILE records with separate request IDs (and consequently get two separate PROFILEINFO records). It's possible that Alice requests the first due to the ADDBUDDY_AUTHORIZED record.

Assuming that Alice has added Bob as a buddy to her Pal Pad (which should go without saying, given that she started the process), she will similarly submit an AUTHADD record and (oddly enough) an ADDBUDDY record to the server.

Again, Bob will immediately be informed of Alice's status in a BUDDY_STATUS record and then be sent an ADDBUDDY_AUTHORIZED record.

Unlike when Alice received Bob's ADDBUDDY_AUTHORIZED record, however, Bob does not receive the ADDBUDDY_REQUEST record. Instead, the server responds to Alice with an ERROR record (error code 1539) indicating that Bob has already been added as a buddy.

Bob will finalize his registration of Alice as a buddy by sending a GETPROFILE record for Alice to the server and receiving a PROFILEINFO record in return.

Removing a buddy is much simpler than adding a buddy. At the first login following Alice's removal of Bob from her Pal Pad, her game will send a DELBUDDY record, at which point the server will delete Bob from Alice's buddy list and no longer send status updates about Bob to Alice.

Alice will not send a DELBUDDY record if she has not finished marking Bob as a friend on the server (i.e. she has not received an ADDBUDDY_AUTHORIZED message). Should Alice receive an ADDBUDDY_AUTHORIZED message after this (if, for example, Bob only logs in and responds to the original ADDBUDDY_REQUEST record with an AUTHADD record of his own after Alice has already removed him from her Pal Pad), she will immediately respond to the server with a DELBUDDY message.

Removing a buddy using DELBUDDY is a one-way operation. Bob may still see Alice's status (and will be informed of her status changes) until he removes Alice from his Pal Pad. However, Alice will not respond to any BUDDYMESSAGE records originating from Bob unless Bob is in her Pal Pad. This is why the Nintendo Wi-Fi Connection gives an ambiguous error as to whether a "failure to connect" is due to a real connection failure or due to Alice having removed Bob from her Pal Pad. In both cases, Alice will fail to respond.

NOTE: It is possible for Bob to send two ADDBUDDY records, if he processes Alice's ADDBUDDY_REQUEST record at login time before Bob has had the chance to send his own ADDBUDDY record for Alice to the server. In this case, depending on whether the server has sent an Bob's first ADDBUDDY_REQUEST record to Alice (or perhaps whether the server has seen an AUTHADD in response) the server will send one or two ADDBUDDY_REQUEST records to Alice. Each ADDBUDDY_REQUEST will be responded to, regardless of whether one has been processed previously (and Bob may thus receive two ADDBUDDY_AUTHORIZED records and respond appropriately).

If, at any point, the server stops sending BUDDY_STATUS records for Alice to Bob, Bob will return to the initial state and will send ADDBUDDY records to the server regarding Alice until BUDDY_STATUS records begin to be sent once more.

GameSpy ADDBUDDY Record
5C 61 64 64 62 75 64 64 79 5C 5C 73 65 73 73 6B
65 79 5C 32 31 30 33 37 35 33 30 33 5C 6E 65 77
70 72 6F 66 69 6C 65 69 64 5C 34 37 35 37 37 36
37 37 35 5C 72 65 61 73 6F 6E 5C 5C 66 69 6E 61
6C 5C
\addbuddy\\sessk
ey\210375303\new
profileid\475776
775\reason\\fina
l\
GameSpy ADDBUDDY_REQUEST Record received while logged in
5C 62 6D 5C 32 5C 66 5C 34 37 35 37 37 36 37 37
35 5C 6D 73 67 5C 0D 0A 0D 0A 7C 73 69 67 6E 65
64 7C 34 30 66 33 64 30 36 38 39 61 62 66 32 61
63 34 36 36 65 33 30 62 62 30 62 34 30 37 31 39
61 31 5C 66 69 6E 61 6C 5C
\bm\2\f\47577677
5\msg\....|signe
d|40f3d0689abf2a
c466e30bb0b40719
a1\final\
GameSpy ADDBUDDY_REQUEST Record received at login
5C 62 6D 5C 32 5C 66 5C 34 37 35 37 37 36 37 37
35 5C 64 61 74 65 5C 31 33 39 34 39 32 33 38 36
32 5C 6D 73 67 5C 0D 0A 0D 0A 7C 73 69 67 6E 65
64 7C 34 30 66 33 64 30 36 38 39 61 62 66 32 61
63 34 36 36 65 33 30 62 62 30 62 34 30 37 31 39
61 31 5C 66 69 6E 61 6C 5C
\bm\2\f\47577677
5\date\139492386
2\msg\....|signe
d|40f3d0689abf2a
c466e30bb0b40719
a1\final\
GameSpy AUTHADD Record and concatenated ADDBUDDY Record
5C 61 75 74 68 61 64 64 5C 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 66 72 6F 6D
70 72 6F 66 69 6C 65 69 64 5C 34 37 35 37 37 36
37 37 35 5C 73 69 67 5C 34 30 66 33 64 30 36 38
39 61 62 66 32 61 63 34 36 36 65 33 30 62 62 30
62 34 30 37 31 39 61 31 5C 66 69 6E 61 6C 5C 5C
61 64 64 62 75 64 64 79 5C 5C 73 65 73 73 6B 65
79 5C 32 31 30 33 37 35 33 30 33 5C 6E 65 77 70
72 6F 66 69 6C 65 69 64 5C 34 37 35 37 37 36 37
37 35 5C 72 65 61 73 6F 6E 5C 5C 66 69 6E 61 6C
5C
\authadd\\sesske
y\210375303\from
profileid\475776
775\sig\40f3d068
9abf2ac466e30bb0
b40719a1\final\\
addbuddy\\sesske
y\210375303\newp
rofileid\4757767
75\reason\\final
\
GameSpy ADDBUDDY_AUTHORIZED Record
5C 62 6D 5C 31 5C 66 5C 34 37 35 37 37 36 37 37
35 5C 6D 73 67 5C 49 20 68 61 76 65 20 61 75 74
68 6F 72 69 7A 65 64 20 79 6F 75 72 20 72 65 71
75 65 73 74 20 74 6F 20 61 64 64 20 6D 65 20 74
6F 20 79 6F 75 72 20 6C 69 73 74 5C 66 69 6E 61
6C 5C
\bm\1\f\47577677
5\msg\I have aut
horized your req
uest to add me t
o your list\fina
l\
GameSpy ERROR 1539 Record
5C 65 72 72 6F 72 5C 5C 65 72 72 5C 31 35 33 39
5C 65 72 72 6D 73 67 5C 54 68 65 20 70 72 6F 66
69 6C 65 20 72 65 71 75 65 73 74 65 64 20 69 73
20 61 6C 72 65 61 64 79 20 61 20 62 75 64 64 79
2E 5C 66 69 6E 61 6C 5C
\error\\err\1539
\errmsg\The prof
ile requested is
 already a buddy
.\final\
GameSpy DELBUDDY Record
5C 64 65 6C 62 75 64 64 79 5C 5C 73 65 73 73 6B
65 79 5C 32 31 30 33 37 35 33 30 33 5C 64 65 6C
70 72 6F 66 69 6C 65 69 64 5C 32 37 35 37 37 36
37 37 35 5C 66 69 6E 61 6C 5C
\delbuddy\\sessk
ey\210375303\del
profileid\275776
775\final\

Announcing and Joining Battles/Trades

Announcing Battles/Trades

If the server knows Bob to be on Alice's Pal Pad (because she has previously successfully marked him as a friend and completed the process by receiving an ADDBUDDY_AUTHORIZED record), Alice will receive a BUDDY_STATUS record for Bob from the server every time she logs in or whenever Bob changes his status using a STATUS record while she is logged in. These messages will be sent until Alice has completed the removal of Bob from her Pal Pad by sending a DELBUDDY record to the server. Bob may remove Alice from his Pal Pad, but Alice will continue to receive BUDDY_STATUS records regarding his activity following any DELBUDDY record he may send.

The BUDDY_STATUS records include all information sent in the corresponding STATUS record that generated them. This usually includes the locstring, which appears to contain the most significant information about the status of the player, including the player's sprite and the player's status (e.g. requesting a trade, requesting a single battle, etc.). However, the server need not be aware of how this string is interpreted, but must only be able to save this state to be echoed to any buddy of the player who logs in.

Any user who changes their status (e.g. from standby to requesting a trade, cancelling such a request, etc.) will send a new STATUS(6) record with an updated locstring indicating the change in status.

When a player sends a LOGOUT record, a BUDDY_STATUS record indicating that the buddy logged out is sent to anyone logged in who has the player marked as a buddy. While logged out, a shorter logged out BUDDY_STATUS record will be sent at login time to any buddy who logs in while the user is logged out.

Format of locstring

The value of locstring for Pokémon games is not well understood, but it appears to be two separate base64 strings, the first 40 bytes long, the second 8 bytes long, concatenated together. In both encodings, the "=" character appears to be replaced by "-".

The initial bytes appear to be a string of 16-bit little-endian integers and may represent some encoding of the player's name, but this does not appear to agree with any known text encoding for the Pokémon games.

Byte 27 (i.e. the 28th byte) indicates the current status of the user and what, if anything, the user is requesting to do. The known values for this byte include:

Byte 29 (i.e. the 30th byte) is optional (it may be missing if the first base64 string terminates early with a "-" character). If present, it indicates the trainer sprite displayed in the Pal Pad (or, presumably, in Platinum, HeartGold, and SoulSilver, the sprite displayed in the Wi-Fi Club lobby). Known values of this byte include:

It is very probable that other values of this byte value match the byte values used in the ActionReplay code which changes the character's sprite in Pokémon Pearl and HeartGold/SoulSilver, as the above four values match those in the code lists.

It is also probable that the trainer ID (or at least the last several binary digits of it) and gender is encoded in the base64 string to allow for the default sprite to be calculated if byte 29 is missing (unfortunately, the algorithm for deriving the default avatar is no longer posted on MetalKid's website). For example, in the locstring value above, "lQGCAe0B9ABjAWMBAAAAAAAAAAAAAAAAAAEBEAD-AAAAAQEA", the player is displayed as a School Kid ♂, which is the default for a male trainer with trainer ID (00000).

Joining Battles/Trades

Upon seeing a request for a battle or trade in a friend's locstring, a user may accept the request to start the battle or trade. This is done as follows (assuming that Alice is announcing, and Bob is accepting the announcement):

  1. A STATUS(6) record is sent by Bob to the server with the locstring changed in state to match the request (i.e. if Alice is requesting a trade, Bob will send a STATUS(6) record with the request byte set to 0x08 "Trading..."). The locstring should remain set as before. The server should send a BUDDY_STATUS record to Alice containing this change.
  2. A STATUS(1) record is sent by Bob to the server with the same locstring, but an empty statstring. The server should send a BUDDY_STATUS record to Alice containing this change.
  3. A STATUS(5) record is sent by Bob to the server with the same locstring and empty statstring. The server should send a BUDDY_STATUS record to Alice containing this change.
  4. A PEER_MESSAGE record should be sent by Bob to the server to notify Alice that Bob is accepting the offer to trade. This PEER_MESSAGE record should then be forwarded to Alice.
  5. Alice will respond with a PEER_MESSAGE record signalling that she agrees to trade with Bob. This PEER_MESSAGE record is again forwarded from the server to Bob.
  6. Bob will then connect to pokemondpds.ms4.gs.nintendowifi.net TCP port 28910 and immediately send a FIND_PEER message.
  7. pokemondpds.ms4.gs.nintendowifi.net will respond with an encrypted FOUND_PEER message. This message is encrypted using the "enctypeX" encryption described by Luigi Auriemma with the 8-byte validation string defined in the FIND_PEER message and the 6-byte game key (1vTlwb for Pokémon Diamond and Pearl).
  8. Bob will then send a START_CONNECTION message to pokemondpds.ms4.gs.nintendowifi.net, which will forward the payload of the message to Alice over her GameSpy UDP connection to pokemondpds.master.gs.nintendowifi.net as a REQUEST_CONNECTION datagram.
  9. If necessary (perhaps when the public IP and local IP differ and both Bob and Alice do not share the same public IP), after sending the START_CONNECTION message, Bob will connect to pokemondpds.natneg1.gs.nintendowifi.net and pokemondpds.natneg2.gs.nintendowifi.net on UDP port 27901 to perform NAT negotiation for Alice to establish a direct connection to Bob as follows:
    1. Bob will make one UDP connection to pokemondpds.natneg1.gs.nintendowifi.net and two connections to pokemondpds.natneg2.gs.nintendowifi.net, labeled 0, 1, and 2. (Bob may reuse the previous UDP socket to pokemondpds.master.gs.nintendowifi.net for the first connection, and create a second UDP socket to use for both connections to natneg2)
    2. On each connection, Bob will send a NATNEG_CLIENT_HELLO datagram identifying the connection.
    3. The server will respond to each NATNEG_CLIENT_HELLO datagram with an appropriate NATNEG_SERVER_HELLO datagram.
    4. Presumably once Alice has similarly sent NATNEG_CLIENT_HELLO datagrams to the server, natneg2 will send a NATNEG_COMPLETE datagram to both Bob and Alice including the IP and port (apparently, the port that was used to contact natneg1) of the other party.
    5. Once the NATNEG_COMPLETE datagram has been received, Bob will send a NATNEG_ACKNOWLEDGE datagram acknowledging his receipt of the previous NATNEG_COMPLETE datagram.
    6. A direct connection between Alice and Bob will then be established using the IP and UDP port specified in the NATNEG_COMPLETE datagrams and will be tested as Bob sends a NATNEG_PING_TEST datagram to Alice directly, which will then be responded to by a NATNEG_PING_ECHO datagram.
    7. Upon receipt of the first NATNEG_PING_ECHO datagram, Bob will send a second NATNEG_PING_TEST datagram and expect a second NATNEG_PING_ECHO response from Alice.
    8. Once both echoes have been received, the direct connection continues using (presumably) a game-specific protocol.
  10. Similarly, if she finds it necessary, after receiving the REQUEST_CONNECTION datagram, Alice will connect to pokemondpds.natneg1.gs.nintendowifi.net and pokemondpds.natneg2.gs.nintendowifi.net to work around NAT issues using the same protocol. Whether or not she does so, Alice will subsequently respond to pokemondpds.master.gs.nintendowifi.net over UDP with an ACKNOWLEDGE_REQUEST datagram.
  11. Following receipt of the REQUEST_CONNECTION datagram, Alice will attempt to make a direct connection to Bob up to 6 times, once a second. If, after 6 seconds, Bob fails to have been contacted by Alice, Bob will send another START_CONNECTION message to the server (which will again be sent to Alice, causing Alice to try to contact Bob 6 more times). Bob will try to get Alice to contact him up to 6 times.
  12. If, after 36 seconds (6 START_CONNECTION messages × 6 attempts to connect to Bob), Alice fails to make a connection to Bob, she will send a STATUS(1) records with her original request for trades and an empty statstring, followed by a STATUS(1) record with a standby status and an empty statstring, followed by a STATUS(6) record with the same (standby) status and empty statstring, followed, finally, by a STATUS(1) record with the same (standby) status and empty statstring, and a STATUS(6) record with the restored statstring. If Bob sees these statuses, he will send a STATUS(1) record with his previous status, followed by one in which he is in standby status. Finally, he will send one STATUS(6) record restoring the statstring. Alice's game will then say "Bob failed to respond." If this happens, Bob will not actually close the TCP connection to pokemondpds.ms4.gs.nintendowifi.net! pokemondpds.ms4.gs.nintendowifi.net may send KEEPALIVE messages every 180 seconds, although it is unclear if these records are the string {0x4C, 0xA4, 0x97}, or a continuation of the previous encrypted stream ({0xB0, 0x9A, 0x2C}), to which the client appears to respond {0x00, 0x03, 0x03}
  13. If, however, Alice successfully started her peer-to-peer connection with Bob, Bob will close the connection to pokemondpds.ms4.gs.nintendowifi.net.
  14. Bob will then send a STATUS(2) record notifying Alice and his other buddies that he is presently in a trade.
  15. Alice will then send a STATUS(6) message updating her statstring to /SCM/2/SCN/2/VER/3 instead of /SCM/2/SCN/1/VER/3
  16. Alice will also send a second STATUS(6) message updating her locstring to also indicate that she is now in the middle of a trade (i.e. the request byte will become 0x08)
  17. Finally, Alice will then send an updated HEARTBEAT datagram to the master UDP server, indicating a statechange event.
  18. In Bob's next HEARTBEAT datagram to the master server, he will indicate his profile ID in the dwc_mresv field and a dwc_mtype value of 3.
  19. When Bob leaves the trade, he will send another HEARTBEAT datagram with a statechange event, followed by a STATUS(1) message (no changes to statstring or locstring) and a STATUS(6) message (restoring statstring's value).
  20. Alice will also send a STATUS(1) message (with empty statstring) and STATUS(6) message (with restored /SCM/2/SCN/1/VER/3 statstring)
  21. Finally, Alice, and then Bob, will send STATUS(6) messages restoring their standby state.
GameSpy BUDDY_STATUS Record
5C 62 6D 5C 31 30 30 5C 66 5C 34 37 35 34 37 35
39 35 36 5C 6D 73 67 5C 7C 73 7C 36 7C 73 73 7C
2F 53 43 4D 2F 32 2F 53 43 4E 2F 31 2F 56 45 52
2F 33 7C 6C 73 7C 6C 51 47 43 41 65 30 42 39 41
42 6A 41 57 4D 42 41 41 41 41 41 41 41 41 41 41
41 41 41 41 41 41 41 41 45 42 45 41 44 2D 41 41
41 41 41 51 45 41 7C 69 70 7C 31 36 37 37 37 32
31 36 36 7C 70 7C 30 7C 71 6D 7C 30 5C 66 69 6E
61 6C 5C
\bm\100\f\475475
956\msg\|s|6|ss|
/SCM/2/SCN/1/VER
/3|ls|lQGCAe0B9A
BjAWMBAAAAAAAAAA
AAAAAAAAEBEAD-AA
AAAQEA|ip|167772
166|p|0|qm|0\fin
al\

NOTE: Unlike the other examples in this section, this is not one which would be received by profile ID 475475956, but rather, is an example of a BUDDY_STATUS record that might be sent to 475475956's buddies as a result of this STATUS(6) record.

GameSpy BUDDY_STATUS Record sent following logout
5C 62 6D 5C 31 30 30 5C 66 5C 34 37 35 37 37 36
37 37 35 5C 6D 73 67 5C 7C 73 7C 30 7C 73 73 7C
4F 66 66 6C 69 6E 65 7C 6C 73 7C 7C 69 70 7C 31
36 37 37 37 32 31 36 36 7C 70 7C 30 7C 71 6D 7C
30 5C 66 69 6E 61 6C 5C
\bm\100\f\475776
775\msg\|s|0|ss|
Offline|ls||ip|1
67772166|p|0|qm|
0\final\
GameSpy BUDDY_STATUS Record sent while logged out
5C 62 6D 5C 31 30 30 5C 66 5C 34 37 35 37 37 36
37 37 35 5C 6D 73 67 5C 7C 73 7C 30 7C 73 73 7C
4F 66 66 6C 69 6E 65 5C 66 69 6E 61 6C 5C
\bm\100\f\475776
775\msg\|s|0|ss|
Offline\final\
GameSpy PEER_MESSAGE Record requesting to accept an offer to trade

TODO

GameSpy forwarded PEER_MESSAGE Record requesting to accept an offer to trade

TODO

NOTE: This is the counterpart to the previous record, sent from the server to the game currently offering to trade. Note that the t field has been rewritten as the f field.

GameSpy PEER_MESSAGE Record accepting a request to accept the offer to trade

TODO

NOTE: Though not depicted here, there is, of course, a forwarded copy of this message that is sent to the recipient in the t field.

GameSpy PEER_MESSAGE Record accepting a request to accept the offer to trade

TODO

NOTE: Though not depicted here, there is, of course, a forwarded copy of this message that is sent to the recipient in the t field.

GameSpy FIND_PEER Message

TODO

GameSpy FOUND_PEER Message

TODO

NOTE: This message is normally encrypted using the "enctypeX" encryption described by Luigi Auriemma with the 8-byte validation string defined in the previous FIND_PEER message and the 6-byte game key (1vTlwb for Pokémon Diamond and Pearl). It has been decrypted here to show the contents of the message.

GameSpy START_CONNECTION Message

TODO

NOTE: For unknown reasons, this message is typically divided into two TCP packets, where the header is sent in the first packet and the body in the second.

GameSpy REQUEST_CONNECTION Datagram

TODO

GameSpy NATNEG_CLIENT_HELLO Datagram

TODO

GameSpy NATNEG_SERVER_HELLO Datagram

TODO

GameSpy NATNEG_COMPLETE Datagram

TODO

GameSpy NATNEG_ACKNOWLEDGE Datagram

TODO

GameSpy NATNEG_PING_TEST Datagram

TODO

GameSpy NATNEG_PING_ECHO Datagram

TODO

GameSpy ACKNOWLEDGE_REQUEST Datagram

TODO

GameSpy HEARTBEAT Datagram of active trading host

TODO

GameSpy HEARTBEAT Datagram of active trading client

TODO

GameSpy HEARTBEAT Datagram of client leaving trade

TODO

Changes in Pokémon Platinum

While Pokémon Platinum's use of Nintendo Wi-Fi Connection is fundamentally similar to that of Diamond and Pearl (as one would expect given its interoperability with the original games), several new features are introduced in Platinum that differ from its predecessors:

Wi-Fi Plaza

The Wi-Fi Plaza is a feature (unique to Platinum?) which makes use of the GameSpy Peerchat protocol. This protocol is an encrypted form of IRC, the encryption of which has been documented by Luigi Auriemma.

(TODO: But how does it work?)

Incomplete notes to be expanded on:

Appears to log in to the TCP server, and, once empty STATUS(1) is sent, connect to pokemonplatds.peerchat.gs.nintendowifi.net

Upon encrypted login, connect to gamestats2.gs.nintendowifi.net, get /pokemondpds/web/enc/lobby/checkProfile.asp?pid=[pid], get back a token.

Then in a separate connection, get /pokemondpds/web/enc/lobby/checkProfile.asp?pid=[pid]&hash=[hash] containing unknown bytestream. This appears to mirror the challenge/response framework of GTS, but with the secret token uLMOGEiiJogofchScpXb instead of sAdeqWo3voLeC5r16DYv. It also probably requires the server to sign all responses like Gen V (again, with the secret token uLMOGEiiJogofchScpXb.

Then in a separate connection, get /pokemondpds/web/enc/lobby/getSchedule.asp?pid=[pid]&hash=[hash] containing unknown bytestream.

Then in a separate connection, get /pokemondpds/web/enc/lobby/getVIP.asp?pid=[pid]&hash=[hash] containing unknown bytestream.

Then in a separate connection, get /pokemondpds/web/enc/lobby/getQuestionnaire.asp?pid=[pid]&hash=[hash] containing unknown bytestream.

Remaining communications use pokemonplatds.peerchat.gs.nintendowifi.net

May use pokemondpds.ms4.gs.nintendowifi.net to negotiate P2P Plaza games

Battle Subway (Gen V)

All Battle Subway data is (probably) handled like the Battle Tower in Gen IV, making use of the gamestats2.gs.nintendowifi.net HTTP server. That is,

  1. The game connects to the GameSpy servers [to be written for Gen V]
  2. The game then requests a service token from nas.nintendowifi.net with "SVCLOC" action and a svc of "0000".
  3. The game fetches /syachi2ds/web/battletower/info.asp?pid=[pid] from gamestats2.gs.nintendowifi.net to get the challenge token
  4. The game then fetches /syachi2ds/web/battletower/info.asp?pid=[pid]&hash=[response-hash]&data=[data] from gamestats2.gs.nintendowifi.net with the appropriate response hash []. Data is in the standard gamestats2 wrapper with the checksum XOR value 0x2db542b2 (big-endian), with an unknown payload of two longs (the second of which is 0x00000000. The first may be a common nonce value (perhaps trainer ID?) that is shared between all Battle Subway connections).
  5. The response contains the standard response payload wrapper (i.e. with a trailing hash) and the payload 0x0001 (little-endian, unknown meaning)
  6. The game then makes a POST to nas.nintendowifi.net/pr
  7. The game then fetches /syachi2ds/web/common/setProfile.asp?pid=[pid], presumably following with a standard setProfile call.
  8. The game then fetches /syachi2ds/web/battletower/roomnum.asp?pid=[pid] with a standard challenge-response. The second request contains the payload ([unknown long], [rank number as byte, starting at 0?], 0x00000000)
  9. Common data structures (all multi-byte values little-endian):

    struct btlsub_pokemon {
        unsigned short species_id;
        unsigned short held_item_id;
        unsigned short move_id[4];
        unsigned short original_trainer_id;
        unsigned short original_trainer_secret_id;
        unsigned long personality_id;
        unsigned long packed_ivs;  /* packed 5-bits a piece, the following IV values from least-significant-bit to most-significant-bit: HP, Attack, Defense, Speed, SP Attack, SP Defense. Two most significant bits are 0. */
        unsigned char hp_ev;
        unsigned char attack_ev;
        unsigned char defense_ev;
        unsigned char speed_ev;
        unsigned char sp_attack_ev;
        unsigned char sp_defense_ev;
        unsigned char unknown;  /* to analyze */
        unsigned char original_language;  /* see Project Pokemon */
        unsigned char ability;
        unsigned char friendship;
        wchar_t name[11];  /* Terminated with 0xFFFF. Contents of unused string data undefined. */
        unsigned long zero;  /* unused, always 0x00000000? */
    };

    struct btlsub_trainer_record {
        wchar_t name[8];  /* Terminated with 0xFFFF. Contents of unused string data undefined. */
        unsigned char unknown;  /* to analyze */
        unsigned char language;  /* see Project Pokemon */
        unsigned char country;  /* TODO: create list */
        unsigned char province;  /* 0x00 if no provinces known for country. TODO: create list */
        unsigned short trainer_id;
        unsigned short trainer_secret_id;
        unsigned short unknown2;  /* to analyze */
        unsigned short record_text_pattern_id;  /* TODO: create list */
        unsigned short record_text_keyword_id;  /* TODO: create list */
        unsigned short unknown3;  /* always 0xFFFF? second keyword ID? */
        unsigned char gender;  /* 0x00 - Male, 0x02 - Female */
        unsigned char unknown4;  /* to analyze */
    };

    struct btlsub_trainer {
        struct btlsub_pokemon pokemon[3];
        struct btlsub_trainer_record trainer;
        unsigned char unknown[26];  /* to analyze */
    };

    The contents of download.asp:

    struct btlsub_download {
        struct btlsub_trainer trainers[7];  /* Encountered in order(?). Final entry is current record holder. */
        struct btlsub_trainer_record successive_records[30];  /* Displayed in order */
        char hash[40];
    };

    The payload of validate:

    struct btlsub_validate {
        char base64[88];  /* unknown base64 id, to analyze */
        unsigned char zero;  /* always 0x00? */
        unsigned short four_hundred;  /* always 0x0400? */
        struct btlsub_pokemon pokemon[3];  /* the three pokemon to be submitted as part of the trainer's pokemon in upload.asp */
    };

    The payload of upload.asp:

    struct btlsub_upload {
        unsigned long unknown;  /* to analyze */
        unsigned long size;  /* size of remaining payload after these first 8 bytes */
        struct btlsub_trainer trainer;
        unsigned char unknown2[16];  /* to analyze */
        unsigned char validation_signature[128];  /* signature received from pkvldtprod.nintendo.co.jp */
        unsigned long size2;  /* size of signature(?) 0x00000080? */
    };