From 74df6f1ea7e6b2ea4120dc7b3c8f82f2e8374dae Mon Sep 17 00:00:00 2001 From: polaris Date: Sun, 30 Mar 2014 08:37:52 -0400 Subject: [PATCH] Documentation, tidying up - Moved some gamespy-related utility functions to where they should've been. - Added more documentation. --- gamespy/gs_database.py | 3 +-- gamespy/gs_utility.py | 25 ++++++++++++++++++++++--- gamespy_server.py | 29 ++++++++++++++++++++++------- master_server.py | 29 +++++++++++++++++++++++++---- other/utils.py | 22 ---------------------- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/gamespy/gs_database.py b/gamespy/gs_database.py index 25f8a36..d87ded5 100644 --- a/gamespy/gs_database.py +++ b/gamespy/gs_database.py @@ -3,7 +3,6 @@ import hashlib import itertools import other.utils as utils - class GamespyDatabase(object): def __init__(self, filename='gpcm.db'): self.conn = sqlite3.connect(filename) @@ -35,7 +34,7 @@ class GamespyDatabase(object): c = self.conn.cursor() c.execute("SELECT max(profileid) FROM users") - r = c.fetchone() + r = self.get_dict(c.fetchone()) profileid = 476639431 #100000000 if r != None and r['max(profileid)'] != None: diff --git a/gamespy/gs_utility.py b/gamespy/gs_utility.py index c7a68e8..c352aac 100644 --- a/gamespy/gs_utility.py +++ b/gamespy/gs_utility.py @@ -1,7 +1,6 @@ -from ctypes import c_uint -import sys import base64 import hashlib +import other.utils as utils # GameSpy uses a slightly modified version of base64 which replaces +/= with []_ def base64_encode(input): @@ -64,4 +63,24 @@ def generate_proof(challenge, ac_challenge, secretkey, authtoken): md5_2.update(output) return md5_2.hexdigest() - \ No newline at end of file + +# Code: Tetris DS @ 02057A14 +def get_friendcode_from_profileid(profileid, gameid): + friendcode = 0 + + # Combine the profileid and gameid into one buffer + buffer = [(profileid >> (8 * i)) & 0xff for i in range(4)] + buffer += [ord(c) for c in gameid] + + crc = utils.calculate_crc8(buffer) + + # The upper 32 bits is the crc8 of the combined buffer. + # The lower 32 bits of the friend code is the profileid. + friendcode = ((crc & 0x7f) << 32) | profileid + + return friendcode + +def get_profileid_from_friendcode(friendcode): + # Get the lower 32 bits as the profile id + profileid = friendcode & 0xffffffff + return profileid \ No newline at end of file diff --git a/gamespy_server.py b/gamespy_server.py index e493601..f642da3 100644 --- a/gamespy_server.py +++ b/gamespy_server.py @@ -45,8 +45,6 @@ while 1: commands = gs_query.parse_gamespy_message(data) for data_parsed in commands: - msg_d = [] - print data_parsed if data_parsed['__cmd__'] == "login": @@ -79,6 +77,7 @@ while 1: sesskey = db.create_session(profileid) + msg_d = [] msg_d.append(('__cmd__', "lc")) msg_d.append(('__cmd_val__', "2")) msg_d.append(('sesskey', sesskey)) @@ -91,9 +90,9 @@ while 1: msg = gs_query.create_gamespy_message(msg_d) elif data_parsed['__cmd__'] == "getprofile": - msg_d = [] - profile = db.get_profile_from_session_key(data_parsed.append(('sesskey'))) + + msg_d = [] msg_d.append(('__cmd__', "pi")) msg_d.append(('__cmd_val__', "")) msg_d.append(('profileid', profile['profileid'])) @@ -112,7 +111,11 @@ while 1: elif data_parsed['__cmd__'] == "updatepro": # Handle properly later - # Assume that there will be other parameters besides lastname, so make it a loop or something along those lines later + # Assume that there will be other parameters besides lastname, so make it a loop or something along those lines later. + # + # Idea: Make user's actual profile data a dictionary, and serialize that and store it in the database + # instead of making each possible field a column in the table? Such a setup would make the the profile + # more robust. db.update_profile(data_parsed['sesskey'], [("lastname", data_parsed['lastname'])]) elif data_parsed['__cmd__'] == "status": @@ -120,8 +123,7 @@ while 1: msg = "" elif data_parsed['__cmd__'] == "ka": - # Unknown - + # Keep alive msg = "" elif data_parsed['__cmd__'] == "bm": @@ -151,6 +153,19 @@ while 1: # (CLIENT) \status\2\sesskey\233209064\statstring\\locstring\JZoAAAAAAAAAAAAA-wA*\final\ # (SERVER) \bm\100\f\217936895\msg\|s|6|ss|/SCM/2/SCN/2/VER/3|ls|97YBAAAAAAAAAAAA-wA*|ip|-1405615422|p|0|qm|0\final\ # (SERVER) \bm\100\f\217936895\msg\|s|6|ss|/SCM/2/SCN/1/VER/3|ls|97YBAAAAAAAAAAAA-wA*|ip|-1405615422|p|0|qm|0\final\ + # + # Notes: + # \bm\1 = Message + # \bm\100 = Status + # Check out OpenSpy to find out more \bm request codes: https://github.com/sfcspanky/Openspy-Core/blob/playerspy/gp.h + # + # msg field: + # s = status + # ss = status string + # ls = location string + # ip = signed ip (convert back into hex, take each byte to build x.x.x.x) + # p = port + # qm = ? (According to OpenSpy, "quietflags". See above linked gp.h to get GP_SILENCE_* flags) msg = "" elif data_parsed['__cmd__'] == "logout": diff --git a/master_server.py b/master_server.py index 8135a96..b7b65d5 100644 --- a/master_server.py +++ b/master_server.py @@ -1,9 +1,7 @@ # Server emulator for *.available.gs.nintendowifi.net and *.master.gs.nintendowifi.net import socket -import time import other.utils as utils - def get_game_id(data): game_id = data[5: -1] return game_id @@ -25,19 +23,42 @@ while 1: # - Bytes 2 - 5 from the client is some kind of ID. This will have to be inspected later. I believe it's a # session-like ID because the number changes between connections. Copying the client's ID might be enough. # + # The above was as guessed. + # The code in Tetris DS (overlay 10) @ 0216E974 handles the network command creation. + # R1 contains the command to be sent to the server. + # R2 contains a pointer to some unknown integer that gets written after the command. + # # - Commands + # Commands range from 0x00 to 0x09 (for client only at least?) (Tetris DS overlay 10 @ 0216DDCC) + # # CLIENT: - # 0x01 - Unknown - # 0x03 - Send client state? + # 0x01 - Unknown (Tetris DS overlay 10 @ 216DCA4) + # 0x03 - Send client state? (Tetris DS overlay 10 @ 216DA30) + # Data sent: + # 1) Loop for each localip available on the system, write as localip%d\x00(local ip) + # 2) localport\x00(local port) + # 3) natneg (either 0 or 1) + # 4) ONLY IF STATE CHANGED: statechanged\x00(state) (Possible values: 0, 1, 2, 3) + # 5) gamename\x00(game name) + # 6) ONLY IF PUBLIC IP AND PORT ARE AVAILABLE: publicip\x00(public ip) + # 7) ONLY IF PUBLIC IP AND PORT ARE AVAILABLE: publicport\x00(public port) + # + # if statechanged != 2: + # Write various other data described here: http://docs.poweredbygamespy.com/wiki/Query_and_Reporting_Implementation + # # 0x07 - Unknown, related to server's 0x06 (returns value sent from server) + # + # # 0x08 - Keep alive? # 0x09 - Availability check + # # SERVER: # 0x01 - Unknown # 0x06 - Unknown # 0x0a - Unknown # # - \xfd\xfc commands get passed directly between the other player(s)? + # if [ord(x) for x in recv_data[0:5]] == [0x09, 0x00, 0x00, 0x00, 0x00]: utils.print_log("Received request for '%s' from %s:%s... %s" % ( diff --git a/other/utils.py b/other/utils.py index ad8703a..4fcd5ec 100644 --- a/other/utils.py +++ b/other/utils.py @@ -64,28 +64,6 @@ def base32_decode(str, reverse = False): return orig -# Code: Tetris DS @ 02057A14 -def get_friendcode_from_profileid(profileid, gameid): - friendcode = 0 - - # Combine the profileid and gameid into one buffer - buffer = [(profileid >> (8 * i)) & 0xff for i in range(4)] - buffer += [ord(c) for c in gameid] - - crc = calculate_crc8(buffer) - - # The upper 32 bits is the crc8 of the combined buffer. - # The lower 32 bits of the friend code is the profileid. - friendcode = ((crc & 0x7f) << 32) | profileid - - return friendcode - -def get_profileid_from_friendcode(friendcode): - # Get the lower 32 bits as the profile id - profileid = friendcode & 0xffffffff - return profileid - - # For server logging def print_log(text): print "[%s] %s" % (time.strftime("%c"), text)