<?php
#################################################################################
## GameSpy PHP Masterserver Class v1.1
##
## Written by:
##  Thomas Reiser alias FiRe^
##  <www.mg34.net/code - fire_1@gmx.de>
##
## Orginal code by:
##  Luigi Auriemma
##  <aluigi.altervista.org - aluigi@autistici.org>
##
## LICENSE:
##  This program is free software; you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation; either version 2 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
##
##  http://www.gnu.org/licenses/gpl.txt
##
## NOTE:
##  Please include our copyrights and a link to this source file
##  to your website! Thank you.
#################################################################################

class GameSpy {
    var 
$Host;
    var 
$Port;
    var 
$Sock;

    function 
GameSpy($Host "master.gamespy.com"$Port 28900) {
        
/*
            --Constructor--
            $Host: GameSpy-Master IP
            $Port: GameSpy-Master Port
        */
        
$this->Host $Host;
        
$this->Port $Port;
    }

    function 
GetServers($Gamename$Handoff$Filter "") {
        
/*
            This function will return the Serverlist for $Gamename in an Array.
            $Gamename: (http://motd.gamespy.com/software/services/index.aspx)
            $Handoff: (http://motd.gamespy.com/software/services/index.aspx?mode=full&services=$Gamename)
                      If the Handoff is >= 13 chars, it will be sized to an 6-char Handoff
            $Filter: The GS-Filter

            Example:
            Array
                (
                        [0] => Array
                        (
                            [ip] => 69.44.61.202
                            [port] => 23000
                        )
                        [1] => Array
                        (
                            [ip] => 195.140.135.250
                            [port] => 23000
                        )
                        ...
                )
        */

        // Try to connect to the GameSpy-Master:
        
$this->Sock = @fsockopen("tcp://$this->Host"$this->Port, &$errno, &$errstr6);
        @
stream_set_timeout($this->Sock6);

        if(!
$this->Sock) { // Error while connecting
            
return false// :-(
        
} else {
            
$this->Connected true;
        }

        
// Receive the Secure-Key:
        
$SecureKey substr(fgets($this->Sock256), -76);

        
// Create the Validate-Key:
        
$ValidateKey $this->__MakeValidate($SecureKey$Handoff);

        
// Send the packet
        
$Packet "\\gamename\\$Gamename\\enctype\\0\\validate\\$ValidateKey\\final\\" .
                  
"\\queryid\\1.1\\list\\cmp\\gamename\\$Gamename\\where\\$Filter\\final\\";
        
fwrite($this->Sock$Packet);

        
// Receive the (compressed) Serverlist:
        
$Data "";
        do {
            
$Data .= fgets($this->Sock2048);
            
$Buffer socket_get_status($this->Sock);
        } while(
$Buffer["unread_bytes"] > 0);

        
fclose($this->Sock); // Close socket

        
return $this->__ParseCompressedIPs($Data); // Return the parsed Serverlist
    
}

    function 
__ParseCompressedIPs($Data) {
        
/*
            Parse the 6-Byte-IP packet and return the Serverlist-Array
            $Data: Compressed IPs received from the Server
        */

        
$UnComp = array();
        
$i 0;
        
$c 0;

        
// Remove the "\final\":
        
$Data str_replace("\\final\\"""$Data);

        while(
$i <= strlen($Data) - 6) {
            
// IP
            
$UnComp[$c]["ip"] = ord($Data{$i++}).".".ord($Data{$i++}).".".ord($Data{$i++}).".".ord($Data{$i++});
            
// Port
            
$UnComp[$c++]["port"] = (ord($Data{$i++}) * 256) + ord($Data{$i++});
        }
        
        return 
$UnComp;
    }

    function 
__MakeValidate($SecureKey$Handoff) {
        
/*
            MakeValidate creates a Validate-Key for you :D
            $SecureKey: The Secure-Key received from the GS Master
            $Handoff: See GetServers()
        */

        
$Temp = array(0000$Handoff); // Array for some temporary Variables
        
$Table = array();

        for(
$i 0$i <= 255$i++) {
            
$Table[$i] = $i// Fill the buffer
        
}

        
// Check the Handoff-Length:
        
if(strlen($Handoff) >= 13) {
            
$Handoff "";
            
// Invalid Handoff! -> Need 6-char Handoff
            
for($i 2$i <= 13$i += 2) {
                
$Handoff .= $Temp[4]{$i};
            }
        }

        
// Add the length of the Keys to the array:
        
$Length = array(strlen($Handoff), strlen($SecureKey));

        for(
$i 0$i <= 255$i++) {
            
// Scramble the Table with the Handoff:
            
$Temp[0] = ($Temp[0] + $Table[$i] + ord($Handoff{$i $Length[0]})) & 255;
            
$Temp[1] = $Table[$Temp[0]];

            
// Update the buffer:
            
$Table[$Temp[0]] = $Table[$i];
            
$Table[$i] = $Temp[1];
        }

        
$Temp[0] = 0;
        
$Key = array();

        
// Scramble the SecureKey with the Table:
        
for($i 0$i $Length[1]; $i++) {
            
// Add the next char to the Array
            
$Key[$i] = ord($SecureKey{$i});

            
$Temp[0] = ($Temp[0] + $Key[$i] + 1) & 255;
            
$Temp[1] = $Table[$Temp[0]];
            
$Temp[2] = ($Temp[2] + $Temp[1]) & 255;
            
$Temp[3] = $Table[$Temp[2]];

            
$Table[$Temp[2]] = $Temp[1];
            
$Table[$Temp[0]] = $Temp[3];

            
// XOR the Key with the Buffer:
            
$Key[$i] ^= $Table[($Temp[1] + $Temp[3]) & 255];
        }

        
$Length[1] /= 3;
        
$i 0;
        
$ValidateKey "";

        
// Create the ValidateKey:
        
while($Length[1]--) {
            
$Temp[1] = $Key[$i++];
            
$Temp[3] = $Key[$i++];

            
$ValidateKey .= $this->__AddChar($Temp[1] >> 2);
            
$ValidateKey .= $this->__AddChar((($Temp[1] & 3) << 4) | ($Temp[3] >> 4));

            
$Temp[1] = $Key[$i++];

            
$ValidateKey .= $this->__AddChar((($Temp[3] & 15) << 2) | ($Temp[1] >> 6));
            
$ValidateKey .= $this->__AddChar($Temp[1] & 63);
        }

        return 
$ValidateKey;
    }

    function 
__AddChar($Number) {
        
// Return a new char
        
if($Number 26) {
            return 
chr($Number 65);
        } elseif(
$Number 52) {
            return 
chr($Number 71);
        } elseif(
$Number 62) {
            return 
chr($Number 4);
        } elseif(
$Number == 62) {
            return 
"+";
        } elseif(
$Number == 63) {
            return 
"/";
        }
    }
}
?>