diff --git a/SourceQuery/BaseSocket.php b/SourceQuery/BaseSocket.php index ce35bc2..7ef4d8d 100644 --- a/SourceQuery/BaseSocket.php +++ b/SourceQuery/BaseSocket.php @@ -35,4 +35,93 @@ abstract public function Open( $Ip, $Port, $Timeout, $Engine ); abstract public function Write( $Header, $String = '' ); abstract public function Read( $Length = 1400 ); + + protected function ReadInternal( $Buffer, $SherlockFunction ) + { + if( $Buffer->Remaining( ) === 0 ) + { + throw new InvalidPacketException( 'Failed to read any data from socket', InvalidPacketException::BUFFER_EMPTY ); + } + + $Header = $Buffer->GetLong( ); + + if( $Header === -1 ) // Single packet + { + // We don't have to do anything + } + else if( $Header === -2 ) // Split packet + { + $Packets = []; + $IsCompressed = false; + $ReadMore = false; + + do + { + $RequestID = $Buffer->GetLong( ); + + switch( $this->Engine ) + { + case SourceQuery::GOLDSOURCE: + { + $PacketCountAndNumber = $Buffer->GetByte( ); + $PacketCount = $PacketCountAndNumber & 0xF; + $PacketNumber = $PacketCountAndNumber >> 4; + + break; + } + case SourceQuery::SOURCE: + { + $IsCompressed = ( $RequestID & 0x80000000 ) !== 0; + $PacketCount = $Buffer->GetByte( ); + $PacketNumber = $Buffer->GetByte( ) + 1; + + if( $IsCompressed ) + { + $Buffer->GetLong( ); // Split size + + $PacketChecksum = $Buffer->GetUnsignedLong( ); + } + else + { + $Buffer->GetShort( ); // Split size + } + + break; + } + } + + $Packets[ $PacketNumber ] = $Buffer->Get( ); + + $ReadMore = $PacketCount > sizeof( $Packets ); + } + while( $ReadMore && $SherlockFunction( $Buffer, $Length ) ); + + $Data = Implode( $Packets ); + + // TODO: Test this + if( $IsCompressed ) + { + // Let's make sure this function exists, it's not included in PHP by default + if( !Function_Exists( 'bzdecompress' ) ) + { + throw new \RuntimeException( 'Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.' ); + } + + $Data = bzdecompress( $Data ); + + if( CRC32( $Data ) !== $PacketChecksum ) + { + throw new InvalidPacketException( 'CRC32 checksum mismatch of uncompressed packet data.', InvalidPacketException::CHECKSUM_MISMATCH ); + } + } + + $Buffer->Set( SubStr( $Data, 4 ) ); + } + else + { + throw new InvalidPacketException( 'Socket read: Raw packet header mismatch. (0x' . DecHex( $Header ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); + } + + return $Buffer; + } } diff --git a/SourceQuery/Socket.php b/SourceQuery/Socket.php index df31def..19ea80f 100644 --- a/SourceQuery/Socket.php +++ b/SourceQuery/Socket.php @@ -73,89 +73,7 @@ $Buffer = new Buffer( ); $Buffer->Set( FRead( $this->Socket, $Length ) ); - if( $Buffer->Remaining( ) === 0 ) - { - throw new InvalidPacketException( 'Failed to read any data from socket', InvalidPacketException::BUFFER_EMPTY ); - } - - $Header = $Buffer->GetLong( ); - - if( $Header === -1 ) // Single packet - { - // We don't have to do anything - } - else if( $Header === -2 ) // Split packet - { - $Packets = []; - $IsCompressed = false; - $ReadMore = false; - - do - { - $RequestID = $Buffer->GetLong( ); - - switch( $this->Engine ) - { - case SourceQuery::GOLDSOURCE: - { - $PacketCountAndNumber = $Buffer->GetByte( ); - $PacketCount = $PacketCountAndNumber & 0xF; - $PacketNumber = $PacketCountAndNumber >> 4; - - break; - } - case SourceQuery::SOURCE: - { - $IsCompressed = ( $RequestID & 0x80000000 ) !== 0; - $PacketCount = $Buffer->GetByte( ); - $PacketNumber = $Buffer->GetByte( ) + 1; - - if( $IsCompressed ) - { - $Buffer->GetLong( ); // Split size - - $PacketChecksum = $Buffer->GetUnsignedLong( ); - } - else - { - $Buffer->GetShort( ); // Split size - } - - break; - } - } - - $Packets[ $PacketNumber ] = $Buffer->Get( ); - - $ReadMore = $PacketCount > sizeof( $Packets ); - } - while( $ReadMore && $this->Sherlock( $Buffer, $Length ) ); - - $Data = Implode( $Packets ); - - // TODO: Test this - if( $IsCompressed ) - { - // Let's make sure this function exists, it's not included in PHP by default - if( !Function_Exists( 'bzdecompress' ) ) - { - throw new \RuntimeException( 'Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.' ); - } - - $Data = bzdecompress( $Data ); - - if( CRC32( $Data ) !== $PacketChecksum ) - { - throw new InvalidPacketException( 'CRC32 checksum mismatch of uncompressed packet data.', InvalidPacketException::CHECKSUM_MISMATCH ); - } - } - - $Buffer->Set( SubStr( $Data, 4 ) ); - } - else - { - throw new InvalidPacketException( 'Socket read: Raw packet header mismatch. (0x' . DecHex( $Header ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); - } + $this->ReadInternal( $Buffer, $this->Sherlock ); return $Buffer; }