[qstat] update to svn382

Tom Callaway spot at fedoraproject.org
Thu Dec 12 20:38:38 UTC 2013


commit 598650f84fbcf11b1312aa2ec9a4029a85dddf5d
Author: Tom Callaway <spot at fedoraproject.org>
Date:   Thu Dec 12 15:38:43 2013 -0500

    update to svn382

 qstat-2.11-20131212svn382.patch |32380 +++++++++++++++++++++++++++++++++++++++
 qstat.spec                      |   11 +-
 2 files changed, 32387 insertions(+), 4 deletions(-)
---
diff --git a/qstat-2.11-20131212svn382.patch b/qstat-2.11-20131212svn382.patch
new file mode 100644
index 0000000..de54a90
--- /dev/null
+++ b/qstat-2.11-20131212svn382.patch
@@ -0,0 +1,32380 @@
+diff -urP qstat-2.11/a2s.c qstat2/a2s.c
+--- qstat-2.11/a2s.c	2006-05-28 06:48:38.000000000 -0500
++++ qstat2/a2s.c	2011-02-04 06:06:01.579727000 -0500
+@@ -12,6 +12,7 @@
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/socket.h>
++#include <arpa/inet.h>
+ #endif
+ #include <stdlib.h>
+ #include <stdio.h>
+@@ -20,13 +21,14 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+ 
+-#define A2S_GETCHALLENGE	  "\xFF\xFF\xFF\xFF\x57"
++#define A2S_GETCHALLENGE		"\xFF\xFF\xFF\xFF\x57"
+ #define A2S_CHALLENGERESPONSE 0x41
+ #define A2S_INFO			  "\xFF\xFF\xFF\xFF\x54Source Engine Query"
+ #define A2S_INFORESPONSE_HL1  0x6D
+ #define A2S_INFORESPONSE_HL2  0x49
+ #define A2S_PLAYER			"\xFF\xFF\xFF\xFF\x55"
+ #define A2S_PLAYERRESPONSE	0x44
++#define A2S_PLAYER_INVALID_CHALLENGE	"\xFF\xFF\xFF\xFF\x55\xFF\xFF\xFF\xFF"
+ #define A2S_RULES			 "\xFF\xFF\xFF\xFF\x56"
+ #define A2S_RULESRESPONSE	 0x45
+ 
+@@ -44,12 +46,14 @@
+ 	unsigned char type;
+ };
+ 
+-void send_a2s_request_packet(struct qserver *server)
++query_status_t send_a2s_request_packet(struct qserver *server)
+ {
+ 	struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+ 
+ 	if(qserver_send_initial(server, A2S_INFO, sizeof(A2S_INFO)) == -1)
+-		goto error;
++	{
++		return DONE_FORCE;
++	}
+ 
+ 	status->sent_info = 1;
+ 	status->type = 0;
+@@ -57,28 +61,34 @@
+ 	if(get_server_rules || get_player_info)
+ 		server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
+ 
+-	return;
+-
+-error:
+-	cleanup_qserver(server, 1);
++	return INPROGRESS;
+ }
+ 
+-void send_a2s_rule_request_packet(struct qserver *server)
++query_status_t send_a2s_rule_request_packet(struct qserver *server)
+ {
+ 	struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+ 
+ 	if(!get_server_rules && !get_player_info)
+ 	{
+-		goto error;
++		return DONE_FORCE;
+ 	}
+ 
+-	do
++	while( 1 )
+ 	{
+ 		if(!status->have_challenge)
+ 		{
+ 			debug(3, "sending challenge");
+-			if(qserver_send_initial(server, A2S_GETCHALLENGE, sizeof(A2S_GETCHALLENGE)-1) == -1)
+-				goto error;
++
++			// Challenge Request was broken so instead we use a player request with an invalid
++			// challenge which prompts the server to send a valid challenge
++			// This was fixed as of the update 2009-08-26
++			//char buf[sizeof(A2S_PLAYER)-1+4] = A2S_PLAYER;
++			//memcpy( buf + sizeof(A2S_PLAYER)-1, &status->challenge, 4 );
++			//if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++			if( SOCKET_ERROR == qserver_send_initial(server, A2S_GETCHALLENGE, sizeof(A2S_GETCHALLENGE)-1) )
++			{
++				return SOCKET_ERROR;
++			}
+ 			status->sent_challenge = 1;
+ 			break;
+ 		}
+@@ -87,18 +97,22 @@
+ 			char buf[sizeof(A2S_RULES)-1+4] = A2S_RULES;
+ 			memcpy(buf+sizeof(A2S_RULES)-1, &status->challenge, 4);
+ 			debug(3, "sending rule query");
+-			if(qserver_send_initial(server, buf, sizeof(buf)) == -1)
+-				goto error;
++			if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++			{
++				return SOCKET_ERROR;
++			}
+ 			status->sent_rules = 1;
+ 			break;
+ 		}
+ 		else if(get_player_info && !status->have_player)
+ 		{
+ 			char buf[sizeof(A2S_PLAYER)-1+4] = A2S_PLAYER;
+-			memcpy(buf+sizeof(A2S_PLAYER)-1, &status->challenge, 4);
++			memcpy( buf + sizeof(A2S_PLAYER)-1, &status->challenge, 4 );
+ 			debug(3, "sending player query");
+-			if(qserver_send_initial(server, buf, sizeof(buf)) == -1)
+-				goto error;
++			if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++			{
++				return SOCKET_ERROR;
++			}
+ 			status->sent_player = 1;
+ 			break;
+ 		}
+@@ -109,15 +123,12 @@
+ 			status->have_challenge = 0;
+ 			status->have_rules = 0;
+ 		}
+-	} while(1);
+-
+-	return;
++	}
+ 
+-error:
+-	cleanup_qserver(server, 1);
++	return INPROGRESS;
+ }
+ 
+-void deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+ 	struct a2s_status* status = (struct a2s_status*)server->master_query_tag;
+ 	char* pkt = rawpkt;
+@@ -157,14 +168,14 @@
+ 		pkt += 4;
+ 
+ 		// packetId
+-		if ( 1 == status->type )
++		if ( 1 == status->type || 200 > server->protocol_version )
+ 		{
+-			// HL2 format
++			// HL1 format
+ 			// The lower four bits represent the number of packets (2 to 15) and
+ 			// the upper four bits represent the current packet starting with 0
+ 			pkt_max = ((unsigned char)*pkt) & 15;
+ 			pkt_index = ((unsigned char)*pkt) >> 4;
+-			debug( 3, "packetid: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++			debug( 3, "packetid[1]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
+ 			pkt++;
+ 			pktlen -= 9;
+ 		}
+@@ -172,20 +183,20 @@
+ 		{
+ 			// HL2 format
+ 			// The next two bytes are:
+-			// 1. the max packets sent
+-			// 2. the index of this packet starting from 0
++			// 1. the max packets sent ( byte )
++			// 2. the index of this packet starting from 0 ( byte )
++			// 3. Size of the split ( short )
+ 			if(pktlen < 10) goto out_too_short;
+ 			pkt_max = ((unsigned char)*pkt);
+ 			pkt_index = ((unsigned char)*(pkt+1));
+-			debug( 3, "packetid: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
+-			pkt+=2;
+-			pktlen -= 10;
++			debug( 3, "packetid[2]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++			pkt+=4;
++			pktlen -= 12;
+ 		}
+ 		else
+ 		{
+ 			malformed_packet( server, "Unable to determine packet format" );
+-			cleanup_qserver( server, 1 );
+-			return;
++			return PKT_ERROR;
+ 		}
+ 
+ 		// pkt_max is the total number of packets expected
+@@ -210,25 +221,24 @@
+ 		if ( NULL == sdata->data )
+ 		{
+ 			malformed_packet(server, "Out of memory");
+-			cleanup_qserver( server, 1 );
+-			return;
++			return MEM_ERROR;
+ 		}
+ 		memcpy( sdata->data, pkt, sdata->datalen);
+ 
+ 		// combine_packets will call us recursively
+-		combine_packets( server );
+-		return;
++		return combine_packets( server );
+ 	}
+ 	else if( 0 != memcmp(pkt, "\xFF\xFF\xFF\xFF", 4) )
+ 	{
+ 		malformed_packet(server, "invalid packet header");
+-		goto out_error;
++		return PKT_ERROR;
+ 	}
+ 
+ 	pkt += 4;
+ 	pktlen -= 4;
+ 
+ 	pktlen -= 1;
++	debug( 2, "A2S type = %x", *pkt );
+ 	switch(*pkt++)
+ 	{
+ 	case A2S_CHALLENGERESPONSE:
+@@ -434,10 +444,14 @@
+ 
+ 		if(pktlen < 9) goto out_too_short;
+ 
+-		// pkt[0], pkt[1] steam id unused
++		// pkt[0], pkt[1] steam appid
++		server->protocol_version = (unsigned short)*pkt;
+ 		server->num_players = (unsigned char)pkt[2];
+ 		server->max_players = (unsigned char)pkt[3];
+ 		// pkt[4] number of bots
++		sprintf( buf, "%hhu", pkt[4] );
++		add_rule( server, "bots", buf, 0 );
++
+ 		add_rule(server, "dedicated", pkt[5]?"1":"0", 0);
+ 		if(pkt[6] == 'l')
+ 		{
+@@ -476,6 +490,57 @@
+ 		pktlen -= str-pkt+1;
+ 		pkt += str-pkt+1;
+ 
++		// EDF
++		if ( 1 <= pktlen )
++		{
++			unsigned char edf = *pkt;
++			pkt++;
++			pktlen--;
++			if ( edf & 0x80 )
++			{
++				// game port
++				unsigned short gameport;
++
++				if(pktlen < 2) goto out_too_short;
++				gameport = swap_short_from_little( pkt );
++				sprintf( buf, "%hu", gameport );
++				add_rule( server, "game_port", buf, 0 );
++				change_server_port( server, gameport, 0 );
++				pkt += 2;
++				pktlen -= 2;
++			}
++
++			if ( edf & 0x40 )
++			{
++				// spectator port
++				unsigned short spectator_port;
++				if(pktlen < 3) goto out_too_short;
++				spectator_port = swap_short_from_little( pkt );
++				sprintf( buf, "%hu", spectator_port );
++				add_rule( server, "spectator_port", buf, 0 );
++				pkt += 2;
++				pktlen -= 2;
++
++				// spectator server name
++				str = memchr(pkt, '\0', pktlen);
++				if(!str) goto out_too_short;
++				add_rule(server, "spectator_server_name", pkt, 0);
++				pktlen -= str-pkt+1;
++				pkt += str-pkt+1;
++			}
++
++			if ( edf & 0x20 )
++			{
++				// game tag
++				str = memchr(pkt, '\0', pktlen);
++				if(!str) goto out_too_short;
++				add_rule(server, "game_tag", pkt, 0);
++				pktlen -= str-pkt+1;
++				pkt += str-pkt+1;
++			}
++		}
++		
++
+ 		status->have_info = 1;
+ 
+ 		server->retry1 = n_retries;
+@@ -553,7 +618,7 @@
+ 
+ 			if(pktlen < 8) goto out_too_short;
+ 
+-			debug(3, "player index %d", idx);
++			debug(3, "player index %d = %s", idx, name );
+ 			p = add_player(server, server->n_player_info);
+ 			if(p)
+ 			{
+@@ -582,7 +647,7 @@
+ 
+ 	default:
+ 		malformed_packet(server, "invalid packet id %hhx", *--pkt);
+-		goto out_error;
++		return PKT_ERROR;
+ 	}
+ 
+ 	if(
+@@ -593,14 +658,11 @@
+ 		server->next_rule = NULL;
+ 	}
+ 
+-	cleanup_qserver(server, 0);
+-	return;
++	return DONE_AUTO;
+ 
+ out_too_short:
+ 	malformed_packet(server, "packet too short");
+-
+-out_error:
+-	cleanup_qserver(server, 1);
++	return PKT_ERROR;
+ }
+ 
+-// vim: sw=4 ts=8 noet
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/a2s.h qstat2/a2s.h
+--- qstat-2.11/a2s.h	2005-06-11 23:47:35.000000000 -0500
++++ qstat2/a2s.h	2009-07-02 06:11:41.032181000 -0400
+@@ -12,8 +12,8 @@
+ 
+ #include "qserver.h"
+ 
+-void send_a2s_request_packet(struct qserver *server);
+-void send_a2s_rule_request_packet(struct qserver *server);
+-void deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen);
++query_status_t send_a2s_request_packet(struct qserver *server);
++query_status_t send_a2s_rule_request_packet(struct qserver *server);
++query_status_t deal_with_a2s_packet(struct qserver *server, char *rawpkt, int pktlen);
+ 
+ #endif
+Only in qstat-2.11: aclocal.m4
+diff -urP qstat-2.11/autogen.sh qstat2/autogen.sh
+--- qstat-2.11/autogen.sh	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/autogen.sh	2008-04-24 18:15:54.223109000 -0400
+@@ -0,0 +1,72 @@
++#!/bin/sh
++
++# Global variables...
++AUTOCONF="autoconf"
++AUTOHEADER="autoheader"
++AUTOM4TE="autom4te"
++AUTOMAKE="automake"
++ACLOCAL="aclocal"
++
++# Please add higher versions first. The last version number is the minimum
++# needed to compile KDE. Do not forget to include the name/version #
++# separator if one is present, e.g. -1.2 where - is the separator.
++AUTOCONF_VERS="-2.61 261 -2.59 259 -2.58 258 -2.57 257 -2.54 254 -2.53 253"
++AUTOMAKE_VERS="-1.9 19 -1.8 18 -1.7 17 -1.6 16 -1.5 15"
++
++# We don't use variable here for remembering the type ... strings. Local 
++# variables are not that portable, but we fear namespace issues with our
++# includer.
++check_autoconf()
++{
++	echo "Checking autoconf version..."
++	for ver in $AUTOCONF_VERS; do
++		if test -x "`$WHICH $AUTOCONF$ver 2>/dev/null`"; then
++			AUTOCONF="`$WHICH $AUTOCONF$ver`"
++			AUTOHEADER="`$WHICH $AUTOHEADER$ver`"
++			AUTOM4TE="`$WHICH $AUTOM4TE$ver`"
++			break
++		fi
++	done
++}
++
++check_automake()
++{
++	echo "Checking automake version..."
++	for ver in $AUTOMAKE_VERS; do
++		if test -x "`$WHICH $AUTOMAKE$ver 2>/dev/null`"; then
++			AUTOMAKE="`$WHICH $AUTOMAKE$ver`"
++			ACLOCAL="`$WHICH $ACLOCAL$ver`"
++			break
++		fi
++	done
++
++	if test -n "$UNSERMAKE"; then 
++		AUTOMAKE="$UNSERMAKE"
++	fi
++}
++
++check_which()
++{
++	WHICH=""
++	for i in "type -p" "which" "type" ; do
++		T=`$i sh 2> /dev/null`
++		test -x "$T" && WHICH="$i" && break
++	done
++}
++
++check_which
++check_autoconf
++check_automake
++
++export AUTOCONF AUTOHEADER AUTOM4TE AUTOMAKE ACLOCAL
++
++set -e
++
++echo "Running aclocal..."
++$ACLOCAL
++echo "Running autoconf..."
++$AUTOCONF
++echo "Running autoheader..."
++$AUTOHEADER
++echo "Running automake..."
++$AUTOMAKE -a --foreign
+diff -urP qstat-2.11/bfbc2.c qstat2/bfbc2.c
+--- qstat-2.11/bfbc2.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/bfbc2.c	2012-05-16 09:05:27.252070000 -0400
+@@ -0,0 +1,148 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Battlefield Bad Company 2 query protocol
++ * Copyright 2009 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_bfbc2_request_packet( struct qserver *server )
++{
++	char buf[50];
++	int size = 0;
++	switch ( server->challenge )
++	{
++	case 0:
++		// Initial connect send serverInfo
++		size = 27;
++		memcpy( buf, "\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00", size );
++		break;
++
++	case 1:
++		// All Done send quit
++		size = 21;
++		memcpy( buf, "\x01\x00\x00\x00\x15\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00quit\x00", size );
++		break;
++
++	case 2:
++		return DONE_FORCE;
++	}
++	debug( 3, "send_bfbc2_request_packet: state = %ld", server->challenge );
++
++	return send_packet( server, buf, size );
++}
++
++query_status_t deal_with_bfbc2_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *end, *crlf;
++	int size, words, word = 0;
++	debug( 2, "processing..." );
++
++	if ( 17 > pktlen )
++	{
++		// Invalid packet
++		return REQ_ERROR;
++	}
++
++	rawpkt[pktlen-1] = '\0';
++	end = &rawpkt[pktlen-1];
++	s = rawpkt;
++
++	server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++	server->n_requests++;
++
++	// Header Sequence
++	s += 4;
++
++	// Packet Size
++	size = *(int*)s;
++	s += 4;
++
++	// Num Words
++	words = *(int*)s;
++	s += 4;
++
++	// Words
++	while ( words > 0 && s + 5 < end )
++	{
++		// Size
++		int ws = *(int*)s;
++		s += 4;
++
++		// Content
++		debug( 6, "word: %s\n", s );
++		switch ( word )
++		{
++		case 0:
++			// Status
++			break;
++		case 1:
++			// Server Name
++			// prevent CR & LF in the server name
++			crlf = strchr( s, '\015' );
++			if ( NULL != crlf )
++			{
++				*crlf = '\0';
++			}
++			crlf = strchr( s, '\012' );
++			if ( NULL != crlf )
++			{
++				*crlf = '\0';
++			}
++			server->server_name = strdup( s );
++			break;
++		case 2:
++			// Player Count
++			server->num_players = atoi( s );
++			break;
++		case 3:
++			// Max Players
++			server->max_players = atoi( s );
++			break;
++		case 4:
++			// Game Mode
++			add_rule( server, "gametype", s, NO_FLAGS );
++			break;
++		case 5:
++			// Map
++			server->map_name = strdup( s );
++			break;
++		}
++		word++;
++
++		s += ws + 1;
++
++		words--;
++	}
++
++	server->challenge++;
++	gettimeofday( &server->packet_time1, NULL );
++
++	if ( 1 == server->challenge )
++	{
++		send_bfbc2_request_packet( server );
++		return INPROGRESS;
++	}
++
++	return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/bfbc2.h qstat2/bfbc2.h
+--- qstat-2.11/bfbc2.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/bfbc2.h	2010-01-14 15:41:32.127318000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Battlefield Bad Company 2 protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_BFBC2_H
++#define QSTAT_BFBC2_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_bfbc2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_bfbc2_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/config.c qstat2/config.c
+--- qstat-2.11/config.c	2006-05-24 09:26:00.000000000 -0500
++++ qstat2/config.c	2009-12-11 19:54:11.483068000 -0500
+@@ -39,16 +39,16 @@
+ static int max_config_types;
+ static int next_gametype_id= LAST_BUILTIN_SERVER+1;
+ 
+-static int load_config_file( char *filename);
+-static int try_load_config_file( char *filename, int show_error);
++static int load_config_file(char const* filename);
++static int try_load_config_file(char const* filename, int const show_error);
+ static int pf_top_level( char *text, void *context);
+ static int pf_gametype_new( char *text, void *context);
+ static int pf_gametype_modify( char *text, void *context);
+ 
+ static int set_game_type_value( server_type *gametype, int key, char *value);
+ static int modify_game_type_value( server_type *gametype, int key, char *value);
+-static int set_packet_value( server_type *gametype, char *value,
+-	char *packet_name, char **packet, int *len);
++static int set_packet_value(server_type* gametype, char const* value,
++	char const* packet_name, char** packet, int* len);
+ 
+ static char *first_token( char *text);
+ static char *next_token();
+@@ -60,14 +60,14 @@
+ static char *force_lower_case( char *string);
+ static char *force_upper_case( char *string);
+ static char *get_token();
+-static void * memdup( void *mem, unsigned int len);
++static void *memdup( const void *mem, unsigned int len);
+ 
+ int parse_config_file( FILE *file);
+ 
+ static int (*parse_func)( char *, void *);
+ static void *parse_context;
+ static int line;
+-static char *current_file_name;
++static char const* current_file_name;
+ 
+ static char *parse_text;
+ static char *parse_end;
+@@ -103,7 +103,7 @@
+     char *key_name;
+ } ConfigKey;
+ 
+-static ConfigKey new_keys[] = {
++static ConfigKey const new_keys[] = {
+ { CK_MASTER_PROTOCOL, "master protocol" },
+ { CK_MASTER_QUERY, "master query" },
+ { CK_MASTER_PACKET, "master packet" },
+@@ -121,7 +121,7 @@
+ { 0, NULL },
+ };
+ 
+-static ConfigKey modify_keys[] = {
++static ConfigKey const modify_keys[] = {
+ { CK_MASTER_PROTOCOL, "master protocol" },
+ { CK_MASTER_QUERY, "master query" },
+ { CK_MASTER_PACKET, "master packet" },
+@@ -136,7 +136,7 @@
+ } ServerFlag;
+ 
+ #define SERVER_FLAG(x) { #x, x }
+-ServerFlag server_flags[] = {
++ServerFlag const server_flags[] = {
+     SERVER_FLAG(TF_SINGLE_QUERY),
+     SERVER_FLAG(TF_OUTFILE),
+     SERVER_FLAG(TF_MASTER_MULTI_RESPONSE),
+@@ -156,7 +156,7 @@
+ };
+ #undef SERVER_FLAG
+ 
+-static int get_config_key( char *first_token, ConfigKey *keys);
++static int get_config_key( char *first_token, const ConfigKey *keys);
+ static void add_config_type( server_type *gametype);
+ static server_type * get_config_type( char *game_type);
+ static void copy_server_type( server_type *dest, server_type *source);
+@@ -239,7 +239,7 @@
+     filename= getenv( "HOME");
+     if ( filename != NULL && filename[0] != '\0')  {
+ 	char path[1024];
+-	sprintf( path, "%s/%s", filename, HOME_CONFIG_FILE);
++	snprintf( path, (sizeof(path) -1),"%s/%s", var, HOME_CONFIG_FILE);
+     }
+ /* 1. $QSTAT_CONFIG
+    2. UNIX: $HOME/.qstatrc         WIN: $HOME/qstat.cfg
+@@ -254,7 +254,7 @@
+ }
+ 
+ int
+-qsc_load_config_file( char *filename)
++qsc_load_config_file(char const* filename)
+ {
+     int rc= load_config_file( filename);
+     if ( rc == -2)  {
+@@ -275,7 +275,7 @@
+ }
+ 
+ STATIC int
+-try_load_config_file( char *filename, int show_error)
++try_load_config_file(char const* filename, int const show_error)
+ {
+     int rc= load_config_file( filename);
+     if ( rc == -2 && show_error)  {
+@@ -290,7 +290,7 @@
+ }
+ 
+ STATIC int
+-load_config_file( char *filename)
++load_config_file(char const* filename)
+ {
+     FILE *file;
+     int rc;
+@@ -522,7 +522,7 @@
+ }
+ 
+ STATIC int
+-get_config_key( char *first_token, ConfigKey *keys)
++get_config_key( char *first_token, const ConfigKey *keys)
+ {
+     char key_name[1024], *token;
+     int key= 0;
+@@ -729,7 +729,7 @@
+ }
+ 
+ STATIC int
+-set_packet_value( server_type *gametype, char *value, char *packet_name,
++set_packet_value(server_type* gametype, char const* value, char const* packet_name,
+ 	char **packet, int *len)
+ {
+     if ( gametype->master)  {
+@@ -740,10 +740,12 @@
+ 	    REPORT_ERROR((stderr, "Empty %s packet", packet_name));
+ 	    return -1;
+ 	}
+-	if ( *packet == NULL)  {
+-	    REPORT_ERROR((stderr, "Cannot set %s packet; extend game type does not define a %s packet", packet_name, packet_name));
+-	    return -1;
+-	}
++	// Removed as this doesn't seem to be any reason why we can't do this and it works just fine for warsow
++	//if ( *packet == NULL)  {
++	//    REPORT_ERROR((stderr, "Cannot set %s packet; extend game type does not define a %s packet", packet_name, packet_name));
++	//    return -1;
++	//}
++
+ 	*packet= memdup( value, value_len);
+ 	*len= value_len;
+     return 0;
+@@ -881,10 +883,15 @@
+ }
+ 
+ STATIC void *
+-memdup( void *mem, unsigned int len)
++memdup( const void *mem, unsigned int len)
+ {
+     void *result= malloc( len);
+-    memcpy( result, mem, len);
++	if ( NULL == result )
++	{
++	    REPORT_ERROR(( stderr, "Failed to malloc %d bytes of memory", len ) );
++		return NULL;
++	}
++	memcpy( result, mem, len);
+     return result;
+ }
+ 
+Only in qstat-2.11: config.guess
+diff -urP qstat-2.11/config.h qstat2/config.h
+--- qstat-2.11/config.h	2002-11-13 21:33:53.000000000 -0500
++++ qstat2/config.h	2009-12-11 19:29:52.155859000 -0500
+@@ -11,7 +11,7 @@
+ #include "qstat.h"
+ 
+ int qsc_load_default_config_files();
+-int qsc_load_config_file( char *filename);
++int qsc_load_config_file(char const* filename);
+ server_type ** qsc_get_config_server_types( int *n_config_types);
+ 
+ 
+Only in qstat-2.11: config.sub
+Only in qstat-2.11: configure
+diff -urP qstat-2.11/configure.ac qstat2/configure.ac
+--- qstat-2.11/configure.ac	2006-10-28 07:36:47.000000000 -0500
++++ qstat2/configure.ac	2012-04-20 08:51:31.440424000 -0400
+@@ -1,4 +1,4 @@
+-AC_INIT([qstat],[2.11],[qstat-users at yahoogroups.com])
++AC_INIT([qstat],[2.12],[qstat-users at yahoogroups.com])
+ AC_CONFIG_SRCDIR([qstat.c])
+ AM_CONFIG_HEADER([gnuconfig.h])
+ 
+@@ -21,6 +21,42 @@
+ 	;;
+ esac
+ 
++dnl Check for strnstr including broken one on MacOSX 10.4 which crashes
++dnl
++AC_CACHE_CHECK(for strnstr, ac_cv_func_strnstr,
++  AC_TRY_RUN([
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++// we expect this to succeed, or crash on over-run.
++// if it passes otherwise we may need a better check.
++int main(int argc, char **argv)
++{
++    int size = 20;
++    char *str = malloc(size);
++    memset(str, 'x', size);
++    strnstr(str, "fubar", size);
++    return 0;
++}
++  ],ac_cv_func_strnstr="yes",ac_cv_func_strnstr="no")
++)
++if test "$ac_cv_func_strnstr" = "yes" ; then
++  AC_DEFINE(HAVE_STRNSTR,1,[Working strnstr])
++else
++  AC_DEFINE(HAVE_STRNSTR,0,[No or broken strnstr])
++fi
++
++dnl check if user wants debug
++AC_MSG_CHECKING([whether to enable optimization])
++AC_ARG_ENABLE(optimize,[  --disable-optimize         turn off optimization])
++if test x$enable_optimize != xno; then
++	AC_MSG_RESULT([yes])
++else
++	CPPFLAGS=""
++	CFLAGS="-ggdb"
++	AC_MSG_RESULT([no])
++fi
++
+ dnl check if user wants debug
+ AC_MSG_CHECKING([whether to enable debug output])
+ AC_ARG_ENABLE(debug,[  --disable-debug         turn off debugging code])
+@@ -44,6 +80,14 @@
+ 	AC_MSG_RESULT([no])
+ fi
+ 
++AC_ARG_WITH(efence,
++[  --with-efence=<path>    Use electric fence for malloc debugging.],
++	if test x$withval != xyes ; then
++		LDFLAGS="${LDFLAGS} -L$withval"
++	fi
++	AC_CHECK_LIB(efence,malloc)
++)
++
+ dnl Use -Wall if we have gcc.
+ changequote(,)dnl
+ if test "x$GCC" = "xyes"; then
+diff -urP qstat-2.11/crysis.c qstat2/crysis.c
+--- qstat-2.11/crysis.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/crysis.c	2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,276 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Crysis query protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "utils.h"
++#include "qstat.h"
++#include "md5.h"
++#include "packet_manip.h"
++
++char *decode_crysis_val( char *val )
++{
++	// Very basic html conversion
++	val = str_replace( val, "&quot;", "\"" );
++	return str_replace( val, "&amp;", "&" );
++}
++
++query_status_t send_crysis_request_packet( struct qserver *server )
++{
++	char cmd[256], buf[1024], *password, *md5;
++	debug( 2, "challenge: %ld", server->challenge );
++	switch ( server->challenge )
++	{
++	case 0:
++		// Not seen a challenge yet, request it
++		server->challenge++;
++		sprintf( cmd, "challenge" );
++		break;
++
++	case 1:
++		server->challenge++;
++		password = get_param_value( server, "password", "" );
++		sprintf( cmd, "%s:%s", server->challenge_string, password );
++		md5 = md5_hex( cmd, strlen( cmd ) );
++		sprintf( cmd, "authenticate %s", md5 );
++		free( md5 );
++		break;
++
++	case 2:
++		// NOTE: we currently don't support player info
++		server->challenge++;
++		server->flags |= TF_STATUS_QUERY;
++		server->n_servers = 3;
++		sprintf( cmd, "status" );
++		break;
++
++	case 3:
++		return DONE_FORCE;
++	}
++
++	server->saved_data.pkt_max = -1;
++	sprintf(buf, "POST /RPC2 HTTP/1.1\015\012Keep-Alive: 300\015\012User-Agent: qstat %s\015\012Content-Length: %d\015\012Content-Type: text/xml\015\012\015\012<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>%s</methodName><params /></methodCall>", VERSION, (int)(98 + strlen(cmd)), cmd);
++
++	return send_packet( server, buf, strlen( buf ) );
++}
++
++query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s;
++	int len;
++	int cnt = packet_count( server );
++	if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) )
++	{
++		// not valid response
++		return REQ_ERROR;
++	}
++
++	s = strnstr(rawpkt, "Content-Length: ", pktlen );
++	if ( NULL == s )
++	{
++		// not valid response
++		return INPROGRESS;
++	}
++	s += 16;
++	if ( 1 != sscanf( s, "%d", &len ) )
++	{
++		return INPROGRESS;
++	}
++
++	s = strnstr(rawpkt, "\015\012\015\012", pktlen );
++	if ( NULL == s )
++	{
++		return INPROGRESS;
++	}
++
++	s += 4;
++	if ( pktlen != ( s - rawpkt + len ) )
++	{
++		return INPROGRESS;
++	}
++
++	return DONE_FORCE;
++}
++
++char* crysis_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *e;
++	int len = pktlen;
++
++	s = strnstr(rawpkt, "<methodResponse><params><param><value><string>", len );
++	if ( NULL == s )
++	{
++		// not valid response
++		return NULL;
++	}
++	s += 46;
++	len += rawpkt - s;
++	e = strnstr(s, "</string></value>", len );
++	if ( NULL == e )
++	{
++		// not valid response
++		return NULL;
++	}
++	*e = '\0';
++
++	return strdup( s );
++}
++
++query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *val, *line;
++	query_status_t state = INPROGRESS;
++	debug( 2, "processing..." );
++
++	if ( ! server->combined )
++	{
++		state = valid_crysis_response( server, rawpkt, pktlen );
++		server->retry1 = n_retries;
++		if ( 0 == server->n_requests )
++		{
++			server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++			server->n_requests++;
++		}
++
++		switch ( state )
++		{
++		case INPROGRESS:
++			{
++			// response fragment recieved
++			int pkt_id;
++			int pkt_max;
++
++			// We're expecting more to come
++			debug( 5, "fragment recieved..." );
++			pkt_id = packet_count( server );
++			pkt_max = pkt_id++;
++			if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++			{
++				// fatal error e.g. out of memory
++				return MEM_ERROR;
++			}
++
++			// combine_packets will call us recursively
++			return combine_packets( server );
++			}
++		case DONE_FORCE:
++			break; // single packet response fall through
++		default:
++			return state;
++		}
++	}
++
++	if ( DONE_FORCE != state )
++	{
++		state = valid_crysis_response( server, rawpkt, pktlen );
++		switch ( state )
++		{
++		case DONE_FORCE:
++			break; // actually process
++		default:
++			return state;
++		}
++	}
++
++	debug( 3, "packet: challenge = %ld", server->challenge );
++	switch ( server->challenge )
++	{
++	case 1:
++		s = crysis_response( server, rawpkt, pktlen );
++		if ( NULL != s )
++		{
++			server->challenge_string = s;
++			return send_crysis_request_packet( server );
++		}
++		return REQ_ERROR;
++	case 2:
++		s = crysis_response( server, rawpkt, pktlen );
++		if ( NULL == s )
++		{
++			return REQ_ERROR;
++		}
++		if ( 0 != strncmp( s, "authorized", 10 ) )
++		{
++			free( s );
++			return REQ_ERROR;
++		}
++		free( s );
++		return send_crysis_request_packet( server );
++	case 3:
++		s = crysis_response( server, rawpkt, pktlen );
++		if ( NULL == s )
++		{
++			return REQ_ERROR;
++		}
++	}
++
++	// Correct ping
++	// Not quite right but gives a good estimate
++	server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++	debug( 3, "processing response..." );
++
++	s = decode_crysis_val( s );
++	line = strtok( s, "\012" );
++
++	// NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++	while ( NULL != line )
++	{
++		debug( 4, "LINE: %s\n", line );
++		val = strstr( line, ":" );
++		if ( NULL != val )
++		{
++			*val = '\0';
++			val+=2;
++			debug( 4, "var: %s, val: %s", line, val );
++			if ( 0 == strcmp( "name", line ) )
++			{
++				server->server_name = strdup( val );
++			}
++			else if ( 0 == strcmp( "level", line ) )
++			{
++				server->map_name = strdup( val );
++			}
++			else if ( 0 == strcmp( "players", line ) )
++			{
++				if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) )
++				{
++				}
++			}
++			else if (
++				0 == strcmp( "version", line ) ||
++				0 == strcmp( "gamerules", line ) ||
++				0 == strcmp( "time remaining", line )
++			)
++			{
++				add_rule( server, line, val, NO_FLAGS );
++			}
++		}
++		
++		line = strtok( NULL, "\012" );
++	}
++
++	gettimeofday( &server->packet_time1, NULL );
++
++	return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/crysis.h qstat2/crysis.h
+--- qstat-2.11/crysis.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/crysis.h	2012-04-20 06:22:15.250385000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Crysis protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_CRYSIS_H
++#define QSTAT_CRYSIS_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_crysis_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_crysis_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/cube2.c qstat2/cube2.c
+--- qstat-2.11/cube2.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/cube2.c	2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,232 @@
++/*
++ * qstat 2.12
++ * by Steve Jankowski
++ *
++ * Cube 2 / Sauerbraten protocol
++ * Copyright 2011 NoisyB
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++struct offset
++{
++	unsigned char *d;
++	int pos;
++	int len;
++};
++
++
++//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
++#define SB_PROTOCOL 258
++#define MIN(a,b) ((a)<(b)?(a):(b))
++#define MAX_ATTR 255
++#define MAX_STRING 1024
++
++
++static int
++getint (struct offset * d)
++{
++	int val = 0;
++
++	if ( d->pos >= d->len )
++	{
++		return 0;
++	}
++
++	val = d->d[d->pos++] & 0xff; // 8 bit value
++
++	// except...
++	if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value
++	{
++		val = (d->d[d->pos++] & 0xff);
++		val |= (d->d[d->pos++] & 0xff) << 8;
++	}
++	else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value
++	{
++		val = (d->d[d->pos++] & 0xff);
++		val |= (d->d[d->pos++] & 0xff) << 8;
++		val |= (d->d[d->pos++] & 0xff) << 16;
++		val |= (d->d[d->pos++] & 0xff) << 24;
++	}
++
++	return val;
++}
++
++
++static char * getstr( char *dest, int dest_len, struct offset *d )
++{
++	int len = 0;
++
++	if (d->pos >= d->len)
++	{
++		return NULL;
++	}
++
++	len = MIN( dest_len, d->len - d->pos );
++	strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0;
++	d->pos += strlen (dest) + 1;
++
++	return dest;
++}
++
++
++static char* sb_getversion_s (int n)
++{
++	static char *version_s[] =
++	{
++		"Justice",
++		"CTF",
++		"Assassin",
++		"Summer",
++		"Spring",
++		"Gui",
++		"Water",
++		"Normalmap",
++		"Sp",
++		"Occlusion",
++		"Shader",
++		"Physics",
++		"Mp",
++		"",
++		"Agc",
++		"Quakecon",
++		"Independence"
++	};
++
++	n = SB_PROTOCOL - n;
++	if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0]))
++	{
++		return version_s[n];
++	}
++	return "unknown";
++}
++
++
++static char* sb_getmode_s(int n)
++{
++	static char *mode_s[] =
++	{
++		"slowmo SP",
++		"slowmo DMSP",
++		"demo",
++		"SP",
++		"DMSP",
++		"ffa/default",
++		"coopedit",
++		"ffa/duel",
++		"teamplay",
++		"instagib",
++		"instagib team",
++		"efficiency",
++		"efficiency team",
++		"insta arena",
++		"insta clan arena",
++		"tactics arena",
++		"tactics clan arena",
++		"capture",
++		"insta capture",
++		"regen capture",
++		"assassin",
++		"insta assassin",
++		"ctf",
++		"insta ctf"
++	};
++
++	n += 6;
++	if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0]))
++	{
++		return mode_s[n];
++	}
++	return "unknown";
++}
++
++
++query_status_t send_cube2_request_packet( struct qserver *server )
++{
++	return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++
++query_status_t deal_with_cube2_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	// skip unimplemented ack, crc, etc
++	int i;
++	int numattr;
++	int attr[MAX_ATTR];
++	char buf[MAX_STRING];
++	enum {
++		MM_OPEN = 0,
++		MM_VETO,
++		MM_LOCKED,
++		MM_PRIVATE
++	};
++	struct offset d;
++	d.d = (unsigned char *) rawpkt;
++	d.pos = 0;
++	d.len = pktlen;
++
++	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++	getint( &d ); // we have the ping already
++	server->num_players = getint( &d );
++	numattr = getint( &d );
++	for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
++	{
++		attr[i] = getint (&d);
++	}
++
++	server->protocol_version = attr[0];
++
++	sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) );
++	add_rule( server, "version", buf, NO_FLAGS );
++
++	sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) );
++	add_rule( server, "mode", buf, NO_FLAGS );
++
++	sprintf( buf, "%d", attr[2] );
++	add_rule( server, "seconds_left", buf, NO_FLAGS );
++
++	server->max_players = attr[3];
++
++	switch ( attr[5] )
++	{
++	case MM_OPEN:
++		sprintf( buf, "%d open", attr[5] );
++		break;
++	case MM_VETO:
++		sprintf( buf, "%d veto", attr[5] );
++		break;
++	case MM_LOCKED:
++		sprintf( buf, "%d locked", attr[5] );
++		break;
++	case MM_PRIVATE:
++		sprintf( buf, "%d private", attr[5] );
++		break;
++	default:
++		sprintf( buf, "%d unknown", attr[5] );
++	}
++	add_rule( server, "mm", buf, NO_FLAGS);
++
++	for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
++	{
++		char buf2[MAX_STRING];
++		sprintf( buf, "attr%d", i );
++		sprintf( buf2, "%d", attr[i] );
++		add_rule( server, buf, buf2, NO_FLAGS );
++	}
++
++	getstr( buf, MAX_STRING, &d );
++	server->map_name = strdup(buf);
++	getstr( buf, MAX_STRING, &d );
++	server->server_name = strdup(buf);
++
++	return DONE_FORCE;
++}
++
++
+diff -urP qstat-2.11/cube2.h qstat2/cube2.h
+--- qstat-2.11/cube2.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/cube2.h	2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,23 @@
++/*
++ * qstat 2.12
++ * by Steve Jankowski
++ *
++ * Cube 2 / Sauerbraten protocol
++ * Copyright 2011 NoisyB
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_CUBE2_H
++#define QSTAT_CUBE2_H
++
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_cube2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_cube2_request_packet( struct qserver *server );
++
++
++#endif  // QSTAT_CUBE2_H
++
++
+diff -urP qstat-2.11/debug.c qstat2/debug.c
+--- qstat-2.11/debug.c	2006-05-24 09:26:00.000000000 -0500
++++ qstat2/debug.c	2009-07-02 06:12:13.391497000 -0400
+@@ -8,13 +8,13 @@
+  * Licensed under the Artistic License, see LICENSE.txt for license terms
+  */
+ 
+-#include "debug.h"
+-
+ #include <stdio.h>
+ #include <stdlib.h>
+ #ifndef _WIN32
+ #include <unistd.h>
+ #include <sys/param.h>
++#else
++#include <stdarg.h>
+ #endif
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -24,15 +24,26 @@
+ #include <fcntl.h>
+ #include <stdarg.h>
+ 
++#define QSTAT_DEBUG_C
++#include "debug.h"
++
++#ifdef ENABLE_DUMP
++#ifndef _WIN32
++	#include <sys/socket.h>
++#else
++	#include <winsock.h>
++#endif
++#endif
++
+ #ifdef DEBUG
++
+ void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...)
+ {
+ 	va_list ap;
+ 	char buf[9];
+-	time_t now;
++	time_t now = time( NULL );
+ 
+-	now = time(NULL);
+-	strftime(buf,9,"%T",localtime(&now));
++	strftime(buf,9,"%H:%M:%S", localtime( &now ) );
+ 
+ 	fprintf(stderr, "debug(%d) %s %s:%d %s() - ", level, buf, file, line, function);
+ 
+@@ -90,24 +101,42 @@
+ 
+ #ifdef ENABLE_DUMP
+ static unsigned count = 0;
+-#endif
+-void dump_packet(const char* buf, int buflen)
++static void _dump_packet(const char* tag, const char* buf, int buflen)
+ {
+-#ifdef ENABLE_DUMP
+ 	char fn[PATH_MAX] = {0};
+ 	int fd;
+-	sprintf(fn, "dump%03u", count++);
++	sprintf(fn, "%03u_%s.pkt", count++, tag);
+ 	fprintf(stderr, "dumping to %s\n", fn);
+ 	fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ 	if(fd == -1) { perror("open"); return; }
+ 	if(write(fd, buf, buflen) == -1)
+ 		perror("write");
+ 	close(fd);
++}
++
++ssize_t send_dump(int s, const void *buf, size_t len, int flags)
++{
++	if(do_dump)
++		_dump_packet("send", buf, len);
++	return send(s, buf, len, flags);
++}
++#endif
++
++void dump_packet(const char* buf, int buflen)
++{
++#ifdef ENABLE_DUMP
++	_dump_packet("recv", buf, buflen);
+ #endif
+ }
+ 
+ void
+-print_packet( struct qserver *server, const char *buf, int buflen)
++print_packet( struct qserver *server, const char *buf, int buflen )
++{
++	output_packet( server, buf, buflen, 0 );
++}
++
++void
++output_packet( struct qserver *server, const char *buf, int buflen, int to )
+ {
+ 	static char *hex= "0123456789abcdef";
+ 	unsigned char *p= (unsigned char*)buf;
+@@ -115,27 +144,40 @@
+ 	char line[256];
+ 
+ 	if ( server != NULL)
+-	fprintf( stderr, "FROM %s len %d\n", server->arg, buflen );
+-
+-	for ( i= buflen; i ; offset+= 16)  {
+-	memset( line, ' ', 256);
+-	h= 0;
+-	h+= sprintf( line, "%5d:", offset);
+-	a= astart = h + 16*2 + 16/4 + 2;
+-	for ( b=16; b && i; b--, i--, p++)  {
+-		if ( (b & 3) == 0)
+-		line[h++]= ' ';
+-		line[h++]= hex[*p >> 4];
+-		line[h++]= hex[*p & 0xf];
+-		if ( isprint( *p))
+-		line[a++]= *p;
+-		else
+-		line[a++]= '.';
+-		if((a-astart)==8) line[a++] = ' ';
++	{
++		fprintf( stderr, "%s %s len %d\n", ( to ) ? "TO" : "FROM", server->arg, buflen );
+ 	}
+-	line[a]= '\0';
+-	fputs( line, stderr);
+-	fputs( "\n", stderr);
++
++	for ( i= buflen; i ; offset+= 16)
++	{
++		memset( line, ' ', 256);
++		h= 0;
++		h+= sprintf( line, "%5d:", offset);
++		a= astart = h + 16*2 + 16/4 + 2;
++		for ( b=16; b && i; b--, i--, p++)
++		{
++			if ( (b & 3) == 0)
++			{
++				line[h++]= ' ';
++			}
++			line[h++]= hex[*p >> 4];
++			line[h++]= hex[*p & 0xf];
++			if ( isprint( *p))
++			{
++				line[a++]= *p;
++			}
++			else
++			{
++				line[a++]= '.';
++			}
++			if((a-astart)==8)
++			{
++				line[a++] = ' ';
++			}
++		}
++		line[a]= '\0';
++		fputs( line, stderr);
++		fputs( "\n", stderr);
+ 	}
+ 	fputs( "\n", stderr);
+ }
+diff -urP qstat-2.11/debug.h qstat2/debug.h
+--- qstat-2.11/debug.h	2006-05-24 09:26:00.000000000 -0500
++++ qstat2/debug.h	2009-08-14 21:52:06.457883000 -0400
+@@ -10,26 +10,50 @@
+ #ifndef QSTAT_DEBUG_H
+ #define QSTAT_DEBUG_H
+ 
++#include <sys/types.h>
++
+ #include "qstat.h"
+ 
+-// NOTE: Windows doesn't support debugging ATM
+-#ifdef _WIN32
+-	#define debug 0 &&
+-#else
+-	#ifdef DEBUG
+-		#include <stdarg.h>
++
++#ifdef DEBUG
++	#include <stdarg.h>
++	#ifdef _WIN32
++		void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...);
++		#define debug(level,fmt,...) \
++			if( level <= get_debug_level() ) \
++				_debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,__VA_ARGS__)
++	#else
+ 		void _debug(const char* file, int line, const char* function, int level, const char* fmt, ...)
+-			GCC_FORMAT_PRINTF(5, 6);
++		GCC_FORMAT_PRINTF(5, 6);
+ 		#define debug(level,fmt,rem...) \
+-		  if( level <= get_debug_level() ) \
+-			_debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,##rem)
+-	#else
+-		#define debug(...)
+-	#endif
+-#endif
++			if( level <= get_debug_level() ) \
++				_debug(__FILE__,__LINE__,__FUNCTION__,level,fmt,##rem)
++	#endif // _WIN32
++#else
++	#define debug(...)
++#endif // DEBUG
+ 
+ void dump_packet(const char* buf, int buflen);
+ 
++#ifdef ENABLE_DUMP
++#ifndef _WIN32
++	#include <sys/mman.h>
++	#include <unistd.h>
++#else
++	#ifdef _MSC_VER
++		#define ssize_t SSIZE_T
++		#define uint32_t UINT32
++	#endif
++#endif
++#include <sys/types.h>
++#include <sys/stat.h>
++int do_dump;
++ssize_t send_dump(int s, const void *buf, size_t len, int flags);
++#ifndef QSTAT_DEBUG_C
++#define send(s, buf, len, flags) send_dump(s, buf, len, flags)
++#endif
++#endif
++
+ /** report a packet decoding error to stderr */
+ void malformed_packet(const struct qserver* server, const char* fmt, ...) GCC_FORMAT_PRINTF(2, 3);
+ 
+@@ -37,5 +61,6 @@
+ void set_debug_level (int level);
+ 
+ void print_packet( struct qserver *server, const char *buf, int buflen );
++void output_packet( struct qserver *server, const char *buf, int buflen, int to );
+ 
+ #endif
+Only in qstat-2.11: depcomp
+diff -urP qstat-2.11/doom3.c qstat2/doom3.c
+--- qstat-2.11/doom3.c	2006-07-11 19:28:49.000000000 -0500
++++ qstat2/doom3.c	2009-08-24 12:54:15.403581000 -0400
+@@ -131,7 +131,7 @@
+ 	return buf;
+ }
+ 
+-void send_doom3master_request_packet( struct qserver *server)
++query_status_t send_doom3master_request_packet( struct qserver *server)
+ {
+ 	int rc = 0;
+ 	int packet_len = -1;
+@@ -146,7 +146,7 @@
+ 	rc= send( server->fd, packet, packet_len, 0);
+ 	if ( rc == SOCKET_ERROR)
+ 	{
+-		perror( "send");
++		return send_error( server, rc );
+ 	}
+ 
+ 	if ( server->retry1 == n_retries)
+@@ -160,9 +160,11 @@
+ 	}
+ 	server->retry1--;
+ 	server->n_packets++;
++
++	return INPROGRESS;
+ }
+ 
+-void send_quake4master_request_packet( struct qserver *server)
++query_status_t send_quake4master_request_packet( struct qserver *server)
+ {
+ 	int rc = 0;
+ 	int packet_len = -1;
+@@ -177,7 +179,7 @@
+ 	rc= send( server->fd, packet, packet_len, 0);
+ 	if ( rc == SOCKET_ERROR)
+ 	{
+-		perror( "send");
++		return send_error( server, rc );
+ 	}
+ 
+ 	if ( server->retry1 == n_retries)
+@@ -191,17 +193,17 @@
+ 	}
+ 	server->retry1--;
+ 	server->n_packets++;
++
++	return INPROGRESS;
+ }
+ 
+ static const char doom3_masterresponse[] = "\xFF\xFFservers";
+ 
+-void
+-deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen)
+ {
+ 	char* pkt, *dest;
+ 	int len;
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
++	server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+ 
+ 	if ( pktlen < sizeof(doom3_masterresponse) + 6 // at least one server
+ 			|| (pktlen - sizeof(doom3_masterresponse)) % 6
+@@ -210,8 +212,7 @@
+ 		server->server_name= SERVERERROR;
+ 		server->master_pkt_len = 0;
+ 		malformed_packet(server, "too short or invalid response");
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	server->retry1 = 0; // received at least one packet so no need to retry
+@@ -239,16 +240,23 @@
+ 	server->n_servers= server->master_pkt_len / 6;
+ 
+ 	debug(2, "%d servers added", server->n_servers);
++
++	return INPROGRESS;
+ }
+ 
+ static const char doom3_inforesponse[] = "\xFF\xFFinfoResponse";
+ static unsigned MAX_DOOM3_ASYNC_CLIENTS = 32;
++static unsigned MAX_WOLF_ASYNC_CLIENTS = 16;
+ 
+-static void _deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen, unsigned version )
++static query_status_t _deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen, unsigned version )
+ {
+ 	char *ptr = rawpkt;
+ 	char *end = rawpkt + pktlen;
+ 	int type = 0;
++	int size = 0;
++	int tail_size = 4;
++	int viewers = 0;
++	int tv = 0;
+ 	unsigned num_players = 0;
+ 	unsigned challenge = 0;
+ 	unsigned protocolver = 0;
+@@ -269,11 +277,18 @@
+ 		memcmp( doom3_inforesponse, ptr, sizeof(doom3_inforesponse)) != 0 )
+ 	{
+ 		malformed_packet(server, "too short or invalid response");
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 	ptr += sizeof(doom3_inforesponse);
+ 
++	if ( 5 == version )
++	{
++		// TaskID
++		ptr += 4;
++		// osmask + ranked
++		tail_size++;
++	}
++
+ 	challenge = swap_long_from_little(ptr);
+ 	ptr += 4;
+ 
+@@ -283,20 +298,28 @@
+ 	snprintf(tmp, sizeof(tmp), "%u.%u", protocolver >> 16, protocolver & 0xFFFF);
+ 	debug(2, "challenge: 0x%08X, protocol: %s (0x%X)", challenge, tmp, protocolver);
+ 
++	server->protocol_version = protocolver;
++	add_rule( server, "protocol", tmp, NO_FLAGS );
++
++
++	if ( 5 == version )
++	{
++		// Size Long
++		size = swap_long_from_little(ptr);
++		debug( 2, "Size = %d", size );
++		ptr += 4;
++	}
++
+ // Commented out until we have a better way to specify the expected version
+ // This is due to prey demo requiring version 4 yet prey retail version 3
+ /*
+ 	if( protocolver >> 16 != version )
+ 	{
+ 		malformed_packet(server, "protocol version %u, expected %u", protocolver >> 16, version );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ */
+ 
+-	server->protocol_version = protocolver;
+-	add_rule( server, "protocol", tmp, NO_FLAGS );
+-
+ 	while ( ptr < end )
+ 	{
+ 		// server info:
+@@ -310,8 +333,7 @@
+ 		if ( !ptr )
+ 		{
+ 			malformed_packet( server, "no rule key" );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 		keylen = ptr - key;
+ 
+@@ -320,19 +342,19 @@
+ 		if ( !ptr )
+ 		{
+ 			malformed_packet( server, "no rule value" );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 		vallen = ptr - val;
+ 		++ptr;
+-		debug( 2, "key:%s=%s:", key, val);
+ 
+-		if(keylen == 0 && vallen == 0)
++		if( keylen == 0 && vallen == 0 )
+ 		{
+ 			type = 1;
+ 			break; // end
+ 		}
+ 
++		debug( 2, "key:%s=%s:", key, val);
++
+ 		// Lets see what we've got
+ 		if ( 0 == strcasecmp( key, "si_name" ) )
+ 		{
+@@ -346,18 +368,46 @@
+ 		else if( 0 == strcasecmp( key, "si_version" ) )
+ 		{
+ 			// format:
++x
+ 			// DOOM 1.0.1262.win-x86 Jul  8 2004 16:46:37
+ 			server->protocol_version = atoi( val+1 );
+ 		}
+ #endif
+ 		else if( 0 == strcasecmp( key, "si_map" ) )
+ 		{
++			if ( 5 == version || 6 == version )
++			{
++				// ET:QW reports maps/<mapname>.entities
++				// so we strip that here if it exists
++				char *tmpp = strstr( val, ".entities" );
++				if ( NULL != tmpp )
++				{
++					*tmpp = '\0';
++				}
++
++				if ( 0 == strncmp( val, "maps/", 5 ) )
++				{
++					val += 5;
++				}
++			}
+ 			server->map_name = strdup( val );
+ 		}
+-		else if( 0 == strcasecmp( key, "si_maxplayers" ) )
++		else if ( 0 == strcasecmp( key, "si_maxplayers" ) )
++		{
++			server->max_players = atoi( val );
++		}
++		else if (  0 == strcasecmp( key, "ri_maxViewers" ) )
+ 		{
++			char max[20];
++			sprintf( max, "%d", server->max_players );
++			add_rule( server, "si_maxplayers", max, NO_FLAGS );
+ 			server->max_players = atoi( val );
+ 		}
++		else if (  0 == strcasecmp( key, "ri_numViewers" ) )
++		{
++			viewers = atoi( val );
++			tv = 1;
++		}
+ 
+ 		add_rule( server, key, val, NO_FLAGS );
+ 	}
+@@ -367,95 +417,221 @@
+ 		// no more info should be player headers here as we
+ 		// requested it
+ 		malformed_packet( server, "player info missing" );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	// now each player details
+-	while( ptr < end )
++	while( ptr < end - tail_size )
+ 	{
+ 		struct player *player;
+ 		char *val;
+ 		unsigned char player_id = *ptr++;
+-		short prediction = 0;
++		short ping = 0;
+ 		unsigned rate = 0;
+ 
+-		if(player_id == MAX_DOOM3_ASYNC_CLIENTS)
+-			break;
+-
+-		/* id's are not steady
+-		if(player_id != num_players)
++		if( ( 6 == version && MAX_WOLF_ASYNC_CLIENTS == player_id ) || MAX_DOOM3_ASYNC_CLIENTS == player_id )
+ 		{
+-			malformed_packet(server, "unexpected player id");
+-			cleanup_qserver( server, 1);
+-			return;
++			break;
+ 		}
+-		*/
++		debug( 2, "ID = %d\n", player_id );
+ 
+-		if ( ptr + 7 > end ) // 2 pred + 4 rate + empty player name ('\0')
++		// Note: id's are not steady
++		if ( ptr + 7 > end ) // 2 ping + 4 rate + empty player name ('\0')
+ 		{
+ 			// run off the end and shouldnt have
+ 			malformed_packet( server, "player info too short" );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
++/*
++		if ( 6 == version )
++		{
++			// Playerid is broken in wolf its always 0
++			player_id = num_players;
++		}
++*/
+ 
+ 		player = add_player( server, player_id );
++		if( ! player )
++		{
++			malformed_packet( server, "duplicate player id" );
++			return PKT_ERROR;
++		}
+ 
++		// doesnt support score so set a sensible default
+ 		player->score = 0;
+ 		player->frags = 0;
+ 
+-		prediction = swap_short_from_little(ptr);
++		// Ping
++		ping = swap_short_from_little(ptr);
++		player->ping = ping;
+ 		ptr += 2;
+ 
+-		player->ping = prediction; // seems to be ping
+-
+-		rate = swap_long_from_little(ptr);
+-		ptr += 4;
++		if ( 5 != version || 0xa0013 >= protocolver ) // No Rate in ETQW since v1.4 ( protocol v10.19 )
++		{
++			// Rate
++			rate = swap_long_from_little(ptr);
++			{
++				char buf[16];
++				snprintf(buf, sizeof(buf), "%u", rate);
++				player_add_info(player, "rate", buf, NO_FLAGS);
++			}
++			ptr += 4;
++		}
+ 
++		if ( 5 == version && ( ( 0xd0009 == protocolver || 0xd000a == protocolver ) && 0 != num_players ) ) // v13.9 or v13.10
+ 		{
+-			char buf[16];
+-			snprintf(buf, sizeof(buf), "%u", rate);
+-			player_add_info(player, "rate", buf, NO_FLAGS);
++			// Fix the packet offset due to the single bit used for bot
++			// which realigns at the byte boundary for the player name
++			ptr++;
+ 		}
+ 
++		// Name
+ 		val = ptr;
+ 		ptr = memchr(ptr, '\0', end-ptr);
+ 		if ( !ptr )
+ 		{
+ 			malformed_packet( server, "player name not null terminated" );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+-		++ptr;
+ 		player->name = strdup( val );
++		ptr++;
+ 
+-		if( 2 == version )
++		switch( version )
+ 		{
++		case 2: // Quake 4
+ 			val = ptr;
+ 			ptr = memchr(ptr, '\0', end-ptr);
+ 			if ( !ptr )
+ 			{
+ 				malformed_packet( server, "player clan not null terminated" );
+-				cleanup_qserver( server, 1);
+-				return;
++				return PKT_ERROR;
+ 			}
+-			++ptr;
+ 			player->tribe_tag = strdup( val );
+-		}
++			ptr++;
++			debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, clan %s",
++					num_players, player->name, ping, rate, player_id, player->tribe_tag);
++			break;
++
++		case 5: // ETQW
++		case 6: // Wolfenstien
++			if ( 0xa0011 <= protocolver ) // clan tag since 10.17
++			{
++				// clantag position
++				ptr++;
++
++				// clantag
++				val = ptr;
++				ptr = memchr(ptr, '\0', end-ptr);
++				if ( !ptr )
++				{
++					malformed_packet( server, "player clan not null terminated" );
++					return PKT_ERROR;
++				}
++				player->tribe_tag = strdup( val );
++				ptr++;
++			}
+ 
+-		debug( 2, "Player[%d] = %s, prediction %hu, rate %u, id %hhu, clan %s",
+-				num_players, player->name, prediction, rate, player_id, player->tribe_tag);
++			// Bot flag
++			if ( 0xd0009 == protocolver || 0xd000a == protocolver ) // v13.9 or v13.10
++			{
++				// Bot flag is a single bit so need to realign everything from here on in :(
++				int i;
++				unsigned char *tp = (unsigned char*)ptr;
++				player->type_flag = (*tp)<<7;
++
++				// alignment is reset at the end
++				for( i = 0; i < 8 && tp < (unsigned char*)end; i++ )
++				{
++					*tp = (*tp)>>1 | *(tp+1)<<7;
++					tp++;
++				}
++			}
++			else
++			{
++				player->type_flag = *ptr++;
++			}
++
++			if ( 0xa0011 <= protocolver ) // clan tag since 10.17
++			{
++				debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hhu, clan %s",
++					num_players, player->name, ping, rate, player_id, player->type_flag, player->tribe_tag);
++			}
++			else
++			{
++				debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hhu",
++					num_players, player->name, ping, rate, player_id, player->type_flag );
++			}
++			break;
++
++		default:
++			debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu", num_players, player->name, ping, rate, player_id );
++		}
+ 
+ 		++num_players;
+ 	}
+ 
+-	if(end - ptr >= 4)
++	if( end - ptr >= 4 )
+ 	{
++		// OS Mask
+ 		snprintf(tmp, sizeof(tmp), "0x%X", swap_long_from_little(ptr));
+ 		add_rule( server, "osmask", tmp, NO_FLAGS );
+ 		debug( 2, "osmask %s", tmp);
+ 		ptr += 4;
++
++		if ( 5 == version && end - ptr >= 1 )
++		{
++			// Ranked flag
++			snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++			add_rule( server, "ranked", tmp, NO_FLAGS );
++
++			if ( end - ptr >= 5 )
++			{
++				// Time Left
++				snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++				add_rule( server, "timeleft", tmp, NO_FLAGS );
++				ptr += 4;
++
++				// Game State
++				snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++				add_rule( server, "gamestate", tmp, NO_FLAGS );
++
++				if ( end - ptr >= 1 )
++				{
++					// Server Type
++					unsigned char servertype = *ptr++;
++					snprintf( tmp, sizeof(tmp), "%hhu", servertype );
++					add_rule( server, "servertype", tmp, NO_FLAGS );
++
++					switch ( servertype )
++					{
++					case 0: // Regular Server
++						// Interested Clients
++						snprintf( tmp, sizeof(tmp), "%hhu", *ptr++ );
++						add_rule( server, "interested_clients", tmp, NO_FLAGS );
++						break;
++
++					case 1: // TV Server
++						// Connected Clients
++						snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++						ptr+=4;
++						add_rule( server, "interested_clients", tmp, NO_FLAGS );
++
++						// Max Clients
++						snprintf( tmp, sizeof(tmp), "%d", swap_long_from_little( ptr ) );
++						ptr+=4;
++						add_rule( server, "interested_clients", tmp, NO_FLAGS );
++						break;
++
++					default:
++						// Unknown
++						if ( show_errors )
++						{
++							fprintf( stderr, "Unknown server type %d\n", servertype );
++						}
++					}
++				}
++			}
++		}
+ 	}
+ 	else
+ 	{
+@@ -469,28 +645,45 @@
+ 	}
+ #endif
+ 
+-	server->num_players = num_players;
++	if ( 0 == tv )
++	{
++		debug( 2, "Num players = %d", num_players );
++		server->num_players = num_players;
++	}
++	else
++	{
++		server->num_players = viewers;
++	}
++
++	return DONE_FORCE;
++}
+ 
+-	cleanup_qserver( server, 1 );
+-	return;
++query_status_t deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen)
++{
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 1 );
++}
++
++query_status_t deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen)
++{
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 2 );
+ }
+ 
+-void deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_prey_demo_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+-	_deal_with_doom3_packet( server, rawpkt, pktlen, 1 );
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 4 );
+ }
+ 
+-void deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+-	_deal_with_doom3_packet( server, rawpkt, pktlen, 2 );
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 3 );
+ }
+ 
+-void deal_with_prey_demo_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_etqw_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+-	_deal_with_doom3_packet( server, rawpkt, pktlen, 4 );
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 5 );
+ }
+ 
+-void deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_wolf_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+-	_deal_with_doom3_packet( server, rawpkt, pktlen, 3 );
++	return _deal_with_doom3_packet( server, rawpkt, pktlen, 6 );
+ }
+diff -urP qstat-2.11/doom3.h qstat2/doom3.h
+--- qstat-2.11/doom3.h	2006-06-22 06:13:51.000000000 -0500
++++ qstat2/doom3.h	2009-08-14 21:22:30.738248000 -0400
+@@ -10,8 +10,6 @@
+ #ifndef QSTAT_DOOM3_H
+ #define QSTAT_DOOM3_H
+ 
+-#include "qstat.h"
+-
+ #define DOOM3_DEFAULT_PORT	27666
+ #define DOOM3_MASTER_DEFAULT_PORT	27650
+ 
+@@ -21,12 +19,22 @@
+ #define PREY_DEFAULT_PORT	27719
+ #define PREY_MASTER_DEFAULT_PORT	27655
+ 
+-void send_doom3master_request_packet( struct qserver *server);
+-void deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen);
++#define ETQW_DEFAULT_PORT	27733
++
++#define WOLF_DEFAULT_PORT	27758
++
++query_status_t send_doom3master_request_packet( struct qserver *server);
++query_status_t deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t send_quake4master_request_packet( struct qserver *server);
++query_status_t deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen);
+ 
+-void send_quake4master_request_packet( struct qserver *server);
+-void deal_with_quake4_packet( struct qserver *server, char *rawpkt, int pktlen);
++query_status_t deal_with_etqw_packet( struct qserver *server, char *rawpkt, int pktlen);
+ 
+-void deal_with_prey_packet( struct qserver *server, char *rawpkt, int pktlen);
++query_status_t deal_with_wolf_packet( struct qserver *server, char *rawpkt, int pktlen);
+ 
+ #endif
+diff -urP qstat-2.11/fl.c qstat2/fl.c
+--- qstat-2.11/fl.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/fl.c	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,512 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Frontlines-Fuel of War protocol
++ * Copyright 2008 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++#define FL_GETCHALLENGE	  "\xFF\xFF\xFF\xFF\x57"
++#define FL_CHALLENGERESPONSE 0x41
++#define FL_INFO			  "\xFF\xFF\xFF\xFF\x46LSQ"
++#define FL_INFORESPONSE  0x49
++#define FL_PLAYER			"\xFF\xFF\xFF\xFF\x55"
++#define FL_PLAYERRESPONSE	0x44
++#define FL_RULES			 "\xFF\xFF\xFF\xFF\x56"
++#define FL_RULESRESPONSE	 0x45
++
++struct fl_status
++{
++	unsigned sent_challenge : 1;
++	unsigned have_challenge : 1;
++	unsigned sent_info : 1;
++	unsigned have_info : 1;
++	unsigned sent_player : 1;
++	unsigned have_player : 1;
++	unsigned sent_rules : 1;
++	unsigned have_rules : 1;
++	unsigned challenge;
++	unsigned char type;
++};
++
++query_status_t send_fl_request_packet(struct qserver *server)
++{
++	struct fl_status* status = (struct fl_status*)server->master_query_tag;
++
++	if( SOCKET_ERROR == qserver_send_initial(server, FL_INFO, sizeof(FL_INFO) - 1) )
++	{
++		return SOCKET_ERROR;
++	}
++
++	status->sent_info = 1;
++	status->type = 0;
++
++	return INPROGRESS;
++}
++
++query_status_t send_fl_rule_request_packet(struct qserver *server)
++{
++	struct fl_status* status = (struct fl_status*)server->master_query_tag;
++
++	if ( 1 >= status->type )
++	{
++		// Not supported
++		return PKT_ERROR;
++	}
++
++	if(!get_server_rules && !get_player_info)
++	{
++		return DONE_FORCE;
++	}
++
++	do
++	{
++		if(!status->have_challenge)
++		{
++			debug(3, "sending challenge");
++			if( SOCKET_ERROR == qserver_send_initial(server, FL_GETCHALLENGE, sizeof(FL_GETCHALLENGE)-1) )
++			{
++				return SOCKET_ERROR;
++			}
++			status->sent_challenge = 1;
++			break;
++		}
++		else if(get_server_rules && !status->have_rules)
++		{
++			char buf[sizeof(FL_RULES)-1+4] = FL_RULES;
++			memcpy(buf+sizeof(FL_RULES)-1, &status->challenge, 4);
++			debug(3, "sending rule query");
++			if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++			{
++				return SOCKET_ERROR;
++			}
++			status->sent_rules = 1;
++			break;
++		}
++		else if(get_player_info && !status->have_player)
++		{
++			char buf[sizeof(FL_PLAYER)-1+4] = FL_PLAYER;
++			memcpy(buf+sizeof(FL_PLAYER)-1, &status->challenge, 4);
++			debug(3, "sending player query");
++			if( SOCKET_ERROR == qserver_send_initial(server, buf, sizeof(buf)) )
++			{
++				return SOCKET_ERROR;
++			}
++			status->sent_player = 1;
++			break;
++		}
++		else
++		{
++			debug(3, "timeout");
++			// we are probably called due to timeout, restart.
++			status->have_challenge = 0;
++			status->have_rules = 0;
++		}
++	} while(1);
++
++	return INPROGRESS;
++}
++
++query_status_t deal_with_fl_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	struct fl_status* status = (struct fl_status*)server->master_query_tag;
++	char* pkt = rawpkt;
++	char buf[16];
++	char* str;
++	unsigned cnt;
++	unsigned short tmp_short;
++
++	if(server->server_name == NULL)
++	{
++		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++		server->n_requests++;
++	}
++
++	if(pktlen < 5) goto out_too_short;
++
++	if( 0 == memcmp(pkt, "\xFF\xFF\xFF\xFE", 4) )
++	{
++		// fragmented packet
++		unsigned char pkt_index, pkt_max;
++		unsigned int pkt_id;
++		SavedData *sdata;
++
++		if(pktlen < 9) goto out_too_short;
++
++		// format:
++		// int Header
++		// int RequestId
++		// byte PacketNumber
++		// byte NumPackets
++		// Short SizeOfPacketSplits
++
++		// Header
++		pkt += 4;
++
++		// RequestId
++		pkt_id = ntohl( *(long *)pkt );
++		debug( 3, "RequestID: %d", pkt_id );
++		pkt += 4;
++
++		// The next two bytes are:
++		// 1. the max packets sent ( byte )
++		// 2. the index of this packet starting from 0 ( byte )
++		// 3. Size of the split ( short )
++		if(pktlen < 10) goto out_too_short;
++
++		// PacketNumber
++		pkt_index = ((unsigned char)*pkt);
++
++		// NumPackates
++		pkt_max = ((unsigned char)*(pkt+1));
++
++		// SizeOfPacketSplits
++		debug( 3, "packetid[2]: 0x%hhx => idx: %hhu, max: %hhu", *pkt, pkt_index, pkt_max );
++		pkt+=4;
++		pktlen -= 12;
++
++		// pkt_max is the total number of packets expected
++		// pkt_index is a bit mask of the packets received.
++		if ( server->saved_data.data == NULL )
++		{
++			sdata = &server->saved_data;
++		}
++		else
++		{
++			sdata = (SavedData*) calloc( 1, sizeof(SavedData));
++			sdata->next = server->saved_data.next;
++			server->saved_data.next = sdata;
++		}
++
++		sdata->pkt_index = pkt_index;
++		sdata->pkt_max = pkt_max;
++		sdata->pkt_id = pkt_id;
++		sdata->datalen = pktlen;
++		sdata->data= (char*)malloc( pktlen );
++		if ( NULL == sdata->data )
++		{
++			malformed_packet(server, "Out of memory");
++			return MEM_ERROR;
++		}
++
++		memcpy( sdata->data, pkt, sdata->datalen );
++
++		// combine_packets will call us recursively
++		return combine_packets( server );
++	}
++	else if ( 0 != memcmp(pkt, "\xFF\xFF\xFF\xFF", 4) )
++	{
++		malformed_packet(server, "invalid packet header");
++		return PKT_ERROR;
++	}
++
++	pkt += 4;
++	pktlen -= 4;
++
++	pktlen -= 1;
++	debug( 2, "FL type = 0x%x", *pkt );
++	switch(*pkt++)
++	{
++	case FL_CHALLENGERESPONSE:
++		if(pktlen < 4) goto out_too_short;
++		memcpy(&status->challenge, pkt, 4);
++		// do not count challenge as retry
++		if(!status->have_challenge && server->retry1 != n_retries)
++		{
++			++server->retry1;
++			if(server->n_retries)
++			{
++				--server->n_retries;
++			}
++		}
++		status->have_challenge = 1;
++		debug(3, "challenge %x", status->challenge);
++		break;
++
++	case FL_INFORESPONSE:
++		if(pktlen < 1) goto out_too_short;
++		status->type = *pkt;
++		if ( *pkt > 1 && ( get_server_rules || get_player_info ) )
++		{
++			 server->next_rule = ""; // trigger calling send_fl_rule_request_packet
++		}
++		snprintf(buf, sizeof(buf), "%hhX", *pkt);
++		add_rule(server, "protocol", buf, 0);
++		pktlen--;
++		pkt++;
++
++		// ServerName
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		server->server_name = strdup(pkt);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		// MapName
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		server->map_name = strdup(pkt);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		// ModName
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		server->game = strdup(pkt);
++		add_rule(server, "modname", pkt, 0);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		// GameMode
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		add_rule(server, "gamemode", pkt, 0);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		// GameDescription
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		add_rule(server, "gamedescription", pkt, 0);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		// GameVersion
++		str = memchr(pkt, '\0', pktlen);
++		if(!str) goto out_too_short;
++		add_rule(server, "gameversion", pkt, 0);
++		pktlen -= str-pkt+1;
++		pkt += str-pkt+1;
++
++		if( pktlen < 13 )
++		{
++			goto out_too_short;
++		}
++
++		// GamePort
++		tmp_short = ((unsigned short)pkt[0] <<8 ) | ((unsigned short)pkt[1]);
++		change_server_port( server, tmp_short, 0 );
++		pkt += 2;
++
++		// Num Players
++		server->num_players = (unsigned char)*pkt++;
++
++		// Max Players
++		server->max_players = (unsigned char)*pkt++;
++
++		// Dedicated
++		add_rule(server, "dedicated", ( 'd' == *pkt++) ? "1" : "0", 0);
++
++		// OS
++		switch( *pkt )
++		{
++		case 'l':
++			add_rule(server, "sv_os", "linux", 0);
++			break;
++
++		case 'w':
++			add_rule(server, "sv_os", "windows", 0);
++			break;
++
++		default:
++			buf[0] = *pkt;
++			buf[1] = '\0';
++			add_rule(server, "sv_os", buf, 0);
++			break;
++		}
++		pkt++;
++
++		// Passworded
++		add_rule(server, "passworded", ( *pkt++ ) ? "1" : "0" , 0);
++
++		// Anticheat
++		add_rule(server, "passworded", ( *pkt++ ) ? "1" : "0" , 0);
++
++		// FrameTime
++		sprintf( buf, "%hhu", *pkt++ );
++		add_rule(server, "frametime", buf , 0);
++
++		// Round
++		sprintf( buf, "%hhu", *pkt++ );
++		add_rule(server, "round", buf , 0);
++
++		// RoundMax
++		sprintf( buf, "%hhu", *pkt++ );
++		add_rule(server, "roundmax", buf , 0);
++
++		// RoundSeconds
++		tmp_short = ((unsigned short)pkt[0] <<8 ) | ((unsigned short)pkt[1]);
++		sprintf( buf, "%hhu", tmp_short );
++		add_rule(server, "roundseconds", buf , 0);
++		pkt += 2;
++
++		status->have_info = 1;
++
++		server->retry1 = n_retries;
++
++		server->next_player_info = server->num_players;
++
++		break;
++
++	case FL_RULESRESPONSE:
++		if(pktlen < 2) goto out_too_short;
++
++		cnt = ((unsigned char)pkt[0] << 8 ) + ((unsigned char)pkt[1]);
++		pktlen -= 2;
++		pkt += 2;
++
++		debug(3, "num_rules: %d", cnt);
++
++		for(;cnt && pktlen > 0; --cnt)
++		{
++			char* key, *value;
++			str = memchr(pkt, '\0', pktlen);
++			if(!str) break;
++			key = pkt;
++			pktlen -= str-pkt+1;
++			pkt += str-pkt+1;
++
++			str = memchr(pkt, '\0', pktlen);
++			if(!str) break;
++			value = pkt;
++			pktlen -= str-pkt+1;
++			pkt += str-pkt+1;
++
++			add_rule(server, key, value, NO_FLAGS);
++		}
++
++		if(cnt)
++		{
++			malformed_packet(server, "packet contains too few rules, missing %d", cnt);
++			server->missing_rules = 1;
++		}
++		if(pktlen)
++		malformed_packet(server, "garbage at end of rules, %d bytes left", pktlen);
++
++		status->have_rules = 1;
++
++		server->retry1 = n_retries;
++
++		break;
++
++	case FL_PLAYERRESPONSE:
++
++		if(pktlen < 1) goto out_too_short;
++
++		cnt = (unsigned char)pkt[0];
++		pktlen -= 1;
++		pkt += 1;
++
++		debug(3, "num_players: %d", cnt);
++
++		for(;cnt && pktlen > 0; --cnt)
++		{
++			unsigned idx;
++			const char* name;
++			struct player* p;
++
++			// Index
++			idx = *pkt++;
++			--pktlen;
++
++			// PlayerName
++			str = memchr(pkt, '\0', pktlen);
++			if(!str) break;
++			name = pkt;
++			pktlen -= str-pkt+1;
++			pkt += str-pkt+1;
++
++			if(pktlen < 8) goto out_too_short;
++
++			debug(3, "player index %d", idx);
++			p = add_player(server, server->n_player_info);
++			if(p)
++			{
++				union
++				{
++					int i;
++					float fl;
++				} temp;
++
++				p->name = strdup(name);
++
++				// Score
++				p->frags = ntohl( *(unsigned int *)pkt );
++
++				// TimeConnected
++				temp.i = ntohl( *(unsigned int *)(pkt+4) );
++				p->connect_time = temp.fl;
++
++				// Ping
++				p->ping = 0;
++				p->ping = ntohs( *(unsigned int *)(pkt+8) );
++				//((unsigned char*)&p->ping)[0] = pkt[9];
++				//((unsigned char*)&p->ping)[1] = pkt[8];
++
++				// ProfileId
++				//p->profileid = ntohl( *(unsigned int *)pkt+10 );
++
++				// Team
++				p->team = *(pkt+14);
++//fprintf( stderr, "Player: '%s', Frags: %u, Time: %u, Ping: %hu, Team: %d\n", p->name, p->frags,  p->connect_time, p->ping, p->team );
++			}
++			pktlen -= 15;
++			pkt += 15;
++		}
++
++#if 0 // seems to be a rather normal condition
++		if(cnt)
++		{
++			malformed_packet(server, "packet contains too few players, missing %d", cnt);
++		}
++#endif
++		if(pktlen)
++		malformed_packet(server, "garbage at end of player info, %d bytes left", pktlen);
++
++		status->have_player = 1;
++
++		server->retry1 = n_retries;
++
++		break;
++
++	default:
++		malformed_packet(server, "invalid packet id %hhx", *--pkt);
++		return PKT_ERROR;
++	}
++
++	if(
++		(!get_player_info || (get_player_info && status->have_player)) &&
++		(!get_server_rules || (get_server_rules && status->have_rules))
++	)
++	{
++		server->next_rule = NULL;
++	}
++
++	return DONE_AUTO;
++
++out_too_short:
++	malformed_packet(server, "packet too short");
++
++	return PKT_ERROR;
++}
++
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/fl.h qstat2/fl.h
+--- qstat-2.11/fl.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/fl.h	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,19 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Frontlines-Fuel of War protocol
++ * Copyright 2008 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_FL_H
++#define QSTAT_FL_H
++
++#include "qserver.h"
++
++query_status_t send_fl_request_packet(struct qserver *server);
++query_status_t send_fl_rule_request_packet(struct qserver *server);
++query_status_t deal_with_fl_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++#endif
+diff -urP qstat-2.11/gitmktar qstat2/gitmktar
+--- qstat-2.11/gitmktar	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/gitmktar	2008-02-04 07:27:02.608985000 -0500
+@@ -0,0 +1,11 @@
++#!/bin/sh
++
++# make snapshot tar archive from git-svn repo
++#
++NAME=qstat
++VERSION=2.11
++LAST_COMMIT=(`git-rev-list --timestamp HEAD^..HEAD`)
++DATE=`date +%Y%m%d%H%M -d "1970-01-01 00:00 UTC $LAST_COMMIT seconds"`
++fn=$NAME-${VERSION}_$DATE.tar.bz2
++git archive --prefix=$NAME-${VERSION}_$DATE/ HEAD | bzip2 > $fn
++echo "version ${VERSION}_$DATE -> $fn"
+Only in qstat-2.11: gnuconfig.h.in
+diff -urP qstat-2.11/gps.c qstat2/gps.c
+--- qstat-2.11/gps.c	2006-06-09 10:30:11.000000000 -0500
++++ qstat2/gps.c	2009-07-02 06:11:41.032181000 -0400
+@@ -59,13 +59,13 @@
+     return 0;
+ }
+ 
+-void send_gps_request_packet( struct qserver *server )
++query_status_t send_gps_request_packet( struct qserver *server )
+ {
+-    send_packet( server, server->type->status_packet, server->type->status_len );
++	return send_packet( server, server->type->status_packet, server->type->status_len );
+ }
+ 
+ 
+-void deal_with_gps_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gps_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ 	char *s, *key, *value, *end;
+ 	struct player *player = NULL;
+@@ -465,6 +465,8 @@
+ 		( server->num_players < 0 && id_minor >= 3)
+ 	)
+ 	{
+-		cleanup_qserver( server, 1);
++		return DONE_FORCE;
+ 	}
++
++	return INPROGRESS;
+ }
+diff -urP qstat-2.11/gps.h qstat2/gps.h
+--- qstat-2.11/gps.h	2005-06-20 07:43:46.000000000 -0500
++++ qstat2/gps.h	2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+ 
+ // Packet processing methods
+-void deal_with_gps_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_gps_request_packet( struct qserver *server );
++query_status_t deal_with_gps_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_gps_request_packet( struct qserver *server );
+ 
+ #endif
+ 
+diff -urP qstat-2.11/gs2.c qstat2/gs2.c
+--- qstat-2.11/gs2.c	2006-08-24 13:52:25.000000000 -0500
++++ qstat2/gs2.c	2009-07-02 06:11:41.032181000 -0400
+@@ -20,7 +20,7 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+ 
+-void send_gs2_request_packet( struct qserver *server )
++query_status_t send_gs2_request_packet( struct qserver *server )
+ {
+ 	// The below should work but seems to make no difference to what some
+ 	// servers send
+@@ -35,13 +35,13 @@
+ 		server->type->status_packet[9] = 0x00;
+ 	}
+ 
+-	send_packet( server, server->type->status_packet, server->type->status_len );
++	return send_packet( server, server->type->status_packet, server->type->status_len );
+ }
+ 
+ 
+ // See the following for protocol details:
+ // http://dev.kquery.com/index.php?article=42
+-void deal_with_gs2_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs2_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ 	char *ptr = rawpkt;
+ 	char *end = rawpkt + pktlen;
+@@ -57,8 +57,7 @@
+ 	if ( pktlen < 15 )
+ 	{
+ 		// invalid packet?
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	server->n_servers++;
+@@ -95,8 +94,7 @@
+ 			{
+ 				malformed_packet( server, "no player headers" );
+ 			}
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 		ptr += var_len + 1;
+ 
+@@ -125,6 +123,11 @@
+ 		{
+ 			server->map_name = strdup( val );
+ 		}
++		else if( 0 == strcmp( var, "map" ) )
++		{
++			// For BF2MC compatibility
++			server->map_name = strdup( val );
++		}
+ 		else if( 0 == strcmp( var, "maxplayers" ) )
+ 		{
+ 			server->max_players = atoi( val );
+@@ -154,8 +157,7 @@
+ 		// no more info should be player headers here as we
+ 		// requested it
+ 		malformed_packet( server, "no player headers" );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	// player info header
+@@ -169,8 +171,7 @@
+ 	if ( ptr >= end )
+ 	{
+ 		malformed_packet( server, "no player headers" );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	while ( 1 == type && ptr < end )
+@@ -188,8 +189,7 @@
+ 			{
+ 				free( headers );
+ 			}
+-			cleanup_qserver( server, 1);
+-			return;
++			return MEM_ERROR;
+ 		}
+ 
+ 		headers = tmpp;
+@@ -211,8 +211,7 @@
+ 		// no more info should be player info here as we
+ 		// requested it
+ 		malformed_packet( server, "no players" );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	while( 2 == type && ptr < end )
+@@ -225,8 +224,7 @@
+ 			if ( 0 != no_players )
+ 			{
+ 				malformed_packet( server, "no players" );
+-				cleanup_qserver( server, 1);
+-				return;
++				return PKT_ERROR;
+ 			}
+ 		}
+ 		else
+@@ -242,8 +240,7 @@
+ 				if ( ptr >= end )
+ 				{
+ 					malformed_packet( server, "short player detail" );
+-					cleanup_qserver( server, 1);
+-					return;
++					return PKT_ERROR;
+ 				}
+ 				val = ptr;
+ 				val_len = strlen( val );
+@@ -292,8 +289,7 @@
+ 		if ( total_players > no_players )
+ 		{
+ 			malformed_packet( server, "to many players %d > %d", total_players, no_players );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 
+ 		// check for end of player info
+@@ -302,8 +298,7 @@
+ 			if ( total_players != no_players )
+ 			{
+ 				malformed_packet( server, "bad number of players %d != %d", total_players, no_players );
+-				cleanup_qserver( server, 1);
+-				return;
++				return PKT_ERROR;
+ 			}
+ 			type = 3;
+ 			ptr++;
+@@ -315,8 +310,7 @@
+ 		// no more info should be team info here as we
+ 		// requested it
+ 		malformed_packet( server, "no teams" );
+-		cleanup_qserver( server, 1 );
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	no_teams = (unsigned char)*ptr;
+@@ -340,8 +334,7 @@
+ 			{
+ 				free( headers );
+ 			}
+-			cleanup_qserver( server, 1);
+-			return;
++			return MEM_ERROR;
+ 		}
+ 
+ 		headers = tmpp;
+@@ -362,8 +355,7 @@
+ 		// no more info should be team info here as we
+ 		// requested it
+ 		malformed_packet( server, "no teams" );
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	while( 4 == type && ptr < end )
+@@ -378,8 +370,7 @@
+ 			if ( ptr >= end )
+ 			{
+ 				malformed_packet( server, "short team detail" );
+-				cleanup_qserver( server, 1);
+-				return;
++				return PKT_ERROR;
+ 			}
+ 			val = ptr;
+ 			val_len = strlen( val );
+@@ -398,11 +389,9 @@
+ 		if ( total_teams > no_teams )
+ 		{
+ 			malformed_packet( server, "to many teams" );
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 	}
+ 
+-	cleanup_qserver( server, 1);
+-	return;
++	return DONE_FORCE;
+ }
+diff -urP qstat-2.11/gs2.h qstat2/gs2.h
+--- qstat-2.11/gs2.h	2005-06-12 07:41:52.000000000 -0500
++++ qstat2/gs2.h	2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+ 
+ // Packet processing methods
+-void deal_with_gs2_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_gs2_request_packet( struct qserver *server );
++query_status_t deal_with_gs2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_gs2_request_packet( struct qserver *server );
+ 
+ #endif
+ 
+diff -urP qstat-2.11/gs3.c qstat2/gs3.c
+--- qstat-2.11/gs3.c	2006-08-15 14:38:38.000000000 -0500
++++ qstat2/gs3.c	2012-01-20 05:31:58.410876000 -0500
+@@ -51,7 +51,7 @@
+ //	query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"]
+ //
+ 
+-void deal_with_gs3_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs3_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ 	char *ptr = rawpkt;
+ 	unsigned int pkt_id;
+@@ -59,14 +59,48 @@
+ 	unsigned char flag;
+ 	unsigned int pkti, final;
+ 
+-	debug( 2, "packet..." );
++	debug( 2, "packet n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) );
++	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++	if ( 7 == pktlen && 0x09 == *ptr )
++	{
++		// gs4 query sent to a gs3 server
++		// switch protocols and try again
++
++		// Correct the stats due to two phase protocol
++		int i;
++		server_type *gs3_type = &builtin_types[50];
++		debug( 3, "Attempting gs3 fallback from gs4" );
++		server->retry1++;
++		if ( GAMESPY3_PROTOCOL_SERVER != gs3_type->id )
++		{
++			// Static coding failure do it the hard way
++			debug( 1, "gs3 static lookup failure, using dynamic lookup" );
++			for( i = 0; i < GAMESPY3_PROTOCOL_SERVER; i++ )
++			{
++				if ( GAMESPY3_PROTOCOL_SERVER == builtin_types[i].id )
++				{
++					// found it
++					gs3_type = &builtin_types[i];
++					i = GAMESPY3_PROTOCOL_SERVER;
++				}
++			}
++
++			if ( GAMESPY3_PROTOCOL_SERVER != gs3_type->id )
++			{
++				malformed_packet( server, "GS3 protocol not found" );
++				return PKT_ERROR;
++			}
++		}
++		server->type = gs3_type;
++		return send_gs3_request_packet( server );
++	}
+ 
+ 	if ( pktlen < 12 )
+ 	{
+ 		// invalid packet?
+ 		malformed_packet( server, "too short" );
+-		cleanup_qserver( server, 1 );
+-		return;
++		return PKT_ERROR;
+ 	}
+ 
+ 	if ( 0x09 == *ptr )
+@@ -78,39 +112,20 @@
+ 		memcpy( &pkt_id, ptr, 4 );
+ 		ptr += 4;
+ 		server->challenge = atoi( ptr );
+-
+-		// Correct the stats due to two phase protocol
++		debug( 3, "Challenge: %ld", server->challenge );
+ 		server->retry1++;
+-		server->n_packets--;
+-		if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
+-		{
+-			server->n_requests--;
+-		}
+-		else
+-		{
+-			server->n_retries--;
+-		}
+-		send_gs3_request_packet( server );
+-		return;
++
++		return send_gs3_request_packet( server );
+ 	}
+ 
+ 	if ( 0x00 != *ptr )
+ 	{
+ 		malformed_packet( server, "bad initial byte '%hhx'", *ptr );
+-		cleanup_qserver( server, 1 );
+-		return;
++		return PKT_ERROR;
+ 	}
+ 	ptr++;
+ 
+ 	server->n_servers++;
+-	if ( server->server_name == NULL )
+-	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
+-	}
+-	else
+-	{
+-		gettimeofday( &server->packet_time1, NULL);
+-	}
+ 
+ 	// Could check the header here should
+ 	// match the 4 byte id sent
+@@ -123,14 +138,12 @@
+ 		if ( server->flags & TF_STATUS_QUERY )
+ 		{
+ 			// we have the status response
+-			deal_with_gs3_status( server, ptr, pktlen - ( ptr - rawpkt )  );
+-			return;
++			return deal_with_gs3_status( server, ptr, pktlen - ( ptr - rawpkt )  );
+ 		}
+ 		else
+ 		{
+ 			malformed_packet( server, "missing splitnum" );
+-			cleanup_qserver( server, 1 );
+-			return;
++			return PKT_ERROR;
+ 		}
+ 	}
+ 	ptr += 9;
+@@ -167,20 +180,18 @@
+ 		if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) )
+ 		{
+ 			// fatal error e.g. out of memory
+-			return;
++			return MEM_ERROR;
+ 		}
+ 
+ 		// combine_packets will call us recursively
+-		combine_packets( server );
+-		return;
++		return combine_packets( server );
+ 	}
+ 
+ 	// if we get here we have what should be a full packet
+-	process_gs3_packet( server );
+-	return;
++	return process_gs3_packet( server );
+ }
+ 
+-void deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ 	char *pkt = rawpkt;
+ 	debug( 1, "status packet" );
+@@ -194,8 +205,90 @@
+ 	pkt += strlen( pkt ) + 1;
+ 
+ 	// map
+-	server->map_name = strdup( pkt );
+-	pkt += strlen( pkt ) + 1;
++	// UT3 retail compatibility
++	if ( 0 == strncmp( pkt, "OwningPlayerId", 13 ) )
++	{
++		char *end = pkt + strlen( pkt ) + 1;
++		char *var = pkt;
++		while ( NULL != var )
++		{
++			char *next;
++			char *val = strstr( var, "=" );
++			*val = '\0';
++			val++;
++			next = strstr( val, "," );
++			if ( NULL != next )
++			{
++				*next = '\0';
++				next++;
++			}
++
++			if ( 0 == strcmp( var, "mapname" ) )
++			{
++				if ( server->map_name )
++				{
++					free( server->map_name );
++				}
++				server->map_name = strdup( val );
++			}
++			else if ( 0 == strcmp( var, "p1073741825" ) )
++			{
++				if ( server->map_name )
++				{
++					free( server->map_name );
++				}
++				server->map_name = strdup( val );
++			}
++			else if ( 0 == strcmp( var, "p1073741826" ) )
++			{
++				add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "p268435705" ) )
++			{
++				add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "p1073741827" ) )
++			{
++				add_rule( server, "description", val, OVERWITE_DUPLICATES );
++#ifndef UT3_PATCHED
++				if ( 0 != strlen( val ) )
++				{
++					if ( server->server_name )
++					{
++						char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++						if ( name )
++						{
++							strcat( name, ": " );
++							strcat( name, val );
++							server->server_name = name;
++						}
++					}
++					server->server_name = strdup( val );
++				}
++#endif
++			}
++			else if ( 0 == strcmp( var, "p268435704" ) )
++			{
++				add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "s32779" ) )
++			{
++				add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
++			}
++			else
++			{
++				add_rule( server, var, val, OVERWITE_DUPLICATES );
++			}
++
++			var = next;
++		}
++		pkt = end;
++	}
++	else
++	{
++		server->map_name = strdup( pkt );
++		pkt += strlen( pkt ) + 1;
++	}
+ 
+ 	// num players
+ 	server->num_players = atoi( pkt );
+@@ -209,7 +302,7 @@
+ 	change_server_port( server, atoi( pkt ), 0 );
+ 	pkt += strlen( pkt ) + 1;
+ 
+-	cleanup_qserver( server, 1 );
++	return DONE_FORCE;
+ }
+ 
+ int process_gs3_packet( struct qserver *server )
+@@ -231,13 +324,17 @@
+ 		char *end = ptr + pktlen;
+ 		debug( 2, "processing fragment[%d]...", fragment->pkt_index );
+ 
++		if( 6 <= get_debug_level() )
++		{
++			print_packet( server, ptr, pktlen );
++		}
++
+ 		// check we have a full header
+ 		if ( pktlen < 16 )
+ 		{
+ 			// invalid packet?
+ 			malformed_packet( server, "too short" );
+-			cleanup_qserver( server, 1 );
+-			return 0;
++			return PKT_ERROR;
+ 		}
+ 
+ 		// skip over the header
+@@ -251,7 +348,15 @@
+ 			char *var, *val;
+ 			int var_len, val_len;
+ 
+-			if ( 0x00 == ptr[0] && 0x01 == ptr[1] )
++			if ( ptr + 1 >= end )
++			{
++				debug( 4, "state = %d, %hhx, bytes left = %d", state, ptr[0], (int)(end - ptr) );
++			}
++			else
++			{
++				debug( 4, "state = %d, %hhx, %hhx, bytes left = %d", state, ptr[0], ptr[1], (int)(end - ptr) );
++			}
++			if ( 0x00 == ptr[0] && ( ptr + 1 >= end || 0x01 >= ptr[1] ) )
+ 			{
+ 				// not quite sure of the significance of these bytes
+ 				// but we use them as a check for end of section
+@@ -267,8 +372,7 @@
+ 			if ( ptr + 1 > end )
+ 			{
+ 				malformed_packet( server, "no rule value" );
+-				cleanup_qserver( server, 1);
+-				return 0;
++				return PKT_ERROR;
+ 			}
+ 
+ 			val = ptr;
+@@ -294,6 +398,89 @@
+ 			}
+ 			else if( 0 == strcmp( var, "mapname" ) )
+ 			{
++				// UT3 retail compatibility
++				if ( 0 == strncmp( val, "OwningPlayerId", 13 ) )
++				{
++					var = val;
++					while ( NULL != var )
++					{
++						char *next;
++						char *val = strstr( var, "=" );
++						*val = '\0';
++						val++;
++						next = strstr( val, "," );
++						if ( NULL != next )
++						{
++							*next = '\0';
++							next++;
++						}
++
++						if ( 0 == strcmp( var, "mapname" ) )
++						{
++							if ( server->map_name )
++							{
++								free( server->map_name );
++							}
++							server->map_name = strdup( val );
++						}
++						else if ( 0 == strcmp( var, "p1073741825" ) )
++						{
++							if ( server->map_name )
++							{
++								free( server->map_name );
++							}
++							server->map_name = strdup( val );
++						}
++						else if ( 0 == strcmp( var, "p1073741826" ) )
++						{
++							add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++						}
++						else if ( 0 == strcmp( var, "p268435705" ) )
++						{
++							add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++						}
++						else if ( 0 == strcmp( var, "p1073741827" ) )
++						{
++#ifndef UT3_PATCHED
++							if ( 0 != strlen( val ) )
++							{
++								if ( server->server_name )
++								{
++									char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++									if ( name )
++									{
++										strcat( name, ": " );
++										strcat( name, val );
++										server->server_name = name;
++									}
++								}
++								server->server_name = strdup( val );
++							}
++#endif
++						}
++						else if ( 0 == strcmp( var, "p268435704" ) )
++						{
++							add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++						}
++						else if ( 0 == strcmp( var, "s32779" ) )
++						{
++							add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
++						}
++						else
++						{
++							add_rule( server, var, val, OVERWITE_DUPLICATES );
++						}
++						var = next;
++					}
++				}
++				else
++				{
++					server->map_name = strdup( val );
++				}
++			}
++			else if( 0 == strcmp( var, "map" ) )
++			{
++				// BF2MC compatibility
+ 				server->map_name = strdup( val );
+ 			}
+ 			else if( 0 == strcmp( var, "maxplayers" ) )
+@@ -308,43 +495,98 @@
+ 			else if( 0 == strcmp( var, "hostport" ) )
+ 			{
+ 				change_server_port( server, atoi( val ), 0 );
+-				add_rule( server, var, val, NO_FLAGS );
++			}
++			else if ( 0 == strcmp( var, "p1073741825" ) )
++			{
++				// UT3 demo compatibility
++				if ( server->map_name )
++				{
++					free( server->map_name );
++				}
++				server->map_name = strdup( val );
++			}
++			else if ( 0 == strcmp( var, "p1073741826" ) )
++			{
++				// UT3 demo compatibility
++				add_rule( server, "gametype", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "p268435705" ) )
++			{
++				// UT3 demo compatibility
++				add_rule( server, "timelimit", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "p1073741827" ) )
++			{
++				// UT3 demo compatibility
++				add_rule( server, "description", val, OVERWITE_DUPLICATES );
++#ifndef UT3_PATCHED
++				if ( 0 != strlen( val ) )
++				{
++					if ( server->server_name )
++					{
++						char *name = (char*)realloc( server->server_name, strlen( server->server_name ) + strlen( val ) + 3 );
++						if ( name )
++						{
++							strcat( name, ": " );
++							strcat( name, val );
++							server->server_name = name;
++						}
++					}
++					server->server_name = strdup( val );
++				}
++#endif
++			}
++			else if ( 0 == strcmp( var, "p268435704" ) )
++			{
++				// UT3 demo compatibility
++				add_rule( server, "goalscore", val, OVERWITE_DUPLICATES );
++			}
++			else if ( 0 == strcmp( var, "s32779" ) )
++			{
++				// UT3 demo compatibility
++				add_rule( server, "gamemode", val, OVERWITE_DUPLICATES );
+ 			}
+ 			else
+ 			{
+-				add_rule( server, var, val, NO_FLAGS );
++				add_rule( server, var, val, OVERWITE_DUPLICATES );
+ 			}
+ 		}
+ 
++
+ 		while ( 1 == state && ptr < end )
+ 		{
+ 			// first we have the header
+ 			char *header = ptr;
+ 			int head_len = strlen( header );
+ 			int header_type;
+-			ptr += head_len + 1;
++
++			debug( 4, "state = %d, bytes left = %d, head_len = %d", state, (int)(end - ptr), head_len );
+ 
+ 			if ( 0 == head_len )
+ 			{
+ 				// no more info
+ 				debug( 3, "All done" );
+-				cleanup_qserver( server, 1 );
+-				return 1;
++				return DONE_FORCE;
+ 			}
+ 
+-			debug( 2, "player header '%s'", header );
++			ptr += head_len + 1;
+ 
+-			if ( ptr > end )
++			if ( ptr >= end )
+ 			{
+-				malformed_packet( server, "no details for header '%s'", header );
+-				cleanup_qserver( server, 1 );
+-				return 0;
++				// partial header, should be restarted in the next fragment
++				debug( 5, "partial header '%s'", header );
++				// ensure gt than
++				ptr++;
++
++				break;
+ 			}
+ 
++			debug( 2, "player header '%s'", header );
++
+ 			// the next byte is the starting number
+ 			total_players = *ptr++;
+ 
+-			if ( 0 == strcmp( header, "player_" ) )
++			if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) )
+ 			{
+ 				header_type = PLAYER_NAME_HEADER;
+ 			}
+@@ -384,7 +626,7 @@
+ 				// check for end of this headers player info
+ 				if ( 0x00 == *ptr )
+ 				{
+-					debug( 3, "end of '%s' detail", header );
++					debug( 3, "end of '%s' detail, %d bytes left", header, (int)(end - ptr) );
+ 					ptr++;
+ 					// Note: can't check ( total_players != no_players ) here as we may have more packets
+ 					if ( ptr < end && 0x00 == *ptr )
+@@ -406,8 +648,7 @@
+ 				if ( ptr >= end )
+ 				{
+ 					malformed_packet( server, "short player detail" );
+-					cleanup_qserver( server, 1);
+-					return 0;
++					return PKT_ERROR;
+ 				}
+ 				val = ptr;
+ 				val_len = strlen( val );
+@@ -462,13 +703,14 @@
+ 				if ( total_players > no_players )
+ 				{
+ 					malformed_packet( server, "to many players %d > %d", total_players, no_players );
+-					cleanup_qserver( server, 1 );
+-					return 0;
++					return PKT_ERROR;
+ 				}
+ 			}
+ 		}
+ 
+-		if ( 2 == state )
++		debug( 4, "state = %d, bytes left = %d", state, (int)(end - ptr) );
++
++		if ( 2 == state && ptr < end )
+ 		{
+ 			no_teams = (unsigned char)*ptr;
+ 			ptr++;
+@@ -489,8 +731,14 @@
+ 			{
+ 				// no more info
+ 				debug( 3, "All done" );
+-				cleanup_qserver( server, 1 );
+-				return 1;
++				return DONE_FORCE;
++			}
++
++			if ( ptr >= end )
++			{
++				// partial header, should be restarted in the next fragment
++				debug( 5, "partial header '%s'", header );
++				break;
+ 			}
+ 
+ 			debug( 2, "team header '%s'", header );
+@@ -516,8 +764,7 @@
+ 				if ( ptr >= end )
+ 				{
+ 					malformed_packet( server, "short team detail" );
+-					cleanup_qserver( server, 1);
+-					return 0;
++					return PKT_ERROR;
+ 				}
+ 				val = ptr;
+ 				val_len = strlen( val );
+@@ -552,11 +799,10 @@
+ 		}
+ 	}
+ 
+-	cleanup_qserver( server, 1 );
+-	return 1;
++	return DONE_FORCE;
+ }
+ 
+-void send_gs3_request_packet( struct qserver *server )
++query_status_t send_gs3_request_packet( struct qserver *server )
+ {
+ 	char *packet;
+ 	char query_buf[128];
+@@ -615,5 +861,5 @@
+ 		}
+ 	}
+ 
+-	send_packet( server, packet, len );
++	return send_packet( server, packet, len );
+ }
+diff -urP qstat-2.11/gs3.h qstat2/gs3.h
+--- qstat-2.11/gs3.h	2005-07-16 22:45:08.000000000 -0500
++++ qstat2/gs3.h	2009-07-02 06:11:41.032181000 -0400
+@@ -13,8 +13,8 @@
+ #include "qserver.h"
+ 
+ // Packet processing methods
+-void deal_with_gs3_packet( struct qserver *server, char *pkt, int pktlen );
+-void deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen );
+-void send_gs3_request_packet( struct qserver *server );
++query_status_t deal_with_gs3_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_gs3_status( struct qserver *server, char *rawpkt, int pktlen );
++query_status_t send_gs3_request_packet( struct qserver *server );
+ 
+ #endif
+diff -urP qstat-2.11/haze.c qstat2/haze.c
+--- qstat-2.11/haze.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/haze.c	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,628 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * New Haze query protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <netinet/in.h>
++#include <sys/socket.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++// Format:
++// 1 - 8: Challenge Request / Response
++char haze_challenge[] = {
++    'f', 'r', 'd', 'c', '_', '_', '_', '_'
++};
++
++int process_haze_packet( struct qserver *server );
++
++// Player headers
++#define PLAYER_NAME_HEADER 1
++#define PLAYER_SCORE_HEADER 2
++#define PLAYER_DEATHS_HEADER 3
++#define PLAYER_PING_HEADER 4
++#define PLAYER_KILLS_HEADER 5
++#define PLAYER_TEAM_HEADER 6
++#define PLAYER_OTHER_HEADER 7
++
++// Team headers
++#define TEAM_NAME_HEADER 1
++#define TEAM_OTHER_HEADER 2
++
++// Challenge response algorithum
++// Before sending a qr2 query (type 0x00) the client must first send a
++// challenge request (type 0x09).  The host will respond with the same
++// packet type containing a string signed integer.
++//
++// Once the challenge is received the client should convert the string to a
++// network byte order integer and embed it in the keys query.
++//
++// Example:
++//
++// 	challenge request: [0xFE][0xFD][0x09][0x.. 4-byte-instance]
++//	challenge response: [0x09][0x.. 4-byte-instance]["-1287574694"]
++//	query: [0xFE][0xFD][0x00][0x.. 4-byte-instance][0xb3412b5a "-1287574694"]
++//
++
++query_status_t deal_with_haze_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *ptr = rawpkt;
++	unsigned int pkt_id;
++	unsigned short len;
++	unsigned char pkt_max, pkt_index;
++
++	debug( 2, "packet..." );
++
++	if ( pktlen < 8 )
++	{
++		// invalid packet
++		malformed_packet( server, "too short" );
++		return PKT_ERROR;
++	}
++
++	if ( 0 == strncmp( ptr, "frdcr", 5 ) )
++	{
++		// challenge response
++		ptr += 8;
++		server->challenge = 1;
++
++		// Correct the stats due to two phase protocol
++		server->retry1++;
++		server->n_packets--;
++		if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
++		{
++			//server->n_requests--;
++		}
++		else
++		{
++			server->n_retries--;
++		}
++		return send_haze_request_packet( server );
++	}
++
++	if ( pktlen < 12 )
++	{
++		// invalid packet
++		malformed_packet( server, "too short" );
++		return PKT_ERROR;
++	}
++
++	server->n_servers++;
++	if ( server->server_name == NULL )
++	{
++		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++	}
++	else
++	{
++		gettimeofday( &server->packet_time1, NULL);
++	}
++
++	// Query version ID
++	ptr += 4;
++
++	// Could check the header here should
++	// match the 4 byte id sent
++	memcpy( &pkt_id, ptr, 4 );
++	ptr += 4;
++
++
++	// Max plackets
++	pkt_max = ((unsigned char)*ptr);
++	ptr++;
++
++	// Packet ID
++	pkt_index = ((unsigned char)*ptr);
++	ptr++;
++
++	// Query Length
++	//len = (unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8);
++	//len = swap_short_from_little( ptr );
++	debug( 1, "%04hx, %04hx", (unsigned short)ptr[0], ((unsigned short)ptr[1] << 8) );
++	//len = (unsigned short)(unsigned short)ptr[0] | ((unsigned short)ptr[1] << 8);
++	// TODO: fix this crap
++	memcpy( &len, ptr+1, 1 );
++	//memcpy( &len+1, ptr, 1 );
++	//memcpy( &len, ptr, 2 );
++	ptr += 2;
++
++	debug( 1, "pkt_index = %d, pkt_max = %d, len = %d", pkt_index, pkt_max, len );
++	if ( 0 != pkt_max )
++	{
++		// not a single packet response or callback
++		debug( 2, "pkt_max %d", pkt_max );
++
++		if ( 0 == pkt_index )
++		{
++			// to prevent reprocessing when we get the call back
++			// override the packet flag so it looks like a single
++			// packet response
++			rawpkt[8] = '\0';
++		}
++
++		// add the packet recalcing maxes
++		if ( ! add_packet( server, pkt_id, pkt_index, pkt_max, pktlen, rawpkt, 1 ) )
++		{
++			// fatal error e.g. out of memory
++			return MEM_ERROR;
++		}
++
++		// combine_packets will call us recursively
++		return combine_packets( server );
++	}
++
++	// if we get here we have what should be a full packet
++	return process_haze_packet( server );
++}
++
++query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *pkt = rawpkt;
++	int len;
++	debug( 1, "status packet" );
++
++
++	// Server name
++	server->server_name = strdup( pkt );
++	pkt += strlen( pkt ) + 1;
++
++	// gametype
++	add_rule( server, "gametype", pkt, NO_FLAGS );
++	pkt += strlen( pkt ) + 1;
++
++	// map
++	len = strlen( pkt );
++	// remove .res from map names
++	if ( 0 == strncmp( pkt + len - 4, ".res", 4 ) )
++	{
++		*(pkt + len - 4) = '\0';
++	}
++	server->map_name = strdup( pkt );
++	pkt += len + 1;
++
++	// num players
++	server->num_players = atoi( pkt );
++	pkt += strlen( pkt ) + 1;
++
++	// max_players
++	server->max_players = atoi( pkt );
++	pkt += strlen( pkt ) + 1;
++
++	// hostport
++	change_server_port( server, atoi( pkt ), 0 );
++	pkt += strlen( pkt ) + 1;
++
++	return DONE_FORCE;
++}
++
++int process_haze_packet( struct qserver *server )
++{
++	unsigned char state = 0;
++	unsigned char no_players = 0;
++	unsigned char total_players = 0;
++	unsigned char no_teams = 0;
++	unsigned char total_teams = 0;
++	int pkt_index = 0;
++	SavedData *fragment;
++
++	debug( 2, "processing packet..." );
++
++	while ( NULL != ( fragment = get_packet_fragment( pkt_index++ ) ) )
++	{
++		int pktlen = fragment->datalen;
++		char *ptr = fragment->data;
++		char *end = ptr + pktlen;
++		debug( 2, "processing fragment[%d]...", fragment->pkt_index );
++
++		// check we have a full header
++		if ( pktlen < 12 )
++		{
++			// invalid packet
++			malformed_packet( server, "too short" );
++			return PKT_ERROR;
++		}
++
++		// skip over the header
++		//server->protocol_version = atoi( val+1 );
++		ptr += 12;
++
++		// 4 * null's signifies the end of a section
++
++		// Basic Info
++		while ( 0 == state && ptr < end )
++		{
++			// name value pairs null seperated
++			char *var, *val;
++			int var_len, val_len;
++
++			if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] )
++			{
++				// end of rules
++				state++;
++				ptr += 4;
++				break;
++			}
++
++			var = ptr;
++			var_len = strlen( var );
++			ptr += var_len + 1;
++
++			if ( ptr + 1 > end )
++			{
++				malformed_packet( server, "no basic value" );
++				return PKT_ERROR;
++			}
++
++			val = ptr;
++			val_len = strlen( val );
++			ptr += val_len + 1;
++			debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len );
++
++			// Lets see what we've got
++			if ( 0 == strcmp( var, "serverName" ) )
++			{
++				server->server_name = strdup( val );
++			}
++			else if( 0 == strcmp( var, "map" ) )
++			{
++				// remove .res from map names
++				if ( 0 == strncmp( val + val_len - 4, ".res", 4 ) )
++				{
++					*(val + val_len - 4) = '\0';
++				}
++				server->map_name = strdup( val );
++			}
++			else if( 0 == strcmp( var, "maxPlayers" ) )
++			{
++				server->max_players = atoi( val );
++
++			}
++			else if( 0 == strcmp( var, "currentPlayers" ) )
++			{
++				server->num_players = no_players = atoi( val );
++			}
++			else
++			{
++				add_rule( server, var, val, NO_FLAGS );
++			}
++		}
++
++		// rules
++		while ( 1 == state && ptr < end )
++		{
++			// name value pairs null seperated
++			char *var, *val;
++			int var_len, val_len;
++
++			if ( ptr+4 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] && 0x00 == ptr[2] && 0x00 == ptr[3] )
++			{
++				// end of basic
++				state++;
++				ptr += 4;
++				break;
++			}
++			var = ptr;
++			var_len = strlen( var );
++			ptr += var_len + 1;
++
++			if ( ptr + 1 > end )
++			{
++				malformed_packet( server, "no basic value" );
++				return PKT_ERROR;
++			}
++
++			val = ptr;
++			val_len = strlen( val );
++			ptr += val_len + 1;
++			debug( 2, "var:%s (%d)=%s (%d)\n", var, var_len, val, val_len );
++
++			// add the rule
++			add_rule( server, var, val, NO_FLAGS );
++		}
++
++		// players
++		while ( 2 == state && ptr < end )
++		{
++			// first we have the header
++			char *header = ptr;
++			int head_len = strlen( header );
++			ptr += head_len + 1;
++
++			if ( ptr+2 <= end && 0x00 == ptr[0] && 0x00 == ptr[1] )
++			{
++				// end of player headers
++				state++;
++				ptr += 2;
++				break;
++			}
++
++			if ( 0 == head_len )
++			{
++				// no more info
++				debug( 3, "All done" );
++				return DONE_FORCE;
++			}
++
++			debug( 2, "player header '%s'", header );
++
++			if ( ptr > end )
++			{
++				malformed_packet( server, "no details for header '%s'", header );
++				return PKT_ERROR;
++			}
++
++		}
++
++		while ( 3 == state && ptr < end )
++		{
++			char *header = ptr;
++			int head_len = strlen( header );
++			int header_type;
++			// the next byte is the starting number
++			total_players = *ptr++;
++
++			if ( 0 == strcmp( header, "player_" ) || 0 == strcmp( header, "name_" ) )
++			{
++				header_type = PLAYER_NAME_HEADER;
++			}
++			else if ( 0 == strcmp( header, "score_" ) )
++			{
++				header_type = PLAYER_SCORE_HEADER;
++			}
++			else if ( 0 == strcmp( header, "deaths_" ) )
++			{
++				header_type = PLAYER_DEATHS_HEADER;
++			}
++			else if ( 0 == strcmp( header, "ping_" ) )
++			{
++				header_type = PLAYER_PING_HEADER;
++			}
++			else if ( 0 == strcmp( header, "kills_" ) )
++			{
++				header_type = PLAYER_KILLS_HEADER;
++			}
++			else if ( 0 == strcmp( header, "team_" ) )
++			{
++				header_type = PLAYER_TEAM_HEADER;
++			}
++			else
++			{
++				header_type = PLAYER_OTHER_HEADER;
++			}
++
++			while( ptr < end )
++			{
++				// now each player details
++				// add the player
++				struct player *player;
++				char *val;
++				int val_len;
++
++				// check for end of this headers player info
++				if ( 0x00 == *ptr )
++				{
++					debug( 3, "end of '%s' detail", header );
++					ptr++;
++					// Note: can't check ( total_players != no_players ) here as we may have more packets
++					if ( ptr < end && 0x00 == *ptr )
++					{
++						debug( 3, "end of players" );
++						// end of all player headers / detail
++						state = 2;
++						ptr++;
++					}
++					break;
++				}
++
++				player = get_player_by_number( server, total_players );
++				if ( NULL == player )
++				{
++					player = add_player( server, total_players );
++				}
++
++				if ( ptr >= end )
++				{
++					malformed_packet( server, "short player detail" );
++					return PKT_ERROR;
++				}
++				val = ptr;
++				val_len = strlen( val );
++				ptr += val_len + 1;
++
++				debug( 2, "Player[%d][%s]=%s\n", total_players, header, val );
++
++				// lets see what we got
++				switch( header_type )
++				{
++				case PLAYER_NAME_HEADER:
++					player->name = strdup( val );
++					break;
++
++				case PLAYER_SCORE_HEADER:
++					player->score = atoi( val );
++					break;
++
++				case PLAYER_DEATHS_HEADER:
++					player->deaths = atoi( val );
++					break;
++
++				case PLAYER_PING_HEADER:
++					player->ping = atoi( val );
++					break;
++
++				case PLAYER_KILLS_HEADER:
++					player->frags = atoi( val );
++					break;
++
++				case PLAYER_TEAM_HEADER:
++					player->team = atoi( val );
++					break;
++
++				case PLAYER_OTHER_HEADER:
++				default:
++					if ( '_' == header[head_len-1] )
++					{
++						header[head_len-1] = '\0';
++						player_add_info( player, header, val, NO_FLAGS );
++						header[head_len-1] = '_';
++					}
++					else
++					{
++						player_add_info( player, header, val, NO_FLAGS );
++					}
++					break;
++				}
++
++				total_players++;
++
++				if ( total_players > no_players )
++				{
++					malformed_packet( server, "to many players %d > %d", total_players, no_players );
++					return PKT_ERROR;
++				}
++			}
++		}
++
++		if ( 3 == state )
++		{
++			no_teams = (unsigned char)*ptr;
++			ptr++;
++
++			debug( 2, "No teams:%d\n", no_teams );
++			state = 3;
++		}
++
++		while ( 4 == state && ptr < end )
++		{
++			// first we have the header
++			char *header = ptr;
++			int head_len = strlen( header );
++			int header_type;
++			ptr += head_len + 1;
++
++			if ( 0 == head_len )
++			{
++				// no more info
++				debug( 3, "All done" );
++				return DONE_FORCE;
++			}
++
++			debug( 2, "team header '%s'", header );
++			if ( 0 == strcmp( header, "team_t" ) )
++			{
++				header_type = TEAM_NAME_HEADER;
++			}
++			else
++			{
++				header_type = TEAM_OTHER_HEADER;
++			}
++
++			// the next byte is the starting number
++			total_teams = *ptr++;
++
++			while( ptr < end )
++			{
++				// now each teams details
++				char *val;
++				int val_len;
++				char rule[512];
++
++				if ( ptr >= end )
++				{
++					malformed_packet( server, "short team detail" );
++					return PKT_ERROR;
++				}
++				val = ptr;
++				val_len = strlen( val );
++				ptr += val_len + 1;
++
++				debug( 2, "Team[%d][%s]=%s\n", total_teams, header, val );
++
++				// lets see what we got
++				switch ( header_type )
++				{
++				case TEAM_NAME_HEADER:
++					// BF being stupid again teams 1 based instead of 0
++					players_set_teamname( server, total_teams + 1, val );
++					// N.B. yes no break
++
++				case TEAM_OTHER_HEADER:
++				default:
++					// add as a server rule
++					sprintf( rule, "%s%d", header, total_teams );
++					add_rule( server, rule, val, NO_FLAGS );
++					break;
++				}
++
++				total_teams++;
++				if ( 0x00 == *ptr )
++				{
++					// end of this headers teams
++					ptr++;
++					break;
++				}
++			}
++		}
++	}
++
++	return DONE_FORCE;
++}
++
++query_status_t send_haze_request_packet( struct qserver *server )
++{
++	char *packet;
++	char query_buf[128];
++	size_t len;
++	unsigned char required = HAZE_BASIC_INFO;
++
++	if ( get_server_rules )
++	{
++		required |= HAZE_GAME_RULES;
++		server->flags |= TF_PLAYER_QUERY;
++	}
++
++	if ( get_player_info )
++	{
++		required |= HAZE_PLAYER_INFO;
++		required |= HAZE_TEAM_INFO;
++		server->flags |= TF_RULES_QUERY;
++	}
++
++	server->flags |= TF_STATUS_QUERY;
++
++	if ( server->challenge )
++	{
++		// we've recieved a challenge response, send the query + challenge id
++		len = sprintf(
++			query_buf,
++			"frdquery%c%c%c%c%c",
++			(unsigned char)(server->challenge >> 24),
++			(unsigned char)(server->challenge >> 16),
++			(unsigned char)(server->challenge >> 8),
++			(unsigned char)(server->challenge >> 0),
++			required
++		);
++		packet = query_buf;
++	}
++	else
++	{
++		// Either basic v3 protocol or challenge request
++		packet = haze_challenge;
++		len = sizeof( haze_challenge );
++	}
++
++	return send_packet( server, packet, len );
++}
+diff -urP qstat-2.11/haze.h qstat2/haze.h
+--- qstat-2.11/haze.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/haze.h	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,27 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * New Haze query protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_HAZE_H
++#define QSTAT_HAZE_H
++
++
++#include "qserver.h"
++
++#define HAZE_BASIC_INFO 0x01
++#define HAZE_GAME_RULES 0x02
++#define HAZE_PLAYER_INFO 0x04
++#define HAZE_TEAM_INFO 0x08
++
++// Packet processing methods
++query_status_t deal_with_haze_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_haze_status( struct qserver *server, char *rawpkt, int pktlen );
++query_status_t send_haze_request_packet( struct qserver *server );
++
++
++#endif
+Only in qstat-2.11/info: Makefile.in
+Only in qstat-2.11: install-sh
+diff -urP qstat-2.11/LICENSE.txt qstat2/LICENSE.txt
+--- qstat-2.11/LICENSE.txt	2002-11-13 21:33:54.000000000 -0500
++++ qstat2/LICENSE.txt	2008-09-09 23:18:37.952285000 -0400
+@@ -1,130 +1,201 @@
+-The Artistic License
++		       The Artistic License 2.0
++
++	    Copyright (c) 2000-2006, The Perl Foundation.
++
++     Everyone is permitted to copy and distribute verbatim copies
++      of this license document, but changing it is not allowed.
+ 
+ Preamble
+-    The intent of this document is to state the conditions
+-    under which a Package may be copied, such that the
+-    Copyright Holder maintains some semblance of artistic
+-    control over the development of the package, while
+-    giving the users of the package the right to use and
+-    distribute the Package in a more-or-less customary
+-    fashion, plus the right to make reasonable modifications.
+-
+-Definitions:
+-      "Package" refers to the collection of files
+-      distributed by the Copyright Holder, and derivatives
+-      of that collection of files created through textual
+-      modification.
+-
+-      "Standard Version" refers to such a Package if it has
+-      not been modified, or has been modified in accordance
+-      with the wishes of the Copyright Holder.
+-
+-      "Copyright Holder" is whoever is named in the
+-      copyright or copyrights for the package.
+-
+-      "You" is you, if you're thinking about copying or
+-      distributing this Package.
+-
+-      "Reasonable copying fee" is whatever you can justify
+-      on the basis of media cost, duplication charges, time
+-      of people involved, and so on. (You will not be
+-      required to justify it to the Copyright Holder, but
+-      only to the computing community at large as a market
+-      that must bear the fee.)
+-
+-      "Freely Available" means that no fee is charged for
+-      the item itself, though there may be fees involved in
+-      handling the item. It also means that recipients of
+-      the item may redistribute it under the same conditions
+-      they received it.
+-
+-    1. You may make and give away verbatim copies of the
+-    source form of the Standard Version of this Package
+-    without restriction, provided that you duplicate all of
+-    the original copyright notices and associated
+-    disclaimers.
+-
+-    2. You may apply bug fixes, portability fixes and other
+-    modifications derived from the Public Domain or from the
+-    Copyright Holder. A Package modified in such a way shall
+-    still be considered the Standard Version.
+-
+-    3. You may otherwise modify your copy of this Package in
+-    any way, provided that you insert a prominent notice in
+-    each changed file stating how and when you changed that
+-    file, and provided that you do at least ONE of the
+-    following:
+-
+-      a) place your modifications in the Public Domain or
+-      otherwise make them Freely Available, such as by
+-      posting said modifications to Usenet or an equivalent
+-      medium, or placing the modifications on a major
+-      archive site such as ftp.uu.net, or by allowing the
+-      Copyright Holder to include your modifications in the
+-      Standard Version of the Package.
+-
+-      b) use the modified Package only within your
+-      corporation or organization.
+-
+-      c) rename any non-standard executables so the names do
+-      not conflict with standard executables, which must
+-      also be provided, and provide a separate manual page
+-      for each non-standard executable that clearly
+-      documents how it differs from the Standard Version.
+-
+-      d) make other distribution arrangements with the
+-      Copyright Holder.
+-
+-    4. You may distribute the programs of this Package in
+-    object code or executable form, provided that you do at
+-    least ONE of the following:
+-
+-      a) distribute a Standard Version of the executables
+-      and library files, together with instructions (in the
+-      manual page or equivalent) on where to get the
+-      Standard Version.
+-
+-      b) accompany the distribution with the
+-      machine-readable source of the Package with your
+-      modifications.
+-
+-      c) accompany any non-standard executables with their
+-      corresponding Standard Version executables, giving the
+-      non-standard executables non-standard names, and
+-      clearly documenting the differences in manual pages
+-      (or equivalent), together with instructions on where
+-      to get the Standard Version.
+-
+-      d) make other distribution arrangements with the
+-      Copyright Holder.
+-
+-    5. You may charge a reasonable copying fee for any
+-    distribution of this Package. You may charge any fee you
+-    choose for support of this Package. You may not charge a
+-    fee for this Package itself. However, you may distribute
+-    this Package in aggregate with other (possibly
+-    commercial) programs as part of a larger (possibly
+-    commercial) software distribution provided that you do
+-    not advertise this Package as a product of your own.
+-
+-    6. The scripts and library files supplied as input to or
+-    produced as output from the programs of this Package do
+-    not automatically fall under the copyright of this
+-    Package, but belong to whomever generated them, and may
+-    be sold commercially, and may be aggregated with this
+-    Package.
+-
+-    7. C subroutines supplied by you and linked into
+-    this Package shall not be considered part of this
+-    Package.
+-
+-    8. The name of the Copyright Holder may not be used to
+-    endorse or promote products derived from this software
+-    without specific prior written permission.
+-
+-    9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY
+-    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+-    LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+-    AND FITNESS FOR A PARTICULAR PURPOSE.
+ 
+-    The End
++This license establishes the terms under which a given free software
++Package may be copied, modified, distributed, and/or redistributed.
++The intent is that the Copyright Holder maintains some artistic
++control over the development of that Package while still keeping the
++Package available as open source and free software.
++
++You are always permitted to make arrangements wholly outside of this
++license directly with the Copyright Holder of a given Package.  If the
++terms of this license do not permit the full use that you propose to
++make of the Package, you should contact the Copyright Holder and seek
++a different licensing arrangement. 
++
++Definitions
++
++    "Copyright Holder" means the individual(s) or organization(s)
++    named in the copyright notice for the entire Package.
++
++    "Contributor" means any party that has contributed code or other
++    material to the Package, in accordance with the Copyright Holder's
++    procedures.
++
++    "You" and "your" means any person who would like to copy,
++    distribute, or modify the Package.
++
++    "Package" means the collection of files distributed by the
++    Copyright Holder, and derivatives of that collection and/or of
++    those files. A given Package may consist of either the Standard
++    Version, or a Modified Version.
++
++    "Distribute" means providing a copy of the Package or making it
++    accessible to anyone else, or in the case of a company or
++    organization, to others outside of your company or organization.
++
++    "Distributor Fee" means any fee that you charge for Distributing
++    this Package or providing support for this Package to another
++    party.  It does not mean licensing fees.
++
++    "Standard Version" refers to the Package if it has not been
++    modified, or has been modified only in ways explicitly requested
++    by the Copyright Holder.
++
++    "Modified Version" means the Package, if it has been changed, and
++    such changes were not explicitly requested by the Copyright
++    Holder. 
++
++    "Original License" means this Artistic License as Distributed with
++    the Standard Version of the Package, in its current version or as
++    it may be modified by The Perl Foundation in the future.
++
++    "Source" form means the source code, documentation source, and
++    configuration files for the Package.
++
++    "Compiled" form means the compiled bytecode, object code, binary,
++    or any other form resulting from mechanical transformation or
++    translation of the Source form.
++
++
++Permission for Use and Modification Without Distribution
++
++(1)  You are permitted to use the Standard Version and create and use
++Modified Versions for any purpose without restriction, provided that
++you do not Distribute the Modified Version.
++
++
++Permissions for Redistribution of the Standard Version
++
++(2)  You may Distribute verbatim copies of the Source form of the
++Standard Version of this Package in any medium without restriction,
++either gratis or for a Distributor Fee, provided that you duplicate
++all of the original copyright notices and associated disclaimers.  At
++your discretion, such verbatim copies may or may not include a
++Compiled form of the Package.
++
++(3)  You may apply any bug fixes, portability changes, and other
++modifications made available from the Copyright Holder.  The resulting
++Package will still be considered the Standard Version, and as such
++will be subject to the Original License.
++
++
++Distribution of Modified Versions of the Package as Source 
++
++(4)  You may Distribute your Modified Version as Source (either gratis
++or for a Distributor Fee, and with or without a Compiled form of the
++Modified Version) provided that you clearly document how it differs
++from the Standard Version, including, but not limited to, documenting
++any non-standard features, executables, or modules, and provided that
++you do at least ONE of the following:
++
++    (a)  make the Modified Version available to the Copyright Holder
++    of the Standard Version, under the Original License, so that the
++    Copyright Holder may include your modifications in the Standard
++    Version.
++
++    (b)  ensure that installation of your Modified Version does not
++    prevent the user installing or running the Standard Version. In
++    addition, the Modified Version must bear a name that is different
++    from the name of the Standard Version.
++
++    (c)  allow anyone who receives a copy of the Modified Version to
++    make the Source form of the Modified Version available to others
++    under
++		
++	(i)  the Original License or
++
++	(ii)  a license that permits the licensee to freely copy,
++	modify and redistribute the Modified Version using the same
++	licensing terms that apply to the copy that the licensee
++	received, and requires that the Source form of the Modified
++	Version, and of any works derived from it, be made freely
++	available in that license fees are prohibited but Distributor
++	Fees are allowed.
++
++
++Distribution of Compiled Forms of the Standard Version 
++or Modified Versions without the Source
++
++(5)  You may Distribute Compiled forms of the Standard Version without
++the Source, provided that you include complete instructions on how to
++get the Source of the Standard Version.  Such instructions must be
++valid at the time of your distribution.  If these instructions, at any
++time while you are carrying out such distribution, become invalid, you
++must provide new instructions on demand or cease further distribution.
++If you provide valid instructions or cease distribution within thirty
++days after you become aware that the instructions are invalid, then
++you do not forfeit any of your rights under this license.
++
++(6)  You may Distribute a Modified Version in Compiled form without
++the Source, provided that you comply with Section 4 with respect to
++the Source of the Modified Version.
++
++
++Aggregating or Linking the Package 
++
++(7)  You may aggregate the Package (either the Standard Version or
++Modified Version) with other packages and Distribute the resulting
++aggregation provided that you do not charge a licensing fee for the
++Package.  Distributor Fees are permitted, and licensing fees for other
++components in the aggregation are permitted. The terms of this license
++apply to the use and Distribution of the Standard or Modified Versions
++as included in the aggregation.
++
++(8) You are permitted to link Modified and Standard Versions with
++other works, to embed the Package in a larger work of your own, or to
++build stand-alone binary or bytecode versions of applications that
++include the Package, and Distribute the result without restriction,
++provided the result does not expose a direct interface to the Package.
++
++
++Items That are Not Considered Part of a Modified Version 
++
++(9) Works (including, but not limited to, modules and scripts) that
++merely extend or make use of the Package, do not, by themselves, cause
++the Package to be a Modified Version.  In addition, such works are not
++considered parts of the Package itself, and are not subject to the
++terms of this license.
++
++
++General Provisions
++
++(10)  Any use, modification, and distribution of the Standard or
++Modified Versions is governed by this Artistic License. By using,
++modifying or distributing the Package, you accept this license. Do not
++use, modify, or distribute the Package, if you do not accept this
++license.
++
++(11)  If your Modified Version has been derived from a Modified
++Version made by someone other than you, you are nevertheless required
++to ensure that your Modified Version complies with the requirements of
++this license.
++
++(12)  This license does not grant you the right to use any trademark,
++service mark, tradename, or logo of the Copyright Holder.
++
++(13)  This license includes the non-exclusive, worldwide,
++free-of-charge patent license to make, have made, use, offer to sell,
++sell, import and otherwise transfer the Package with respect to any
++patent claims licensable by the Copyright Holder that are necessarily
++infringed by the Package. If you institute patent litigation
++(including a cross-claim or counterclaim) against any party alleging
++that the Package constitutes direct or contributory patent
++infringement, then this Artistic License to you shall terminate on the
++date that such litigation is filed.
++
++(14)  Disclaimer of Warranty:
++THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
++IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
++WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
++NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
++LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
++BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
++DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
++ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+diff -urP qstat-2.11/Makefile.am qstat2/Makefile.am
+--- qstat-2.11/Makefile.am	2006-08-04 15:42:09.000000000 -0500
++++ qstat2/Makefile.am	2012-04-20 08:51:31.440424000 -0400
+@@ -20,7 +20,20 @@
+ 	gs2.c gs2.h \
+ 	gs3.c gs3.h \
+ 	ts2.c ts2.h \
+-	tm.c tm.h
++	tm.c tm.h \
++	haze.c haze.h \
++	ottd.c ottd.h \
++	wic.c wic.h \
++	fl.c fl.h \
++	tee.c tee.h \
++	cube2.c cube2.h \
++	ts3.c ts3.h \
++	bfbc2.c bfbc2.h \
++	ventrilo.c ventrilo.h \
++	mumble.c mumble.h \
++	terraria.c terraria.h \
++	crysis.c crysis.h \
++	utils.c utils.h
+ 
+ dist_configfiles_DATA = qstat.cfg
+ configfilesdir = $(sysconfdir)
+@@ -33,4 +46,4 @@
+ 
+ cl:
+ 	cvs2cl.pl --utc --no-wrap --separate-header --no-times -f ChangeLog.cvs
+-	rm -f ChangeLog.cvs.bak
++	rm -f ChangeLog.cvs.bak qstat.core
+Only in qstat-2.11: Makefile.in
+diff -urP qstat-2.11/Makefile.noauto qstat2/Makefile.noauto
+--- qstat-2.11/Makefile.noauto	2006-10-28 07:36:58.000000000 -0500
++++ qstat2/Makefile.noauto	2012-04-20 08:51:31.440424000 -0400
+@@ -5,17 +5,17 @@
+ #LDLIBS  =
+ 
+ #CFLAGS += -Dsysconfdir=\"/etc\"
+-CFLAGS = -DDEBUG=1
++CFLAGS = -DDEBUG=1 -DENABLE_DUMP=1
+ 
+ ## NOTE: if you get errors when linking qstat (missing symbols or
+ ## libraries), then modify LDFLAGS or LDLIBS
+ 
+-SRC = config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c
++SRC = config.c debug.c hcache.c md5.c qserver.c qstat.c template.c ut2004.c a2s.c packet_manip.c gs3.c gs2.c gps.c ts2.c doom3.c tm.c haze.c wic.c ottd.c fl.c tee.c ts3.c bfbc2.c ventrilo.c cube2.c mumble.c terraria.c crysis.c utils.c
+ OBJ = $(SRC:.c=.obj)
+ O   = $(SRC:.c=.o)
+ 
+ SOLARIS_LIBS = -lsocket -lnsl
+-WINDOWS_LIBS = /ML wsock32.lib
++WINDOWS_LIBS = wsock32.lib
+ OS2_LIBS     = so32dll.lib tcp32dll.lib
+ EMX_LIBS     = -lsocket
+ 
+@@ -39,7 +39,7 @@
+ 
+ windows_debug: $(SRC)
+ 	rm -f *.pdb
+-	$(CC) $(CFLAGS) /Zi /ML $(SRC) /Feqstat.exe wsock32.lib /link /fixed:no /incremental:no
++	$(CC) $(CFLAGS) /Zi $(SRC) /Feqstat.exe $(WINDOWS_LIBS) /link /fixed:no /incremental:no
+ 
+ os2: $(SRC)
+ 	$(CC) /Q /W0 /C+ $(SRC)
+diff -urP qstat-2.11/md5.c qstat2/md5.c
+--- qstat-2.11/md5.c	2004-10-17 15:41:52.000000000 -0500
++++ qstat2/md5.c	2012-04-20 08:51:31.440424000 -0400
+@@ -21,7 +21,7 @@
+   ghost at aladdin.com
+ 
+  */
+-/* $Id: md5.c,v 1.1 2004/10/17 20:41:52 l-n Exp $ */
++/* $Id: md5.c 375 2012-04-20 12:51:31Z stevenhartland $ */
+ /*
+   Independent implementation of MD5 (RFC 1321).
+ 
+@@ -379,3 +379,26 @@
+     for (i = 0; i < 16; ++i)
+ 	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+ }
++
++static const char hexchar[] = "0123456789abcdef";
++
++char* md5_hex(const char *bytes, int nbytes)
++{
++	char out[33];
++	char* o = out+32;
++	char digest[16];
++	char *digestp = digest + 16;
++	md5_state_t md5;
++
++	out[32] = '\0';
++	md5_init(&md5);
++	md5_append(&md5, (unsigned char*)bytes, nbytes);
++	md5_finish(&md5, (unsigned char*)digest);
++	do
++	{
++		*--o = hexchar[*--digestp&0x0F];
++		*--o = hexchar[(*digestp>>4)&0x0F];
++	} while(o != out);
++
++	return strdup(out);
++}
+diff -urP qstat-2.11/md5.h qstat2/md5.h
+--- qstat-2.11/md5.h	2004-10-17 15:41:53.000000000 -0500
++++ qstat2/md5.h	2012-04-20 06:22:15.250385000 -0400
+@@ -21,7 +21,7 @@
+   ghost at aladdin.com
+ 
+  */
+-/* $Id: md5.h,v 1.1 2004/10/17 20:41:53 l-n Exp $ */
++/* $Id: md5.h 374 2012-04-20 10:22:15Z stevenhartland $ */
+ /*
+   Independent implementation of MD5 (RFC 1321).
+ 
+@@ -84,6 +84,9 @@
+ /* Finish the message and return the digest. */
+ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+ 
++/* Return an hex md5 of the given string */
++char* md5_hex(const char *bytes, int nbytes);
++
+ #ifdef __cplusplus
+ }  /* end extern "C" */
+ #endif
+Only in qstat-2.11: missing
+diff -urP qstat-2.11/mumble.c qstat2/mumble.c
+--- qstat-2.11/mumble.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/mumble.c	2012-02-07 08:55:59.913866000 -0500
+@@ -0,0 +1,70 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Mumble protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#ifndef _WIN32
++#include <arpa/inet.h>
++#endif
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_mumble_request_packet( struct qserver *server )
++{
++	return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_mumble_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	// skip unimplemented ack, crc, etc
++	char *pkt = rawpkt;
++	char bandwidth[11];
++	char version[11];
++
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	if ( 24 != pktlen || 0 != memcmp( pkt+4, server->type->status_packet+4, 8 ) )
++	{
++		// unknown packet
++		return PKT_ERROR;
++	}
++
++	// version
++	server->protocol_version = ntohl(*(unsigned long*)pkt);
++	sprintf(version,"%u.%u.%u", (unsigned char)*pkt+1, (unsigned char)*pkt+2, (unsigned char)*pkt+3);
++	add_rule( server, "version", version, NO_FLAGS );
++	pkt += 4;
++
++	// ident
++	pkt += 8;
++
++	// num players
++	server->num_players = ntohl(*(unsigned long*)pkt);
++	pkt += 4;
++
++	// max players
++	server->max_players = ntohl(*(unsigned long*)pkt);
++	pkt += 4;
++
++	// allowed bandwidth
++	sprintf( bandwidth, "%d", ntohl(*(unsigned long*)pkt));
++	add_rule( server, "allowed_bandwidth", bandwidth, NO_FLAGS );
++	pkt += 4;
++
++	// Unknown details
++	server->map_name = strdup( "N/A" );
++	server->server_name = strdup( "Unknown" );
++	add_rule( server, "gametype", "Unknown", NO_FLAGS );
++
++	return DONE_FORCE;
++}
+diff -urP qstat-2.11/mumble.h qstat2/mumble.h
+--- qstat-2.11/mumble.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/mumble.h	2012-01-20 05:33:19.302785000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Mumble protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_MUMBLE_H
++#define QSTAT_MUMBLE_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_mumble_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_mumble_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/ottd.c qstat2/ottd.c
+--- qstat-2.11/ottd.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ottd.c	2010-01-25 11:39:12.583979000 -0500
+@@ -0,0 +1,344 @@
++/*
++ * qstat 2.11
++ *
++ * opentTTD protocol
++ * Copyright 2007 Ludwig Nussel
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#endif
++#include <stdio.h>
++#include <stdlib.h>
++
++#include "qstat.h"
++#include "qserver.h"
++#include "debug.h"
++
++enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 };
++
++static const char* vehicle_types[] = {
++	"num_trains",
++	"num_trucks",
++	"num_busses",
++	"num_aircrafts",
++	"num_ships",
++};
++
++static const char* station_types[] = {
++	"num_stations",
++	"num_truckbays",
++	"num_busstations",
++	"num_airports",
++	"num_docks",
++};
++
++query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	unsigned num;
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	server->server_name = MASTER;
++
++	if(swap_short_from_little(rawpkt) != pktlen)
++	{
++		malformed_packet( server, "invalid packet length" );
++		return PKT_ERROR;
++	}
++	if(rawpkt[2] != 7)
++	{
++		malformed_packet( server, "invalid packet type" );
++		return PKT_ERROR;
++	}
++
++	if(rawpkt[3] != 1)
++	{
++		malformed_packet( server, "invalid packet version" );
++		return PKT_ERROR;
++	}
++
++	num = swap_short_from_little(&rawpkt[4]);
++	rawpkt += 6;
++	pktlen -= 6;
++	if( num && num*6 <= pktlen )
++	{
++		unsigned i;
++		server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen );
++		memset(server->master_pkt + server->master_pkt_len, 0, pktlen );
++		server->master_pkt_len += pktlen;
++		for( i = 0; i < num * 6; i += 6 )
++		{
++			memcpy(&server->master_pkt[i], &rawpkt[i], 4);
++			server->master_pkt[i+4] = rawpkt[i+5];
++			server->master_pkt[i+5] = rawpkt[i+4];
++		}
++		server->n_servers += num;
++	}
++	else
++	{
++		malformed_packet( server, "invalid packet" );
++		return PKT_ERROR;
++	}
++
++	bind_sockets();
++
++	return DONE_AUTO;
++}
++
++#define xstr(s) str(s)
++#define str(s) #s
++
++#define GET_STRING do { \
++		str = (char*)ptr; \
++		ptr = memchr(ptr, '\0', end-ptr); \
++		if ( !ptr ) \
++		{ \
++			malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \
++			return PKT_ERROR; \
++		} \
++		++ptr; \
++	} while(0)
++
++#define FAIL_IF(cond, msg) \
++	if((cond)) { \
++		malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \
++		return PKT_ERROR; \
++	}
++
++#define INVALID_IF(cond) \
++	FAIL_IF(cond, "invalid packet")
++
++query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	unsigned char *ptr = (unsigned char*)rawpkt;
++	unsigned char *end = (unsigned char*)(rawpkt + pktlen);
++	unsigned char type;
++	char* str;
++	char buf[32];
++	unsigned ver;
++
++	server->n_servers++;
++	if ( server->server_name == NULL)
++	{
++		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++		server->n_requests++;
++	}
++	else
++	{
++		gettimeofday( &server->packet_time1, NULL);
++	}
++
++	FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet");
++
++	type = ptr[2];
++	ver = ptr[3];
++	ptr += 4;
++
++	debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver);
++
++	FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported");
++
++	if(type == 1) // info packet
++	{
++		unsigned numgrf = *ptr;
++		FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number");
++		ptr += numgrf * 20 + 1;
++
++		snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++		add_rule(server, "date_days", buf, NO_FLAGS);
++		ptr += 4;
++
++		snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++		add_rule(server, "startdate_days", buf, NO_FLAGS);
++		ptr += 4;
++
++		FAIL_IF(ptr + 3 > end, "invalid packet");
++
++		snprintf(buf, sizeof(buf), "%hhu", ptr[0]);
++		add_rule(server, "maxcompanies", buf, NO_FLAGS);
++		snprintf(buf, sizeof(buf), "%hhu", ptr[1]);
++		add_rule(server, "numcompanies", buf, NO_FLAGS);
++		server->max_spectators = ptr[2];
++		ptr += 3;
++
++		GET_STRING;
++		server->server_name = strdup(str);
++
++		GET_STRING;
++		add_rule(server, "version", str, NO_FLAGS);
++
++		FAIL_IF(ptr + 7 > end, "invalid packet");
++
++		{
++			static const char* langs[] = {
++				"any",
++				"English",
++				"German",
++				"French"
++			};
++			unsigned i = *ptr++;
++			if(i > 3) i = 0;
++			add_rule(server, "language", (char*)langs[i], NO_FLAGS);
++		}
++
++		add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS);
++
++		server->max_players = *ptr++;
++		server->num_players = *ptr++;
++		server->num_spectators = *ptr++;
++
++		GET_STRING;
++
++		server->map_name = strdup(str);
++
++		snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++		add_rule(server, "map_width", buf, NO_FLAGS);
++		snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++		add_rule(server, "map_height", buf, NO_FLAGS);
++
++		{
++			static const char* sets[] = {
++				"temperate",
++				"arctic",
++				"desert",
++				"toyland"
++			};
++			unsigned i = *ptr++;
++			if(i > 3) i = 0;
++			add_rule(server, "map_set", (char*)sets[i], NO_FLAGS);
++		}
++
++		add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS);
++	}
++	else if(type == 3) // player packet
++	{
++		unsigned i, j;
++		INVALID_IF(ptr + 2 > end);
++
++		server->num_players = *ptr++;
++
++		for(i = 0; i < server->num_players; ++i)
++		{
++			unsigned long long lli;
++			struct player* player;
++			unsigned char nr;
++
++			nr = *ptr++;
++
++			debug(3, "player number %d", nr);
++			player = add_player(server, i);
++			FAIL_IF(!player, "can't allocate player");
++
++			GET_STRING;
++			player->name = strdup(str);
++			debug(3, "name %s", str);
++			player->frags = 0;
++
++			INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end);
++
++			snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
++			player_add_info(player, "startdate", buf, 0);
++			ptr += 4;
++
++			lli = swap_long_from_little(ptr+4);
++			lli <<= 32;
++			lli += swap_long_from_little(ptr);
++			snprintf(buf, sizeof(buf), "%lld", lli);
++			player_add_info(player, "value", buf, 0);
++			ptr += 8;
++
++			lli = swap_long_from_little(ptr+4);
++			lli <<= 32;
++			lli = swap_long_from_little(ptr);
++			snprintf(buf, sizeof(buf), "%lld", lli);
++			player_add_info(player, "money", buf, 0);
++			ptr += 8;
++
++			lli = swap_long_from_little(ptr+4);
++			lli <<= 32;
++			lli += swap_long_from_little(ptr);
++			snprintf(buf, sizeof(buf), "%lld", lli);
++			player_add_info(player, "income", buf, 0);
++			ptr += 8;
++
++			snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++			player_add_info(player, "performance", buf, 0);
++			ptr += 2;
++
++			player_add_info(player, "password", *ptr?"1":"0", 0);
++			++ptr;
++
++			for (j = 0; j < MAX_VEHICLE_TYPES; ++j)
++			{
++				snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++				player_add_info(player, (char*)vehicle_types[j], buf, 0);
++				ptr += 2;
++			}
++			for (j = 0; j < MAX_STATION_TYPES; ++j)
++			{
++				snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
++				player_add_info(player, (char*)station_types[j], buf, 0);
++				ptr += 2;
++			}
++			
++			if (ver != 5)
++			{
++				// connections
++				while(ptr + 1 < end && *ptr)
++				{
++					++ptr;
++					GET_STRING; // client name
++					debug(3, "%s played by %s", str, player->name);
++					GET_STRING; // id
++					INVALID_IF(ptr + 4 > end);
++					ptr += 4;
++				}
++
++				++ptr; // record terminated by zero byte
++			}
++		}
++
++		// spectators
++		while(ptr + 1 < end && *ptr)
++		{
++			++ptr;
++			GET_STRING; // client name
++			debug(3, "spectator %s", str);
++			GET_STRING; // id
++			INVALID_IF(ptr + 4 > end);
++			ptr += 4;
++		}
++		++ptr; // record terminated by zero byte
++
++		server->next_rule = NO_SERVER_RULES; // we're done
++		server->next_player_info = server->num_players; // we're done
++	}
++	else
++	{
++		malformed_packet( server, "invalid type" );
++		return PKT_ERROR;
++	}
++
++	server->retry1 = n_retries; // we're done with this packet, reset retry counter
++
++	return DONE_AUTO;
++}
++
++query_status_t send_ottdmaster_request_packet(struct qserver *server)
++{
++	return qserver_send_initial(server, server->type->master_packet, server->type->master_len);
++}
++
++query_status_t send_ottd_request_packet(struct qserver *server)
++{
++	qserver_send_initial(server, server->type->status_packet, server->type->status_len);
++
++	if(get_server_rules || get_player_info)
++	{
++		server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
++	}
++
++	return INPROGRESS;
++}
+diff -urP qstat-2.11/ottd.h qstat2/ottd.h
+--- qstat-2.11/ottd.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ottd.h	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ *
++ * opentTTD protocol
++ * Copyright 2007 Ludwig Nussel
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_OTTD_H
++#define QSTAT_OTTD_H
++
++#include "qstat.h"
++
++query_status_t send_ottdmaster_request_packet(struct qserver *server);
++query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++query_status_t send_ottd_request_packet(struct qserver *server);
++query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen);
++
++#endif
+diff -urP qstat-2.11/packet_manip.c qstat2/packet_manip.c
+--- qstat-2.11/packet_manip.c	2006-08-14 04:51:21.000000000 -0500
++++ qstat2/packet_manip.c	2010-03-03 10:40:22.622169000 -0500
+@@ -15,7 +15,7 @@
+ #include <stdio.h>
+ 
+ #define MAX_PACKETS 8
+-#define MAX_FAGMENTS 16
++#define MAX_FAGMENTS 128
+ 
+ int pkt_seq = 0;
+ int pkt_id_index = -1;
+@@ -29,7 +29,7 @@
+ 	int maxes[MAX_PACKETS];
+ 	int lengths[MAX_PACKETS];
+ 	SavedData *sdata = &server->saved_data;
+-	int i, p, done = 0;
++	int i, p, ret = INPROGRESS;
+ 	n_ids = 0;
+ 
+ 	memset( &segments[0][0], 0, sizeof(segments) );
+@@ -46,6 +46,13 @@
+ 			continue;
+ 		}
+ 
++		if ( sdata->pkt_index >= MAX_FAGMENTS )
++		{
++			// we only deal up to MAX_FAGMENTS packet fragment
++			fprintf( stderr, "Too many fragments %d for packetid %d max %d\n", sdata->pkt_index, sdata->pkt_id, MAX_FAGMENTS );
++			continue;
++		}
++
+ 		for ( i = 0; i < n_ids; i++ )
+ 		{
+ 			if ( sdata->pkt_id == ids[i])
+@@ -78,12 +85,6 @@
+ 		if ( segments[i][sdata->pkt_index] == NULL)
+ 		{
+ 			// add the packet to the list of segments
+-			if ( sdata->pkt_index >= MAX_FAGMENTS )
+-			{
+-				// we only deal up to MAX_FAGMENTS packet fragment
+-				fprintf( stderr, "Too many fragments for packetid %d max %d\n", sdata->pkt_id, MAX_FAGMENTS );
+-				continue;
+-			}
+ 			segments[i][sdata->pkt_index]= sdata;
+ 			counts[i]++;
+ 			lengths[i] += sdata->datalen;
+@@ -116,7 +117,7 @@
+ 				// reset to be unusable
+ 				pkt_id_index = -1;
+ 				free( combined );
+-				return 0;
++				return INPROGRESS;
+ 			}
+ 			memcpy( combined + datalen, segments[pkt_id_index][p]->data, segments[pkt_id_index][p]->datalen );
+ 			datalen += segments[pkt_id_index][p]->datalen;
+@@ -133,13 +134,15 @@
+ 		{
+ 			print_packet( server, combined, datalen );
+ 		}
+-		// Call the server's packet processing method
+-		done = ( (int (*)()) server->type->packet_func)( server, combined, datalen );
++		// Call the server's packet processing method flagging as a combine call
++		server->combined = 1;
++		ret = ( (int (*)()) server->type->packet_func)( server, combined, datalen );
+ 		free( combined );
++		server->combined = 0;
+ 
+ 		// Note: this is currently invalid as packet processing methods
+ 		// are void not int
+-		if ( done || server->saved_data.data == NULL)
++		if ( INPROGRESS != ret || NULL == server->saved_data.data )
+ 		{
+ 			break;
+ 		}
+@@ -147,9 +150,12 @@
+ 	// reset to be unusable
+ 	pkt_id_index = -1;
+ 
+-	return done;
++	return ret;
+ }
+ 
++// NOTE:
++// pkt_id is the packet aka response identifier
++// pkt_index is the index of the packet fragment
+ int add_packet( struct qserver *server, unsigned int pkt_id, int pkt_index, int pkt_max, int datalen, char *data, int calc_max )
+ {
+ 	SavedData *sdata;
+@@ -196,7 +202,6 @@
+ 	if ( NULL == sdata->data )
+ 	{
+ 		fprintf( stderr, "Out of memory\n" );
+-		cleanup_qserver( server, 1 );
+ 		return 0;
+ 	}
+ 
+@@ -240,3 +245,19 @@
+ 	}
+ 	return len;
+ }
++
++unsigned packet_count( struct qserver *server )
++{
++	SavedData *sdata = &server->saved_data;
++	unsigned cnt = 0;
++	if ( NULL == sdata->data )
++	{
++		return 0;
++	}
++
++	for ( ; sdata != NULL; sdata = sdata->next )
++	{
++		cnt++;
++	}
++	return cnt;
++}
+diff -urP qstat-2.11/packet_manip.h qstat2/packet_manip.h
+--- qstat-2.11/packet_manip.h	2006-08-14 04:51:21.000000000 -0500
++++ qstat2/packet_manip.h	2009-12-21 13:38:56.452334000 -0500
+@@ -18,5 +18,6 @@
+ int next_sequence();
+ SavedData* get_packet_fragment( int index );
+ unsigned combined_length( struct qserver *server, int pkt_id );
++unsigned packet_count( struct qserver *server );
+ 
+ #endif
+diff -urP qstat-2.11/qserver.c qstat2/qserver.c
+--- qstat-2.11/qserver.c	2006-05-24 09:39:41.000000000 -0500
++++ qstat2/qserver.c	2009-07-02 06:12:36.820869000 -0400
+@@ -18,82 +18,106 @@
+ #endif
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <errno.h>
++#include <string.h>
+ #include <sys/types.h>
+ 
+-int qserver_send_initial(struct qserver* server, const char* data, size_t len)
++// TODO: get rid of this and use send_packet instead, remove n_requests hack from a2s
++query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len)
+ {
+-    int ret = 0;
++    int status = INPROGRESS;
+ 
+-    if(data)
++    if( data )
+     {
+-	if ( server->flags & FLAG_BROADCAST)
+-	    ret = send_broadcast(server, data, len);
+-	else
+-	    ret = send( server->fd, data, len, 0);
++		int ret;
++		debug( 2, "[%s] send", server->type->type_prefix );
++		if( 4 <= get_debug_level() )
++		{
++			output_packet( server, data, len, 1 );
++		}
+ 
+-	if ( ret == SOCKET_ERROR)
+-	{
+-	    perror( "send");
+-	}
++		if ( server->flags & FLAG_BROADCAST)
++		{
++			ret = send_broadcast(server, data, len);
++		}
++		else
++		{
++			ret = send( server->fd, data, len, 0);
++		}
++
++		if ( ret == SOCKET_ERROR)
++		{
++			send_error( server, ret );
++			status = SYS_ERROR;
++		}
+     }
+ 
+     if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST)
+     {
+-	gettimeofday( &server->packet_time1, NULL);
++		gettimeofday( &server->packet_time1, NULL);
+     }
+     else
+     {
+-	server->n_retries++;
++		server->n_retries++;
+     }
+ 
+     server->retry1--;
+     server->n_packets++;
+ 
+-    return ret;
++    return status;
+ }
+ 
+-int qserver_send(struct qserver* server, const char* data, size_t len)
++query_status_t qserver_send(struct qserver* server, const char* data, size_t len)
+ {
+-    int ret = 0;
++    int status = INPROGRESS;
+ 
+     if(data)
+     {
+-	if ( server->flags & FLAG_BROADCAST)
+-	    ret = send_broadcast(server, data, len);
+-	else
+-	    ret = send( server->fd, data, len, 0);
++		int ret;
++		if ( server->flags & FLAG_BROADCAST)
++		{
++			ret = send_broadcast(server, data, len);
++		}
++		else
++		{
++			ret = send( server->fd, data, len, 0);
++		}
+ 
+-	if ( ret == SOCKET_ERROR)
+-	{
+-	    perror( "send");
+-	}
++		if ( ret == SOCKET_ERROR)
++		{
++			send_error( server, ret );
++			status = SYS_ERROR;
++		}
+     }
+ 
+-    server->retry1 = n_retries;
++    server->retry1 = n_retries-1;
+     gettimeofday( &server->packet_time1, NULL);
+     server->n_requests++;
+     server->n_packets++;
+ 
+-    return ret;
++    return status;
+ 
+ }
+ 
+-int
+-send_broadcast( struct qserver *server, const char *pkt, size_t pktlen)
++int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen)
+ {
+     struct sockaddr_in addr;
+     addr.sin_family= AF_INET;
+     if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
++	{
+         addr.sin_port= htons(server->port);
++	}
+     else
++	{
+         addr.sin_port= htons((unsigned short)( server->port + server->type->port_offset ));
++	}
+     addr.sin_addr.s_addr= server->ipaddr;
+     memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero));
+-    return sendto( server->fd, (const char*) pkt, pktlen, 0,
+-		(struct sockaddr *) &addr, sizeof(addr));
++
++    return sendto( server->fd, (const char*) pkt, pktlen, 0, (struct sockaddr *) &addr, sizeof(addr));
+ }
+ 
+-void register_send( struct qserver *server )
++int register_send( struct qserver *server )
+ {
+     if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
+ 	{
+@@ -109,19 +133,21 @@
+ 	gettimeofday( &server->packet_time1, NULL);
+     server->retry1--;
+     server->n_packets++;
++
++	return INPROGRESS;
+ }
+ 
+-int send_packet( struct qserver* server, const char* data, size_t len )
++query_status_t send_packet( struct qserver* server, const char* data, size_t len )
+ {
+-    int ret = 0;
+-
+ 	debug( 2, "[%s] send", server->type->type_prefix );
+ 	if( 4 <= get_debug_level() )
+ 	{
+-		print_packet( server, data, len );
++		output_packet( server, data, len, 1 );
+ 	}
++
+     if( data )
+     {
++		int ret;
+ 		if ( server->flags & FLAG_BROADCAST )
+ 		{
+ 			ret = send_broadcast( server, data, len );
+@@ -133,18 +159,25 @@
+ 
+ 		if ( ret == SOCKET_ERROR )
+ 		{
+-			unsigned int ipaddr = ntohl(server->ipaddr) ;
+-			fprintf( stderr, "Error on %d.%d.%d.%d\n",
+-				(ipaddr>>24)&0xff,
+-				(ipaddr>>16)&0xff,
+-				(ipaddr>>8)&0xff,
+-				ipaddr&0xff
+-			);
+-			perror( "send" );
++			return send_error( server, ret );
+ 		}
+     }
+ 
+ 	register_send( server );
+ 
+-	return ret;
++	return INPROGRESS;
++}
++
++query_status_t send_error( struct qserver *server, int rc )
++{
++	unsigned int ipaddr = ntohl(server->ipaddr);
++	const char* errstr = strerror(errno);
++	fprintf(stderr, "Error on %d.%d.%d.%d: %s, skipping ...\n",
++		(ipaddr >> 24) &0xff,
++		(ipaddr >> 16) &0xff,
++		(ipaddr >> 8) &0xff,
++		ipaddr &0xff,
++		errstr
++	);
++	return SYS_ERROR;
+ }
+diff -urP qstat-2.11/qserver.h qstat2/qserver.h
+--- qstat-2.11/qserver.h	2006-05-24 09:26:00.000000000 -0500
++++ qstat2/qserver.h	2013-02-13 07:58:41.414304000 -0500
+@@ -27,6 +27,17 @@
+     struct SavedData *next;
+ } SavedData;
+ 
++typedef enum {
++    STATE_INIT			=  0,
++    STATE_CONNECTING	=  1,
++    STATE_CONNECTED		=  2,
++    STATE_QUERYING		=  3,
++    STATE_QUERIED		=  4,
++
++    STATE_TIMEOUT		= -1,
++    STATE_SYS_ERROR		= -2,
++} server_state_t;
++
+ struct qserver {
+     char *arg;
+     char *host_name;
+@@ -34,12 +45,21 @@
+     int flags;
+     server_type * type;
+     int fd;
++	server_state_t state;
+     char *outfilename;
+     char *query_arg;
++    char *challenge_string;
+     struct query_param *params;
+     unsigned long challenge;
+     unsigned short port;
++    unsigned short orig_port; // This port is always constant from creation where as port can be updated based on the query results
+     unsigned short query_port;
++
++	// State variable to flag if the current processing call is a call from combine_packets
++	// This should really by done via a flag to the method itself but that would require changes
++	// to all handlers :(
++	unsigned short combined;
++
+     /** \brief number of retries _left_ for status query or rule query.
+      *
+      * That means
+@@ -103,6 +123,8 @@
+     int max_players;
+     int num_players;
+     int protocol_version;
++	int max_spectators;
++	int num_spectators;
+ 
+     SavedData saved_data;
+ 
+@@ -166,7 +188,7 @@
+  * \param len length of data
+  * \returns number of bytes sent or SOCKET_ERROR
+  */
+-int qserver_send_initial(struct qserver* server, const char* data, size_t len);
++query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len);
+ 
+ /** \brief send an initial query packet to a server
+  *
+@@ -176,7 +198,7 @@
+  *
+  * @see qserver_send_initial
+  */
+-int qserver_send(struct qserver* server, const char* data, size_t len);
++query_status_t qserver_send(struct qserver* server, const char* data, size_t len);
+ 
+ int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen);
+ 
+@@ -185,13 +207,18 @@
+  *
+  * This updates n_requests, n_packets, packet_time1 and decrements n_retries
+  */
+-void register_send( struct qserver *server );
++int register_send( struct qserver *server );
+ 
+ /**
+  * Sends a packet to the server either direct or via broadcast.
+  *
+  * Once sent calls register_send to register the send of the packet
+  */
+-int send_packet( struct qserver* server, const char* data, size_t len );
++query_status_t send_packet( struct qserver* server, const char* data, size_t len );
++
++/**
++ * Logs the error from a socket send
++ */
++query_status_t send_error( struct qserver *server, int rc );
+ 
+ #endif
+diff -urP qstat-2.11/qstat.c qstat2/qstat.c
+--- qstat-2.11/qstat.c	2006-10-28 07:37:18.000000000 -0500
++++ qstat2/qstat.c	2013-02-14 13:44:39.932703000 -0500
+@@ -17,19 +17,11 @@
+  * Licensed under the Artistic License, see LICENSE.txt for license terms
+  */
+ 
+-#ifdef HAVE_CONFIG_H
+-# include "gnuconfig.h"
+-#else
+-# ifndef VERSION
+-#  define VERSION "2.11"
+-# endif
+-#endif
+-
+-char *qstat_version= VERSION;
++#define RECV_BUF 204800
+ 
+ /* OS/2 defines */
+ #ifdef __OS2__
+-#define BSD_SELECT
++	#define BSD_SELECT
+ #endif
+ 
+ #include <stdio.h>
+@@ -48,210 +40,211 @@
+ 
+ 
+ #ifndef _WIN32
+-#include <signal.h>
+-#include <unistd.h>
+-#include <sys/socket.h>
+-#ifndef VMS
+-#include <sys/param.h>
+-#endif
+-#include <sys/time.h>
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-#include <netdb.h>
+-#include <arpa/inet.h>
+-
+-#ifndef F_SETFL
+-#include <fcntl.h>
+-#endif
+-
+-#ifdef __hpux
+-extern int h_errno;
+-#define STATIC static
+-#else
+-#define STATIC
+-#endif
+-
+-#define INVALID_SOCKET -1
+-#ifndef INADDR_NONE
+-#define INADDR_NONE ~0
+-#endif
+-#define sockerr()	errno
++	#include <signal.h>
++	#include <unistd.h>
++	#include <sys/socket.h>
++	#ifndef VMS
++		#include <sys/param.h>
++	#endif
++	#include <sys/time.h>
++	#include <netinet/in.h>
++	#include <netinet/tcp.h>
++	#include <netdb.h>
++	#include <arpa/inet.h>
++
++	#ifndef F_SETFL
++		#include <fcntl.h>
++	#endif
++
++	#ifdef __hpux
++		extern int h_errno;
++		#define STATIC static
++	#else
++		#define STATIC
++	#endif
++
++	#define INVALID_SOCKET -1
++	#ifndef INADDR_NONE
++		#define INADDR_NONE ~0
++	#endif
++	#define sockerr()	errno
+ #endif /* _WIN32 */
+ 
+ #ifdef __OS2__
+-#include <sys/socket.h>
+-#include <sys/select.h>
+-#include <sys/time.h>
+-#include <netinet/in.h>
+-#include <netdb.h>
+-#include <utils.h>
+-
+-#define INVALID_SOCKET -1
+-#define SOCKET_ERROR -1
+-#define close(a)        soclose(a)
++	#include <sys/socket.h>
++	#include <sys/select.h>
++	#include <sys/time.h>
++	#include <netinet/in.h>
++	#include <netdb.h>
++	#include <utils.h>
++
++	#define INVALID_SOCKET -1
++	#define SOCKET_ERROR -1
++	#define close(a)    soclose(a)
+ #endif /* __OS2__ */
+ 
+ 
++
+ #ifndef FD_SETSIZE
+-#define FD_SETSIZE 64
++	#define FD_SETSIZE 64
+ #endif
+ 
+ /* Figure out whether to use poll() or select()
+-*/
++ */
+ #ifndef USE_POLL
+-#ifndef USE_SELECT
++	#ifndef USE_SELECT
+ 
+-#ifdef sun
+-#define USE_POLL
+-#endif
+-#ifdef linux
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __linux__
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __linux
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef __hpux
+-#define USE_POLL
+-#include <sys/poll.h>
+-#endif
+-#ifdef __OpenBSD__
+-#define USE_POLL
+-#include <sys/poll.h>
+-#endif
+-#ifdef _AIX
+-#define USE_POLL
+-#include <poll.h>
+-#endif
+-#ifdef _WIN32
+-#define USE_SELECT
+-#endif
+-#ifdef __EMX__
+-#define USE_SELECT
+-#endif
++		#ifdef sun
++			#define USE_POLL
++		#endif
++		#ifdef linux
++			#define USE_POLL
++			#include <poll.h>
++		#endif
++		#ifdef __linux__
++			#define USE_POLL
++			#include <poll.h>
++		#endif
++		#ifdef __linux
++			#define USE_POLL
++			#include <poll.h>
++		#endif
++		#ifdef __hpux
++			#define USE_POLL
++			#include <sys/poll.h>
++		#endif
++		#ifdef __OpenBSD__
++			#define USE_POLL
++			#include <sys/poll.h>
++		#endif
++		#ifdef _AIX
++			#define USE_POLL
++			#include <poll.h>
++		#endif
++		#ifdef _WIN32
++			#define USE_SELECT
++		#endif
++		#ifdef __EMX__
++			#define USE_SELECT
++		#endif
+ 
+-#endif /* USE_SELECT */
++	#endif /* USE_SELECT */
+ #endif /* USE_POLL */
+ 
+ /* If did not chose, then use select()
+-*/
++ */
+ #ifndef USE_POLL
+-#ifndef USE_SELECT
+-#define USE_SELECT
+-#endif
+-#endif
+-
+-#ifdef ENABLE_DUMP
+-#ifndef _WIN32
+-#include <sys/mman.h>
+-#include <unistd.h>
+-#endif
+-#include <sys/types.h>
+-#include <sys/stat.h>
+-static int do_dump;
++	#ifndef USE_SELECT
++		#define USE_SELECT
++	#endif
+ #endif
+ 
+ #include "debug.h"
+ 
+ server_type *types;
+ int n_server_types;
++char *qstat_version = VERSION;
+ 
+ /*
+  * Values set by command-line arguments
+  */
+ 
+-
+-int hostname_lookup= 0;		/* set if -H was specified */
+-int new_style= 1;		/* unset if -old was specified */
+-int n_retries= DEFAULT_RETRIES;
+-int retry_interval= DEFAULT_RETRY_INTERVAL;
+-int master_retry_interval= DEFAULT_RETRY_INTERVAL*4;
+-int get_player_info= 0;
+-int get_server_rules= 0;
+-int up_servers_only= 0;
+-int no_full_servers= 0;
+-int no_empty_servers= 0;
+-int no_header_display= 0;
+-int raw_display= 0;
+-char *raw_delimiter= "\t";
++int hostname_lookup = 0; /* set if -H was specified */
++int new_style = 1; /* unset if -old was specified */
++int n_retries = DEFAULT_RETRIES;
++int retry_interval = DEFAULT_RETRY_INTERVAL;
++int master_retry_interval = DEFAULT_RETRY_INTERVAL * 4;
++int syncconnect = 0;
++int get_player_info = 0;
++int get_server_rules = 0;
++int up_servers_only = 0;
++int no_full_servers = 0;
++int no_empty_servers = 0;
++int no_header_display = 0;
++int raw_display = 0;
++char *raw_delimiter = "\t";
+ char *multi_delimiter = "|";
+-int player_address= 0;
+-int hex_player_names= 0;
+-int hex_server_names= 0;
+-int name_xforms= 1;
+-int strip_carets= 1;
+-int max_simultaneous= MAXFD_DEFAULT;
++int player_address = 0;
++int hex_player_names = 0;
++int hex_server_names = 0;
++int name_xforms = 1;
++int name_xforms_strip_unprintable = 0; /* xform strips unprintable chars */
++int strip_carets = 1;
++int max_simultaneous = MAXFD_DEFAULT;
+ int sendinterval = 5;
+-int html_names= -1;
++int html_names = -1;
+ extern int html_mode;
+-int raw_arg= 0;
+-int show_game_in_raw= 0;
+-int progress= 0;
+-int num_servers_total= 0;
+-int num_players_total= 0;
+-int max_players_total= 0;
+-int num_servers_returned= 0;
+-int num_servers_timed_out= 0;
+-int num_servers_down= 0;
+-server_type* default_server_type= NULL;
+-FILE *OF;		/* output file */
+-unsigned int source_ip= INADDR_ANY;
+-unsigned short source_port_low= 0;
+-unsigned short source_port_high= 0;
+-unsigned short source_port= 0;
++int raw_arg = 0;
++int show_game_in_raw = 0;
++int progress = 0;
++int num_servers_total = 0;
++int num_players_total = 0;
++int max_players_total = 0;
++int num_servers_returned = 0;
++int num_servers_timed_out = 0;
++int num_servers_down = 0;
++server_type *default_server_type = NULL;
++FILE *OF; /* output file */
++unsigned int source_ip = INADDR_ANY;
++unsigned short source_port_low = 0;
++unsigned short source_port_high = 0;
++unsigned short source_port = 0;
+ int show_game_port = 0;
+ int no_port_offset = 0;
+ 
+-#define ENCODING_LATIN_1  1
+-#define ENCODING_UTF_8    8
+-int xml_display= 0;
+-int xml_encoding= ENCODING_LATIN_1;
++#define ENCODING_LATIN_1 1
++#define ENCODING_UTF_8  8
++#define UTF8BYTESWAPNOTACHAR	0xFFFE
++#define UTF8NOTACHAR	0xFFFF
++#define UTF8MAXFROMUCS4	0x10FFFF
++
++int output_bom = 0;
++int xml_display = 0;
++int xml_encoding = ENCODING_LATIN_1;
+ 
+ #define SUPPORTED_SERVER_SORT	"pgihn"
+ #define SUPPORTED_PLAYER_SORT	"PFTNS"
+ #define SUPPORTED_SORT_KEYS	"l" SUPPORTED_SERVER_SORT SUPPORTED_PLAYER_SORT
+ char sort_keys[32];
+-int player_sort= 0;
+-int server_sort= 0;
++int player_sort = 0;
++int server_sort = 0;
+ 
+-void quicksort( void **array, int i, int j, int (*compare)(void*,void*));
+-int qpartition( void **array, int i, int j, int (*compare)(void*,void*));
+-void sort_servers( struct qserver **array, int size);
+-void sort_players( struct qserver *server);
+-int server_compare( struct qserver *one, struct qserver *two);
+-int player_compare( struct player *one, struct player *two);
+-int type_option_compare( server_type *one, server_type *two );
+-int type_string_compare( server_type *one, server_type *two );
+-STATIC int u2xmp_html_color( short color, char *dest, int *font_tag );
+-STATIC int ut2k4_html_color( const unsigned char *color, char *dest, int *font_tag );
++void quicksort(void **array, int i, int j, int(*compare)(void *, void*));
++int qpartition(void **array, int i, int j, int(*compare)(void *, void*));
++void sort_servers(struct qserver **array, int size);
++void sort_players(struct qserver *server);
++int server_compare(struct qserver *one, struct qserver *two);
++int player_compare(struct player *one, struct player *two);
++int type_option_compare(server_type *one, server_type *two);
++int type_string_compare(server_type *one, server_type *two);
++int process_func_ret( struct qserver *server, int ret );
++int connection_inprogress();
++void clear_socketerror();
++STATIC int u2xmp_html_color(short color, char *dest, int *font_tag);
++STATIC int ut2k4_html_color(const unsigned char *color, char *dest, int *font_tag);
+ 
+-int show_errors= 0;
++int show_errors = 0;
+ static int noserverdups = 1;
+ 
+ #define DEFAULT_COLOR_NAMES_RAW		0
+ #define DEFAULT_COLOR_NAMES_DISPLAY	1
+-int color_names= -1;
++int color_names = -1;
+ 
+ #define SECONDS 0
+ #define CLOCK_TIME 1
+ #define STOPWATCH_TIME 2
+ #define DEFAULT_TIME_FMT_RAW		SECONDS
+ #define DEFAULT_TIME_FMT_DISPLAY	CLOCK_TIME
+-int time_format= -1;
++int time_format = -1;
+ 
+-struct qserver *servers= NULL;
+-struct qserver **last_server= &servers;
+-struct qserver **connmap= NULL;
++struct qserver *servers = NULL;
++struct qserver **last_server = &servers;
++struct qserver **connmap = NULL;
+ int max_connmap;
+-struct qserver *last_server_bind= NULL;
+-struct qserver *first_server_bind= NULL;
+-int connected= 0;
+-time_t run_timeout= 0;
++struct qserver *last_server_bind = NULL;
++struct qserver *first_server_bind = NULL;
++int connected = 0;
++time_t run_timeout = 0;
+ time_t start_time;
+ int waiting_for_masters;
+ 
+@@ -260,725 +253,550 @@
+ static struct qserver **server_hash[ADDRESS_HASH_LENGTH];
+ static unsigned int server_hash_len[ADDRESS_HASH_LENGTH];
+ static void free_server_hash();
+-static void xml_display_player_info_info(struct player* player);
++static void xml_display_player_info_info(struct player *player);
+ 
+-char *DOWN= "DOWN";
+-char *SYSERROR= "SYSERROR";
+-char *TIMEOUT= "TIMEOUT";
+-char *MASTER= "MASTER";
+-char *SERVERERROR= "ERROR";
+-char *HOSTNOTFOUND= "HOSTNOTFOUND";
+-char *BFRIS_SERVER_NAME= "BFRIS Server";
+-char *GAMESPY_MASTER_NAME= "Gamespy Master";
++char *DOWN = "DOWN";
++char *SYSERROR = "SYSERROR";
++char *TIMEOUT = "TIMEOUT";
++char *MASTER = "MASTER";
++char *SERVERERROR = "ERROR";
++char *HOSTNOTFOUND = "HOSTNOTFOUND";
++char *BFRIS_SERVER_NAME = "BFRIS Server";
++char *GAMESPY_MASTER_NAME = "Gamespy Master";
+ 
+-int display_prefix= 0;
++int display_prefix = 0;
+ char *current_filename;
+ int current_fileline;
+ 
+-int count_bits( int n);
++int count_bits(int n);
+ 
+-static int qserver_get_timeout(struct qserver* server, struct timeval* now);
+-static int wait_for_timeout( unsigned int ms);
++static int qserver_get_timeout(struct qserver *server, struct timeval *now);
++static int wait_for_timeout(unsigned int ms);
+ static void finish_output();
+-static void decode_stefmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-static void decode_q3master_packet( struct qserver *server, char *pkt, int pktlen);
++static int decode_stefmaster_packet(struct qserver *server, char *pkt, int pktlen);
++static int decode_q3master_packet(struct qserver *server, char *ikt, int pktlen);
+ 
+-char * ut2003_strdup( const char *string, const char *end, char **next );
+-int html_entity( const char c, char *dest );
++char *ut2003_strdup(const char *string, const char *end, char **next);
++int html_entity(const char c, char *dest);
+ 
+-static const char * unreal_colors[] = {
+-	"AliceBlue",
+-	"AntiqueWhite",
+-	"Aqua",
+-	"Aquamarine",
+-	"Azure",
+-	"Beige",
+-	"Bisque",
+-	"Black",
+-	"BlanchedAlmond",
+-	"Blue",
+-	"BlueViolet",
+-	"Brown",
+-	"BurlyWood",
+-	"CadetBlue",
+-	"Chartreuse",
+-	"Chocolate",
+-	"Coral",
+-	"CornflowerBlue",
+-	"Cornsilk",
+-	"Crimson",
+-	"Cyan",
+-	"DarkBlue",
+-	"DarkCyan",
+-	"DarkGoldenrod",
+-	"DarkGray",
+-	"DarkGreen",
+-	"DarkKhaki",
+-	"DarkMagenta",
+-	"DarkOliveGreen",
+-	"DarkOrange",
+-	"DarkOrchid",
+-	"DarkRed",
+-	"DarkSalmon",
+-	"DarkSeaGreen",
+-	"DarkSlateBlue",
+-	"DarkSlateGray",
+-	"DarkTurquoise",
+-	"DarkViolet",
+-	"DeepPink",
+-	"DeepSkyBlue",
+-	"DimGray",
+-	"DodgerBlue",
+-	"FireBrick",
+-	"FloralWhite",
+-	"ForestGreen",
+-	"Fuchsia",
+-	"Gainsboro",
+-	"GhostWhite",
+-	"Gold",
+-	"Goldenrod",
+-	"Gray",
+-	"Green",
+-	"GreenYellow",
+-	"Honeydew",
+-	"HotPink",
+-	"IndianRed",
+-	"Indigo",
+-	"Ivory",
+-	"Khaki",
+-	"Lavender",
+-	"LavenderBlush",
+-	"LawnGreen",
+-	"LemonChiffon",
+-	"LightBlue",
+-	"LightCoral",
+-	"LightCyan",
+-	"LightGoldenrodYellow",
+-	"LightGreen",
+-	"LightGrey",
+-	"LightPink",
+-	"LightSalmon",
+-	"LightSeaGreen",
+-	"LightSkyBlue",
+-	"LightSlateGray",
+-	"LightSteelBlue",
+-	"LightYellow",
+-	"Lime",
+-	"LimeGreen",
+-	"Linen",
+-	"Magenta",
+-	"Maroon",
+-	"MediumAquamarine",
+-	"MediumBlue",
+-	"MediumOrchid",
+-	"MediumPurple",
+-	"MediumSeaGreen",
+-	"MediumSlateBlue",
+-	"MediumSpringGreen",
+-	"MediumTurquoise",
+-	"MediumVioletRed",
+-	"MidnightBlue",
+-	"MintCream",
+-	"MistyRose",
+-	"Moccasin",
+-	"NavajoWhite",
+-	"Navy",
+-	"OldLace",
+-	"Olive",
+-	"OliveDrab",
+-	"Orange",
+-	"OrangeRed",
+-	"Orchid",
+-	"PaleGoldenrod",
+-	"PaleGreen",
+-	"PaleTurquoise",
+-	"PaleVioletRed",
+-	"PapayaWhip",
+-	"PeachPuff",
+-	"Peru",
+-	"Pink",
+-	"Plum",
+-	"PowderBlue",
+-	"Purple",
+-	"Red",
+-	"RosyBrown",
+-	"RoyalBlue",
+-	"SaddleBrown",
+-	"Salmon",
+-	"SandyBrown",
+-	"SeaGreen",
+-	"Seashell",
+-	"Sienna",
+-	"Silver",
+-	"SkyBlue",
+-	"SlateBlue",
+-	"SlateGray",
+-	"Snow",
+-	"SpringGreen",
+-	"SteelBlue",
+-	"Tan",
+-	"Teal",
+-	"Thistle",
+-	"Tomato",
+-	"Turquoise",
+-	"Violet",
+-	"Wheat",
+-	"White",
+-	"WhiteSmoke",
+-	"Yellow",
+-	"YellowGreen",
++static const char *unreal_colors[] =
++{
++	"AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet",
++	"Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan",
++	"DarkBlue", "DarkCyan", "DarkGoldenrod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange",
++	"DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet",
++	"DeepPink", "DeepSkyBlue", "DimGray", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro",
++	"GhostWhite", "Gold", "Goldenrod", "Gray", "Green", "GreenYellow", "Honeydew", "HotPink", "IndianRed", "Indigo", "Ivory",
++	"Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan",
++	"LightGoldenrodYellow", "LightGreen", "LightGrey", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue",
++	"LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquamarine",
++	"MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise",
++	"MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab",
++	"Orange", "OrangeRed", "Orchid", "PaleGoldenrod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff",
++	"Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown",
++	"SeaGreen", "Seashell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan",
++	"Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen",
+ };
+ 
+-static const char *unreal_rgb_colors[] = {
+-	"#F0F8FF",
+-	"#FAEBD7",
+-	"#00FFFF",
+-	"#7FFFD4",
+-	"#F0FFFF",
+-	"#F5F5DC",
+-	"#FFE4C4",
+-	"#000000",
+-	"#FFEBCD",
+-	"#0000FF",
+-	"#8A2BE2",
+-	"#A52A2A",
+-	"#DEB887",
+-	"#5F9EA0",
+-	"#7FFF00",
+-	"#D2691E",
+-	"#FF7F50",
+-	"#6495ED",
+-	"#FFF8DC",
+-	"#DC143C",
+-	"#00FFFF",
+-	"#00008B",
+-	"#008B8B",
+-	"#B8860B",
+-	"#A9A9A9",
+-	"#006400",
+-	"#BDB76B",
+-	"#8B008B",
+-	"#556B2F",
+-	"#FF8C00",
+-	"#9932CC",
+-	"#8B0000",
+-	"#E9967A",
+-	"#8FBC8F",
+-	"#483D8B",
+-	"#2F4F4F",
+-	"#00CED1",
+-	"#9400D3",
+-	"#FF1493",
+-	"#00BFFF",
+-	"#696969",
+-	"#1E90FF",
+-	"#B22222",
+-	"#FFFAF0",
+-	"#228B22",
+-	"#FF00FF",
+-	"#DCDCDC",
+-	"#F8F8FF",
+-	"#FFD700",
+-	"#DAA520",
+-	"#808080",
+-	"#008000",
+-	"#ADFF2F",
+-	"#F0FFF0",
+-	"#FF69B4",
+-	"#CD5C5C",
+-	"#4B0082",
+-	"#FFFFF0",
+-	"#F0E68C",
+-	"#E6E6FA",
+-	"#FFF0F5",
+-	"#7CFC00",
+-	"#FFFACD",
+-	"#ADD8E6",
+-	"#F08080",
+-	"#E0FFFF",
+-	"#FAFAD2",
+-	"#90EE90",
+-	"#D3D3D3",
+-	"#FFB6C1",
+-	"#FFA07A",
+-	"#20B2AA",
+-	"#87CEFA",
+-	"#778899",
+-	"#B0C4DE",
+-	"#FFFFE0",
+-	"#00FF00",
+-	"#32CD32",
+-	"#FAF0E6",
+-	"#FF00FF",
+-	"#800000",
+-	"#66CDAA",
+-	"#0000CD",
+-	"#BA55D3",
+-	"#9370DB",
+-	"#3CB371",
+-	"#7B68EE",
+-	"#00FA9A",
+-	"#48D1CC",
+-	"#C71585",
+-	"#191970",
+-	"#F5FFFA",
+-	"#FFE4E1",
+-	"#FFE4B5",
+-	"#FFDEAD",
+-	"#000080",
+-	"#FDF5E6",
+-	"#808000",
+-	"#6B8E23",
+-	"#FFA500",
+-	"#FF4500",
+-	"#DA70D6",
+-	"#EEE8AA",
+-	"#98FB98",
+-	"#AFEEEE",
+-	"#DB7093",
+-	"#FFEFD5",
+-	"#FFDAB9",
+-	"#CD853F",
+-	"#FFC0CB",
+-	"#DDA0DD",
+-	"#B0E0E6",
+-	"#800080",
+-	"#FF0000",
+-	"#BC8F8F",
+-	"#4169E1",
+-	"#8B4513",
+-	"#FA8072",
+-	"#F4A460",
+-	"#2E8B57",
+-	"#FFF5EE",
+-	"#A0522D",
+-	"#C0C0C0",
+-	"#87CEEB",
+-	"#6A5ACD",
+-	"#708090",
+-	"#FFFAFA",
+-	"#00FF7F",
+-	"#4682B4",
+-	"#D2B48C",
+-	"#008080",
+-	"#D8BFD8",
+-	"#FF6347",
+-	"#40E0D0",
+-	"#EE82EE",
+-	"#F5DEB3",
+-	"#FFFFFF",
+-	"#F5F5F5",
+-	"#FFFF00",
+-	"#9ACD32",
++static const char *unreal_rgb_colors[] =
++{
++	"#F0F8FF", "#FAEBD7", "#00FFFF", "#7FFFD4", "#F0FFFF", "#F5F5DC", "#FFE4C4", "#000000", "#FFEBCD", "#0000FF", "#8A2BE2", "#A52A2A",
++	"#DEB887", "#5F9EA0", "#7FFF00", "#D2691E", "#FF7F50", "#6495ED", "#FFF8DC", "#DC143C", "#00FFFF", "#00008B", "#008B8B",
++	"#B8860B", "#A9A9A9", "#006400", "#BDB76B", "#8B008B", "#556B2F", "#FF8C00", "#9932CC", "#8B0000", "#E9967A", "#8FBC8F",
++	"#483D8B", "#2F4F4F", "#00CED1", "#9400D3", "#FF1493", "#00BFFF", "#696969", "#1E90FF", "#B22222", "#FFFAF0", "#228B22",
++	"#FF00FF", "#DCDCDC", "#F8F8FF", "#FFD700", "#DAA520", "#808080", "#008000", "#ADFF2F", "#F0FFF0", "#FF69B4", "#CD5C5C",
++	"#4B0082", "#FFFFF0", "#F0E68C", "#E6E6FA", "#FFF0F5", "#7CFC00", "#FFFACD", "#ADD8E6", "#F08080", "#E0FFFF", "#FAFAD2",
++	"#90EE90", "#D3D3D3", "#FFB6C1", "#FFA07A", "#20B2AA", "#87CEFA", "#778899", "#B0C4DE", "#FFFFE0", "#00FF00", "#32CD32",
++	"#FAF0E6", "#FF00FF", "#800000", "#66CDAA", "#0000CD", "#BA55D3", "#9370DB", "#3CB371", "#7B68EE", "#00FA9A", "#48D1CC",
++	"#C71585", "#191970", "#F5FFFA", "#FFE4E1", "#FFE4B5", "#FFDEAD", "#000080", "#FDF5E6", "#808000", "#6B8E23", "#FFA500",
++	"#FF4500", "#DA70D6", "#EEE8AA", "#98FB98", "#AFEEEE", "#DB7093", "#FFEFD5", "#FFDAB9", "#CD853F", "#FFC0CB", "#DDA0DD",
++	"#B0E0E6", "#800080", "#FF0000", "#BC8F8F", "#4169E1", "#8B4513", "#FA8072", "#F4A460", "#2E8B57", "#FFF5EE", "#A0522D",
++	"#C0C0C0", "#87CEEB", "#6A5ACD", "#708090", "#FFFAFA", "#00FF7F", "#4682B4", "#D2B48C", "#008080", "#D8BFD8", "#FF6347",
++	"#40E0D0", "#EE82EE", "#F5DEB3", "#FFFFFF", "#F5F5F5", "#FFFF00", "#9ACD32",
++
+ };
+ 
+-void free_server( struct qserver *server);
+-void free_player( struct player *player);
+-void free_rule( struct rule *rule);
+-void standard_display_server( struct qserver *server);
++void free_server(struct qserver *server);
++void free_player(struct player *player);
++void free_rule(struct rule *rule);
++void standard_display_server(struct qserver *server);
+ 
+ /* MODIFY HERE
+  * Change these functions to display however you want
+  */
+-void
+-display_server( struct qserver *server)
++void display_server(struct qserver *server)
+ {
+-    if ( player_sort)
+-	sort_players( server);
++	if (player_sort)
++	{
++		sort_players(server);
++	}
+ 
+-    if ( raw_display)
+-	raw_display_server( server);
+-    else if ( xml_display)
+-	xml_display_server( server);
+-    else if ( have_server_template())
+-	template_display_server( server);
+-    else
+-	standard_display_server( server);
+-
+-    free_server( server);
+-}
+-
+-void
+-standard_display_server( struct qserver *server)
+-{
+-    char prefix[64];
+-    if ( display_prefix)
+-	sprintf( prefix, "%-4s ", server->type->type_prefix);
+-    else
+-	prefix[0]='\0';
+-
+-    if ( server->server_name == DOWN)  {
+-	if ( ! up_servers_only)
+-	    fprintf( OF, "%s%-16s %10s\n", prefix,
+-		(hostname_lookup) ? server->host_name : server->arg, DOWN);
+-	return;
+-    }
+-    if ( server->server_name == TIMEOUT)  {
+-	if ( server->flags & FLAG_BROADCAST && server->n_servers)
+-	    fprintf( OF, "%s%-16s %d servers\n", prefix,
+-		server->arg, server->n_servers);
+-	else if ( ! up_servers_only)
+-	    fprintf( OF, "%s%-16s no response\n", prefix,
+-		(hostname_lookup) ? server->host_name : server->arg);
+-	return;
+-    }
++	if (raw_display)
++	{
++		raw_display_server(server);
++	}
++	else if (xml_display)
++	{
++		xml_display_server(server);
++	}
++	else if (have_server_template())
++	{
++		template_display_server(server);
++	}
++	else
++	{
++		standard_display_server(server);
++	}
+ 
+-    if ( server->type->master)  {
+-	display_qwmaster(server);
+-	return;
+-    }
++	free_server(server);
++}
+ 
+-    if ( no_full_servers && server->num_players >= server->max_players)
+-	return;
++void standard_display_server(struct qserver *server)
++{
++	char prefix[64];
++	if (display_prefix)
++	{
++		sprintf(prefix, "%-4s ", server->type->type_prefix);
++	}
++	else
++	{
++		prefix[0] = '\0';
++	}
+ 
+-    if ( no_empty_servers && server->num_players == 0)
+-	return;
++	if (server->server_name == DOWN || server->server_name == SYSERROR)
++	{
++		if (!up_servers_only)
++		{
++			fprintf(OF, "%s%-16s %10s\n", prefix, (hostname_lookup) ? server->host_name: server->arg, server->server_name);
++		}
++		return ;
++	}
+ 
+-    if ( server->error != NULL)  {
+-	fprintf( OF, "%s%-21s ERROR <%s>\n",
+-	    prefix,
+-	    (hostname_lookup) ? server->host_name : server->arg,
+-	    server->error);
+-	return;
+-    }
++	if (server->server_name == TIMEOUT)
++	{
++		if (server->flags &FLAG_BROADCAST && server->n_servers)
++		{
++			fprintf(OF, "%s%-16s %d servers\n", prefix, server->arg, server->n_servers);
++		}
++		else if (!up_servers_only)
++		{
++			fprintf(OF, "%s%-16s no response\n", prefix, (hostname_lookup) ? server->host_name: server->arg);
++		}
++		return ;
++	}
+ 
+-    if ( new_style)  {
+-	char *game= get_qw_game( server);
+-	int map_name_width= 8, game_width=0;
+-	switch( server->type->id)  {
+-	case QW_SERVER: case Q2_SERVER: case Q3_SERVER:
+-	    game_width= 9; break;
+-	case TRIBES2_SERVER:
+-	    map_name_width= 14; game_width= 8; break;
+-	case GHOSTRECON_SERVER:
+-	    map_name_width= 15; game_width= 15; break;
+-	case HL_SERVER:
+-	    map_name_width= 12; break;
+-	default:
+-	    break;
++	if (server->type->master)
++	{
++		display_qwmaster(server);
++		return ;
++	}
++
++	if (no_full_servers && server->num_players >= server->max_players)
++	{
++		return ;
++	}
++
++	if (no_empty_servers && server->num_players == 0)
++	{
++		return ;
++	}
++
++	if (server->error != NULL)
++	{
++		fprintf(OF, "%s%-21s ERROR <%s>\n", prefix, (hostname_lookup) ? server->host_name: server->arg, server->error);
++		return ;
++	}
++
++	if (new_style)
++	{
++		char *game = get_qw_game(server);
++		int map_name_width = 8, game_width = 0;
++		switch (server->type->id)
++		{
++			case QW_SERVER:
++			case Q2_SERVER:
++			case Q3_SERVER:
++				game_width = 9;
++				break;
++			case TRIBES2_SERVER:
++				map_name_width = 14;
++				game_width = 8;
++				break;
++			case GHOSTRECON_SERVER:
++				map_name_width = 15;
++				game_width = 15;
++				break;
++			case HL_SERVER:
++				map_name_width = 12;
++				break;
++			default:
++				break;
++		}
++		fprintf(OF,
++			"%s%-21s %2d/%-2d %2d/%-2d %*s %6d / %1d  %*s %s\n",
++			prefix,
++			(hostname_lookup) ? server->host_name: server->arg,
++			server->num_players,
++			server->max_players,
++			server->num_spectators,
++			server->max_spectators,
++			map_name_width,
++			(server->map_name) ? xform_name(server->map_name, server): "?",
++			server->n_requests ? server->ping_total / server->n_requests: 999,
++			server->n_retries,
++			game_width,
++			game,
++			xform_name(server->server_name, server)
++		);
++		if (get_server_rules && NULL != server->type->display_rule_func )
++		{
++			server->type->display_rule_func(server);
++		}
++		if (get_player_info && NULL != server->type->display_player_func )
++		{
++			server->type->display_player_func(server);
++		}
++	}
++	else
++	{
++		char name[512];
++		sprintf(name, "\"%s\"", server->server_name);
++		fprintf(OF,
++			"%-16s %10s map %s at %22s %d/%d players %d ms\n",
++			(hostname_lookup) ? server->host_name: server->arg,
++			name,
++			server->map_name,
++			server->address,
++			server->num_players,
++			server->max_players,
++			server->n_requests ? server->ping_total / server->n_requests: 999
++		);
+ 	}
+-	fprintf( OF, "%s%-21s %2d/%2d %*s %6d / %1d  %*s %s\n",
+-	    prefix,
+-	    (hostname_lookup) ? server->host_name : server->arg,
+-	    server->num_players, server->max_players,
+-	    map_name_width, (server->map_name) ? server->map_name : "?",
+-	    server->n_requests ? server->ping_total/server->n_requests : 999,
+-	    server->n_retries,
+-	    game_width, game,
+-	    xform_name(server->server_name, server));
+-	if ( get_server_rules)
+-	    server->type->display_rule_func( server);
+-	if ( get_player_info)
+-	    server->type->display_player_func( server);
+-    }
+-    else  {
+-	char name[512];
+-	sprintf( name, "\"%s\"", server->server_name);
+-	fprintf( OF, "%-16s %10s map %s at %22s %d/%d players %d ms\n",
+-	    (hostname_lookup) ? server->host_name : server->arg,
+-	    name, server->map_name,
+-	    server->address, server->num_players, server->max_players,
+-	    server->n_requests ? server->ping_total/server->n_requests : 999);
+-    }
+ }
+ 
+-void
+-display_qwmaster( struct qserver *server)
++void display_qwmaster(struct qserver *server)
+ {
+-    char *prefix;
+-    prefix= server->type->type_prefix;
++	char *prefix;
++	prefix = server->type->type_prefix;
+ 
+-    if ( server->error != NULL)
+-	fprintf( OF, "%s %-17s ERROR <%s>\n", prefix,
+-		(hostname_lookup) ? server->host_name : server->arg,
+-		server->error);
+-    else
+-	fprintf( OF, "%s %-17s %d servers %6d / %1d\n", prefix,
+-	    (hostname_lookup) ? server->host_name : server->arg,
+-	    server->n_servers,
+-	    server->n_requests ? server->ping_total/server->n_requests : 999,
+-	    server->n_retries);
+-}
+-
+-void
+-display_header()
+-{
+-    if ( ! no_header_display)
+-	fprintf( OF, "%-16s %8s %8s %15s    %s\n", "ADDRESS", "PLAYERS", "MAP",
+-		"RESPONSE TIME", "NAME");
+-}
+-
+-void
+-display_server_rules( struct qserver *server)
+-{
+-    struct rule *rule;
+-    int printed= 0;
+-    rule= server->rules;
+-    for ( ; rule != NULL; rule= rule->next)  {
+-	if ( (server->type->id != Q_SERVER &&
+-		server->type->id != H2_SERVER) || ! is_default_rule( rule)) {
+-	    fprintf( OF, "%c%s=%s", (printed)?',':'\t', rule->name, rule->value);
+-	    printed++;
++	if (server->error != NULL)
++	{
++		fprintf(OF,
++			"%s %-17s ERROR <%s>\n",
++			prefix,
++			(hostname_lookup) ? server->host_name: server->arg,
++			server->error
++		);
++	}
++	else
++	{
++		fprintf(OF,
++			"%s %-17s %d servers %6d / %1d\n",
++			prefix,
++			(hostname_lookup) ? server->host_name: server->arg,
++			server->n_servers,
++			server->n_requests ? server->ping_total / server->n_requests: 999,
++			server->n_retries
++		);
+ 	}
+-    }
+-    if ( printed)
+-	fputs( "\n", OF);
+ }
+ 
+-void
+-display_q_player_info( struct qserver *server)
++void display_header()
+ {
+-    char fmt[128];
+-    struct player *player;
+-
+-    strcpy( fmt, "\t#%-2d %3d frags %9s ");
+-
+-    if ( color_names)
+-	strcat( fmt, "%9s:%-9s ");
+-    else
+-	strcat( fmt, "%2s:%-2s ");
+-    if ( player_address)
+-	strcat( fmt, "%22s ");
+-    else
+-	strcat( fmt, "%s");
+-    strcat( fmt, "%s\n");
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		player->number,
+-		player->frags,
+-		play_time(player->connect_time,1),
+-		quake_color(player->shirt_color),
+-		quake_color(player->pants_color),
+-		(player_address)?player->address:"",
+-		xform_name( player->name, server));
+-    }
++	if (!no_header_display)
++	{
++		fprintf(OF, "%-16s %8s %8s %15s    %s\n", "ADDRESS", "PLAYERS", "MAP", "RESPONSE TIME", "NAME");
++	}
++}
++
++void display_server_rules(struct qserver *server)
++{
++	struct rule *rule;
++	int printed = 0;
++	rule = server->rules;
++	for (; rule != NULL; rule = rule->next)
++	{
++		if ((server->type->id != Q_SERVER && server->type->id != H2_SERVER) || !is_default_rule(rule))
++		{
++			fprintf(OF, "%c%s=%s", (printed) ? ',' : '\t', rule->name, rule->value);
++			printed++;
++		}
++	}
++	if (printed)
++	{
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-display_qw_player_info( struct qserver *server)
++void display_q_player_info(struct qserver *server)
+ {
+-    char fmt[128];
+-    struct player *player;
++	char fmt[128];
++	struct player *player;
+ 
+-    strcpy( fmt, "\t#%-6d %3d frags %6s@%-5s %8s");
++	strcpy(fmt, "\t#%-2d %3d frags %9s ");
+ 
+-    if ( color_names)
+-	strcat( fmt, "%9s:%-9s ");
+-    else
+-	strcat( fmt, "%2s:%-2s ");
+-    strcat( fmt, "%s\n");
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		player->number,
+-		player->frags,
+-		play_time(player->connect_time,0),
+-		ping_time(player->ping),
+-		player->skin ? player->skin : "",
+-		quake_color(player->shirt_color),
+-		quake_color(player->pants_color),
+-		xform_name( player->name, server));
+-    }
++	if (color_names)
++	{
++		strcat(fmt, "%9s:%-9s ");
++	}
++	else
++	{
++		strcat(fmt, "%2s:%-2s ");
++	}
++	if (player_address)
++	{
++		strcat(fmt, "%22s ");
++	}
++	else
++	{
++		strcat(fmt, "%s");
++	}
++	strcat(fmt, "%s\n");
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			player->number,
++			player->frags,
++			play_time(player->connect_time, 1),
++			quake_color(player->shirt_color),
++			quake_color(player->pants_color),
++			(player_address) ? player->address: "",
++			xform_name(player->name, server)
++		);
++	}
+ }
+ 
+-void
+-display_q2_player_info( struct qserver *server)
++void display_qw_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	char fmt[128];
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( server->flags & FLAG_PLAYER_TEAMS)
+-	    fprintf( OF, "\t%3d frags team#%d %8s  %s\n",
+-		player->frags,
+-		player->team,
+-		ping_time(player->ping),
+-		xform_name( player->name, server));
+-	else
+-	    fprintf( OF, "\t%3d frags %8s  %s\n",
+-		player->frags,
+-		ping_time(player->ping),
+-		xform_name( player->name, server));
+-    }
++	strcpy(fmt, "\t#%-6d %5d frags %6s@%-5s %8s");
++
++	if (color_names)
++	{
++		strcat(fmt, "%9s:%-9s ");
++	}
++	else
++	{
++		strcat(fmt, "%2s:%-2s ");
++	}
++	strcat(fmt, "%12s %s\n");
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			player->number,
++			player->frags,
++			play_time(player->connect_time, 0),
++			ping_time(player->ping),
++			player->skin ? player->skin: "",
++			quake_color(player->shirt_color),
++			quake_color(player->pants_color),
++			xform_name(player->name, server),
++			xform_name(player->team_name, server)
++		);
++	}
++}
++
++void display_q2_player_info(struct qserver *server)
++{
++	struct player *player;
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		if (server->flags &FLAG_PLAYER_TEAMS)
++		{
++			fprintf(OF,"\t%3d frags team#%d %8s  %s\n", player->frags, player->team, ping_time(player->ping), xform_name(player->name, server));
++		}
++		else
++		{
++			fprintf(OF, "\t%3d frags %8s  %s\n", player->frags, ping_time(player->ping), xform_name(player->name, server));
++		}
++	}
+ }
+ 
+ 
+-void
+-display_unreal_player_info( struct qserver *server)
++
++void display_unreal_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    static const char * fmt_team_number= "\t%3d frags team#%-3d %7s %s\n";
+-    static const char * fmt_team_name= "\t%3d frags %8s %7s %s\n";
+-    static const char * fmt_no_team= "\t%3d frags %8s  %s\n";
++	struct player *player;
++	static const char *fmt_team_number = "\t%3d frags team#%-3d %7s %s\n";
++	static const char *fmt_team_name = "\t%3d frags %8s %7s %s\n";
++	static const char *fmt_no_team = "\t%3d frags %8s  %s\n";
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if ( server->flags & FLAG_PLAYER_TEAMS)
++		if (server->flags &FLAG_PLAYER_TEAMS)
+ 		{
+ 			// we use (player->score) ? player->score : player->frags,
+ 			// so we get details from halo
+-			if ( player->team_name)
++			if (player->team_name)
+ 			{
+-				fprintf( OF, fmt_team_name,
+-					(player->score && NA_INT != player->score ) ? player->score : player->frags,
++				fprintf(OF, fmt_team_name,
++					(player->score && NA_INT != player->score) ? player->score: player->frags,
+ 					player->team_name,
+ 					ping_time(player->ping),
+-					xform_name( player->name, server)
++					xform_name(player->name, server)
+ 				);
+ 			}
+ 			else
+ 			{
+-				fprintf( OF, fmt_team_number,
+-					(player->score && NA_INT != player->score ) ? player->score : player->frags,
++				fprintf(OF, fmt_team_number,
++					(player->score && NA_INT != player->score) ? player->score: player->frags,
+ 					player->team,
+ 					ping_time(player->ping),
+-					xform_name( player->name, server)
++					xform_name(player->name, server)
+ 				);
+ 			}
+ 		}
+ 		else
+ 		{
+-			fprintf( OF, fmt_no_team,
+-				player->frags,
+-				ping_time(player->ping),
+-				xform_name( player->name, server)
+-			);
++			fprintf(OF, fmt_no_team, player->frags, ping_time(player->ping), xform_name(player->name, server));
+ 		}
+ 	}
+ }
+ 
+-void
+-display_shogo_player_info( struct qserver *server)
++void display_shogo_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\t%3d frags %8s %s\n",
+-		player->frags,
+-		ping_time(player->ping),
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %s\n", player->frags, ping_time(player->ping), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_halflife_player_info( struct qserver *server)
++void display_halflife_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\t%3d frags %8s %s\n",
+-		player->frags,
+-		play_time( player->connect_time,1),
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_tribes_player_info( struct qserver *server)
++void display_fl_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\t%4d score team#%d %8s %s\n",
+-		player->frags,
+-		player->team,
+-		ping_time( player->ping),
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %8s %s\n", player->frags, ping_time(player->ping), play_time(player->connect_time, 1), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_tribes2_player_info( struct qserver *server)
++void display_tribes_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\tscore %4d %14s %s\n",
+-		player->frags,
+-		player->team_name ? player->team_name : (player->number == TRIBES_TEAM ? "TEAM" : "?"),
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%4d score team#%d %8s %s\n", player->frags, player->team, ping_time(player->ping), xform_name(player->name, server)
++	);
++	}
+ }
+ 
+-void
+-display_bfris_player_info( struct qserver *server)
++void display_tribes2_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-      fprintf( OF, "\ttid: %d, ship: %d, team: %s, ping: %d, score: %d, kills: %d, name: %s\n",
+-	     player->number,
+-	     player->ship,
+-	     player->team_name,
+-	     player->ping,
+-	     player->score,
+-	     player->frags,
+-	     xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\tscore %4d %14s %s\n",
++			player->frags,
++			player->team_name ? player->team_name: (player->number == TRIBES_TEAM ?	"TEAM" : "?"),
++			xform_name(player->name, server)
++		);
++	}
++}
++
++void display_bfris_player_info(struct qserver *server)
++{
++	struct player *player;
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\ttid: %d, ship: %d, team: %s, ping: %d, score: %d, kills: %d, name: %s\n",
++			player->number,
++			player->ship,
++			player->team_name,
++			player->ping,
++			player->score,
++			player->frags,
++			xform_name(player->name, server)
++		);
++	}
+ }
+ 
+-void
+-display_descent3_player_info( struct qserver *server)
++void display_descent3_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\t%3d frags %3d deaths team#%-3d %7s %s\n",
+-		player->frags,
+-		player->deaths,
+-		player->team,
+-		ping_time(player->ping),
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %3d deaths team#%-3d %7s %s\n",
++			player->frags,
++			player->deaths,
++			player->team,
++			ping_time(player->ping),
++			xform_name(player->name, server)
++		);
++	}
+ }
+ 
+-void
+-display_ghostrecon_player_info( struct qserver *server)
++void display_ghostrecon_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, "\tdead=%3d team#%-3d %s\n",
+-		player->deaths,
+-		player->team,
+-		xform_name( player->name, server));
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\tdead=%3d team#%-3d %s\n", player->deaths, player->team, xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_eye_player_info( struct qserver *server)
++void display_eye_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->team_name)
+-	    fprintf( OF, "\tscore %4d %6s team %12s %s\n",
+-		player->score,
+-		ping_time(player->ping),
+-		player->team_name,
+-		xform_name( player->name, server));
+-	else
+-	    fprintf( OF, "\tscore %4d %6s team#%d %s\n",
+-		player->score,
+-		ping_time(player->ping),
+-		player->team,
+-		xform_name( player->name, server));
+-    }
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		if (player->team_name)
++		{
++			fprintf(OF, "\tscore %4d %6s team %12s %s\n",
++				player->score,
++				ping_time(player->ping),
++				player->team_name,
++				xform_name(player->name,server)
++			);
++		}
++		else
++		{
++			fprintf(OF, "\tscore %4d %6s team#%d %s\n",
++				player->score,
++				ping_time(player->ping),
++				player->team,
++				xform_name(player->name,server)
++			);
++		}
++	}
+ }
+ 
+-int
+-calculate_armyops_score( struct player *player)
++int calculate_armyops_score(struct player *player)
+ {
+ 	/* Calculates a player's score for ArmyOps from the basic components */
+ 
+@@ -986,7354 +804,8945 @@
+ 	int kill_score = 0;
+ 	struct info *info;
+ 
+-	for ( info = player->info; info; info = info->next )
++	for (info = player->info; info; info = info->next)
+ 	{
+-		if ( 0 == strcmp( info->name, "leader") || 0 == strcmp( info->name, "goal") || 0 == strcmp( info->name, "roe") )
+-			score += atoi( info->value );
+-		else if ( 0 == strcmp( info->name, "kia") || 0 == strcmp( info->name, "enemy") )
+-			kill_score += atoi( info->value );
++		if (0 == strcmp(info->name, "leader") || 0 == strcmp(info->name, "goal") || 0 == strcmp(info->name, "roe"))
++		{
++			score += atoi(info->value);
++		}
++		else if (0 == strcmp(info->name, "kia") || 0 == strcmp(info->name, "enemy"))
++		{
++			kill_score += atoi(info->value);
++		}
+ 	}
+ 
+-	if ( kill_score > 0 )
++	if (kill_score > 0)
++	{
+ 		score += kill_score;
++	}
+ 
+ 	return score;
+ }
+ 
+-void
+-display_gs2_player_info( struct qserver *server)
++void display_gs2_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->team_name)
+-	    fprintf( OF, "\tscore %4d %6s team %12s %s\n",
+-		player->score,
+-		ping_time(player->ping),
+-		player->team_name,
+-		xform_name( player->name, server));
+-	else
+-	    fprintf( OF, "\tscore %4d %6s team#%d %s\n",
+-		player->score,
+-		ping_time(player->ping),
+-		player->team,
+-		xform_name( player->name, server));
+-    }
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		if (player->team_name)
++		{
++			fprintf(OF, "\tscore %4d %6s team %12s %s\n",
++				player->score,
++				ping_time(player->ping),
++				player->team_name,
++				xform_name(player->name,server)
++			);
++		}
++		else
++		{
++			fprintf(OF, "\tscore %4d %6s team#%d %s\n",
++				player->score,
++				ping_time(player->ping),
++				player->team,
++				xform_name(player->name, server)
++			);
++		}
++	}
+ }
+ 
+-void
+-display_armyops_player_info( struct qserver *server)
++void display_armyops_player_info(struct qserver *server)
+ {
+ 	struct player *player;
+ 
+-	player= server->players;
+-	for ( ; player != NULL; player= player->next)  {
+-		player->score = calculate_armyops_score( player );
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		player->score = calculate_armyops_score(player);
+ 	}
+ 
+ 	display_gs2_player_info(server);
+ }
+ 
+-void
+-display_ts2_player_info( struct qserver *server)
++void display_ts2_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
+-    {
+-	    fprintf( OF, "\t%6s %s\n",
+-			ping_time(player->ping),
+-			xform_name( player->name, server)
+-		);
+-    }
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%6s %s\n", ping_time(player->ping), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_tm_player_info( struct qserver *server)
++void display_ts3_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
+-    {
+-	    fprintf( OF, "\t%6s %s\n",
+-			ping_time(player->ping),
+-			xform_name( player->name, server)
+-		);
+-    }
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%s\n", xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_doom3_player_info( struct qserver *server)
++void display_bfbc2_player_info(struct qserver *server)
+ {
+-    struct player *player;
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->tribe_tag)
+-	    fprintf( OF, "\t#%-4d score %4d %6s team %12s %s\n",
+-		player->number,
+-		player->score,
+-		ping_time(player->ping),
+-		player->tribe_tag,
+-		xform_name( player->name, server));
+-	else
+-	    fprintf( OF, "\t#%-4d score %4d %6s team#%d %s\n",
+-		player->number,
+-		player->score,
+-		ping_time(player->ping),
+-		player->team,
+-		xform_name( player->name, server));
+-    }
+-}
+-
+-void
+-display_hl2_player_info( struct qserver *server )
+-{
+-	// ATM this looks like halflife player info
+-	display_halflife_player_info( server );
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%s\n", xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-display_ravenshield_player_info( struct qserver *server)
++void display_wic_player_info(struct qserver *server)
+ {
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player = player->next )
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t%3d frags %8s %s\n",
+-			player->frags,
+-			play_time( player->connect_time,1),
+-			xform_name( player->name, server));
+-    }
++		fprintf(OF, "\t#%-4d score %4d team %12s role %12s %s\n",
++			player->number,
++			player->score,
++			player->team_name,
++			player->tribe_tag ? player->tribe_tag : "",
++			xform_name(player->name, server)
++		);
++	}
+ }
+ 
+-
+-void
+-display_savage_player_info( struct qserver *server)
++void display_ventrilo_player_info(struct qserver *server)
+ {
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player = player->next )
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t%3d frags %8s %s\n",
+-			player->frags,
+-			play_time( player->connect_time,1),
+-			xform_name( player->name, server));
+-    }
++		fprintf(OF, "\t# %d ping %s time %d cid %i ch %s name %s\n",
++			player->number,
++			ping_time(player->ping),
++			player->connect_time,
++			player->team,
++			player->team_name,
++			xform_name(player->name, server)
++		);
++	}
+ }
+ 
+-
+-void
+-display_farcry_player_info( struct qserver *server)
++void display_tm_player_info(struct qserver *server)
+ {
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player = player->next )
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t%3d frags %8s %s\n",
+-			player->frags,
+-			play_time( player->connect_time,1),
+-			xform_name( player->name, server));
+-    }
++		fprintf(OF, "\t%6s %s\n", ping_time(player->ping), xform_name(player->name, server));
++	}
+ }
+ 
+-char *
+-get_qw_game( struct qserver *server)
++void display_doom3_player_info(struct qserver *server)
+ {
+-    struct rule *rule;
+-    if ( server->type->game_rule == NULL || *server->type->game_rule == '\0')
+-	{
+-		return "";
+-	}
+-    rule = server->rules;
+-    for ( ; rule != NULL; rule = rule->next)
++	struct player *player;
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if ( strcmp( rule->name, server->type->game_rule) == 0)
++		if (player->tribe_tag)
+ 		{
+-			if ( server->type->id == Q3_SERVER && strcmp( rule->value, "baseq3") == 0)
+-			{
+-				return "";
+-			}
+-			return rule->value;
++			fprintf(OF, "\t#%-4d score %4d %6s team %12s %s\n",
++				player->number,
++				player->score,
++				ping_time(player->ping),
++				player->tribe_tag,
++				xform_name(player->name, server)
++			);
+ 		}
+-	}
+-    rule= server->rules;
+-    for ( ; rule != NULL; rule = rule->next)
+-	{
+-		if ( strcmp( rule->name, "game" ) == 0)
++		else
+ 		{
+-			return rule->value;
++			fprintf(OF, "\t#%-4d score %4d %6s team#%d %s\n",
++				player->number,
++				player->score,
++				ping_time(player->ping),
++				player->team,
++				xform_name(player->name, server)
++			);
+ 		}
+ 	}
+-	return "";
+ }
+ 
+-/* Raw output for web master types
+- */
+-
+-#define RD raw_delimiter
+-
+-void
+-raw_display_server( struct qserver *server)
++void display_ravenshield_player_info(struct qserver *server)
+ {
+-    char *prefix;
+-    int ping_time;
+-    prefix= server->type->type_prefix;
+-
+-    if ( server->n_requests)
+-	ping_time= server->ping_total/server->n_requests;
+-    else
+-	ping_time= 999;
+-
+-    if ( server->server_name == DOWN)  {
+-	if ( ! up_servers_only)
+-	    fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s\n\n",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup)?server->host_name:server->arg,
+-		RD, DOWN);
+-	return;
+-    }
+-    if ( server->server_name == TIMEOUT)  {
+-	if ( server->flags & FLAG_BROADCAST && server->n_servers)
+-	    fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%d\n", prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, server->arg,
+-		RD, server->n_servers);
+-	else if ( ! up_servers_only)
+-	    fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s\n\n",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup)?server->host_name:server->arg,
+-		RD, TIMEOUT);
+-	return;
+-    }
+-
+-    if ( server->error != NULL)  {
+-        fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, "ERROR",
+-		RD, server->error);
+-    }
+-    else if ( server->type->flags & TF_RAW_STYLE_QUAKE)  {
+-        fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%s" "%s%d" "%s%d" "%s%d" "%s%d" "%s%s",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, xform_name( server->server_name, server),
+-		RD, server->address,
+-		RD, server->protocol_version,
+-		RD, server->map_name,
+-		RD, server->max_players,
+-		RD, server->num_players,
+-		RD, ping_time,
+-		RD, server->n_retries,
+-		show_game_in_raw ? RD : "", show_game_in_raw ? get_qw_game(server) : ""
+-        );
+-    }
+-    else if ( server->type->flags & TF_RAW_STYLE_TRIBES)  {
+-	fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, xform_name( server->server_name, server),
+-		RD, (server->map_name) ? server->map_name : "?",
+-		RD, server->num_players,
+-		RD, server->max_players
+-	);
+-    }
+-    else if ( server->type->flags & TF_RAW_STYLE_GHOSTRECON)  {
+-	fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, xform_name( server->server_name, server),
+-		RD, (server->map_name) ? server->map_name : "?",
+-		RD, server->num_players,
+-		RD, server->max_players
+-	);
+-    }
+-    else if ( server->type->master)  {
+-        fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%d",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, server->n_servers
+-	);
+-    }
+-    else  {
+-        fprintf( OF, "%s" "%.*s%.*s" "%s%s" "%s%s" "%s%s" "%s%d" "%s%d" "%s%d" "%s%d" "%s%s",
+-		prefix,
+-		raw_arg, RD, raw_arg, server->arg,
+-		RD, (hostname_lookup) ? server->host_name : server->arg,
+-		RD, xform_name( server->server_name, server),
+-		RD, (server->map_name) ? server->map_name : "?",
+-		RD, server->max_players,
+-		RD, server->num_players,
+-		RD, ping_time,
+-		RD, server->n_retries,
+-		show_game_in_raw ? RD : "", show_game_in_raw ? get_qw_game(server) : ""
+-        );
+-    }
+-    fputs( "\n", OF);
+-
+-    if ( server->type->master || server->error != NULL)  {
+-	fputs( "\n", OF);
+-	return;
+-    }
+-
+-    if ( get_server_rules)
+-	server->type->display_raw_rule_func( server);
+-    if ( get_player_info)
+-	server->type->display_raw_player_func( server);
+-    fputs( "\n", OF);
+-}
+-
+-void
+-raw_display_server_rules( struct qserver *server)
+-{
+-    struct rule *rule;
+-    int printed= 0;
+-    rule= server->rules;
+-    for ( ; rule != NULL; rule= rule->next)  {
+-	if ( server->type->id == TRIBES2_SERVER)  {
+-	    char *v;
+-	    for ( v= rule->value; *v; v++)
+-		if ( *v == '\n') *v= ' ';
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
+ 	}
+-	fprintf( OF, "%s%s=%s", (printed)?RD:"", rule->name, rule->value);
+-	printed++;
+-    }
+-    if ( server->missing_rules)
+-	fprintf( OF, "%s?", (printed)?RD:"");
+-    fputs( "\n", OF);
+-}
+-
+-void
+-raw_display_q_player_info( struct qserver *server)
+-{
+-    char fmt[] = "%d" "%s%s" "%s%s" "%s%d" "%s%s" "%s%s" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		player->number,
+-		RD, xform_name( player->name, server),
+-		RD, player->address,
+-		RD, player->frags,
+-		RD, play_time(player->connect_time,1),
+-		RD, quake_color(player->shirt_color),
+-		RD, quake_color(player->pants_color)
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_qw_player_info( struct qserver *server)
+-{
+-    char fmt[128];
+-    struct player *player;
+-
+-    strcpy( fmt, "%d" "%s%s" "%s%d" "%s%s" "%s%s" "%s%s");
+-    strcat( fmt, "%s%d" "%s%s");
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		player->number,
+-		RD, xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, play_time(player->connect_time,1),
+-		RD, quake_color(player->shirt_color),
+-		RD, quake_color(player->pants_color),
+-		RD, player->ping,
+-		RD, player->skin ? player->skin : ""
+-	);
+-	fputs( "\n", OF);
+-    }
+ }
+ 
+-void
+-raw_display_q2_player_info( struct qserver *server)
+-{
+-    static const char *fmt = "%s" "%s%d" "%s%d";
+-    static const char *fmt_team = "%s" "%s%d" "%s%d" "%s%d";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( server->flags & FLAG_PLAYER_TEAMS)
+-	    fprintf( OF, fmt_team,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->ping,
+-		RD, player->team
+-	    );
+-	else
+-	    fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->ping
+-	    );
+-	fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_unreal_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+-    static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->team_name)
+-	fprintf( OF, fmt_team_name,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->ping,
+-		RD, player->team_name,
+-		RD, player->skin ? player->skin : "",
+-		RD, player->mesh ? player->mesh : "",
+-		RD, player->face ? player->face : ""
+-	);
+-	else
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->ping,
+-		RD, player->team,
+-		RD, player->skin ? player->skin : "",
+-		RD, player->mesh ? player->mesh : "",
+-		RD, player->face ? player->face : ""
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
+ 
+-void
+-raw_display_halflife_player_info( struct qserver *server)
++void display_savage_player_info(struct qserver *server)
+ {
+-    static char fmt[24]= "%s" "%s%d" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, play_time( player->connect_time,1)
+-	);
+-	fputs( "\n", OF);
+-    }
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-raw_display_tribes_player_info( struct qserver *server)
+-{
+-    static char fmt[24]= "%s" "%s%d" "%s%d" "%s%d" "%s%d";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->ping,
+-		RD, player->team,
+-		RD, player->packet_loss
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
+ 
+-void
+-raw_display_tribes2_player_info( struct qserver *server)
++void display_farcry_player_info(struct qserver *server)
+ {
+-    static char fmt[]= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+-    struct player *player;
+-    char *type;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	switch( player->type_flag)  {
+-	case PLAYER_TYPE_BOT: type= "Bot"; break;
+-	case PLAYER_TYPE_ALIAS: type= "Alias"; break;
+-	default: type= ""; break;
+-	}
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->team,
+-		RD, player->team_name ? player->team_name : "TEAM",
+-		RD, type,
+-		RD, player->tribe_tag ? xform_name(player->tribe_tag,server) : ""
+-	);
+-	fputs( "\n", OF);
+-    }
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%3d frags %8s %s\n", player->frags, play_time(player->connect_time, 1), xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-raw_display_bfris_player_info( struct qserver *server)
++void display_tee_player_info(struct qserver *server)
+ {
+-  static char fmt[] = "%d" "%s%d" "%s%s" "%s%d" "%s%d" "%s%d" "%s%s";
+-  struct player *player;
++	struct player *player;
+ 
+-  player= server->players;
+-  for ( ; player != NULL; player= player->next)  {
+-    fprintf( OF, fmt,
+-	    player->number,
+-	    RD, player->ship,
+-	    RD, player->team_name,
+-	    RD, player->ping,
+-	    RD, player->score,
+-	    RD, player->frags,
+-	    RD, xform_name( player->name, server)
+-	    );
+-    fputs( "\n", OF);
+-  }
+-}
+-
+-void
+-raw_display_descent3_player_info( struct qserver *server)
+-{
+-    static char fmt[]= "%s" "%s%d" "%s%d" "%s%d" "%s%d";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->frags,
+-		RD, player->deaths,
+-		RD, player->ping,
+-		RD, player->team
+-	);
+-	fputs( "\n", OF);
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t%4d score %s\n", player->score, xform_name(player->name, server));
++	}
+ }
+ 
+-void
+-raw_display_ghostrecon_player_info( struct qserver *server)
++char *get_qw_game(struct qserver *server)
+ {
+-    static char fmt[28]= "%s" "%s%d" "%s%d";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->deaths,
+-		RD, player->team
+-	);
+-	fputs( "\n", OF);
+-    }
++	struct rule *rule;
++	char *game_rule = server->type->game_rule;
++	if (game_rule == NULL || *game_rule == '\0')
++	{
++		return "";
++	} rule = server->rules;
++	for (; rule != NULL; rule = rule->next)
++	{
++		if (strcmp(rule->name, game_rule) == 0)
++		{
++			if (server->type->id == Q3_SERVER && strcmp(rule->value, "baseq3") == 0)
++			{
++				return "";
++			}
++			return rule->value;
++		}
++	}
++	rule = server->rules;
++	for (; rule != NULL; rule = rule->next)
++	{
++		if (0 == strcmp(rule->name, "game") || 0 == strcmp(rule->name, "fs_game"))
++		{
++			return rule->value;
++		}
++	}
++	return "";
+ }
+ 
+-void
+-raw_display_eye_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s";
+-    static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->team_name)
+-	    fprintf( OF, fmt_team_name,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->team_name,
+-		RD, player->skin ? player->skin : "",
+-		RD, play_time( player->connect_time,1)
+-	);
+-	else
+-	    fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->team,
+-		RD, player->skin ? player->skin : "",
+-		RD, play_time( player->connect_time,1)
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
++/* Raw output for web master types
++ */
+ 
+-void
+-raw_display_doom3_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%u";
+-    static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%u";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->tribe_tag)
+-	    fprintf( OF, fmt_team_name,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->tribe_tag,
+-		RD, player->number
+-	);
+-	else
+-	    fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->team,
+-		RD, player->number
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
++#define RD raw_delimiter
+ 
+-void
+-raw_display_hl2_player_info( struct qserver *server )
++void raw_display_server(struct qserver *server)
+ {
+-	// ATM this looks like halflife player info
+-	raw_display_halflife_player_info( server );
+-}
++	char *prefix;
++	int ping_time;
++	prefix = server->type->type_prefix;
+ 
+-void
+-raw_display_gs2_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%d" "%s%d" "%s%s" "%s%s";
+-    static const char *fmt_team_name= "%s" "%s%d" "%s%d" "%s%s" "%s%s" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-	if ( player->team_name)
+-	    fprintf( OF, fmt_team_name,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->team_name,
+-		RD, player->skin ? player->skin : "",
+-		RD, play_time( player->connect_time,1)
+-	);
++	if (server->n_requests)
++	{
++		ping_time = server->ping_total / server->n_requests;
++	}
+ 	else
+-	    fprintf( OF, fmt,
+-		xform_name( player->name, server),
+-		RD, player->score,
+-		RD, player->ping,
+-		RD, player->team,
+-		RD, player->skin ? player->skin : "",
+-		RD, play_time( player->connect_time,1)
+-	);
+-	fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_armyops_player_info( struct qserver *server)
+-{
+-	struct player *player;
+-
+-	player= server->players;
+-	for ( ; player != NULL; player= player->next)  {
+-		player->score = calculate_armyops_score( player );
++	{
++		ping_time = 999;
+ 	}
+ 
+-	raw_display_gs2_player_info(server);
+-}
++	if (server->server_name == DOWN || server->server_name == SYSERROR)
++	{
++		if (!up_servers_only)
++		{
++			fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s\n\n",
++				prefix,
++				raw_arg, RD,
++				raw_arg,
++				server->arg, RD,
++				(hostname_lookup) ? server->host_name : server->arg, RD,
++				server->server_name
++			);
++		}
++		return ;
++	}
+ 
+-void
+-raw_display_ts2_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%s" "%s%s";
+-    struct player *player;
++	if (server->server_name == TIMEOUT)
++	{
++		if (server->flags &FLAG_BROADCAST && server->n_servers)
++		{
++			fprintf(OF, "%s""%.*s%.*s""%s%s""%s%d\n", prefix, raw_arg, RD, raw_arg, server->arg, RD, server->arg, RD, server->n_servers);
++		}
++		else if (!up_servers_only)
++		{
++			fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s\n\n", prefix, raw_arg, RD, raw_arg, server->arg, RD, (hostname_lookup) ? server->host_name : server->arg, RD, TIMEOUT);
++		}
++		return ;
++	}
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
+-    {
+-	    fprintf( OF, fmt,
+-			xform_name( player->name, server),
+-			RD, player->ping,
+-			RD, player->skin ? player->skin : "",
+-			RD, play_time( player->connect_time, 1 )
++	if (server->error != NULL)
++	{
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ? server->host_name: server->arg, RD,
++			"ERROR", RD,
++			server->error
+ 		);
+-		fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_tm_player_info( struct qserver *server)
+-{
+-    static const char *fmt= "%s" "%s%d" "%s%s" "%s%s";
+-    struct player *player;
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
+-    {
+-	    fprintf( OF, fmt,
+-			xform_name( player->name, server),
+-			RD, player->ping,
+-			RD, player->skin ? player->skin : "",
+-			RD, play_time( player->connect_time, 1 )
++	}
++	else if (server->type->flags &TF_RAW_STYLE_QUAKE)
++	{
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%s""%s%d""%s%d""%s%d""%s%d""%s%d""%s%d""%s%s",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ? server->host_name: server->arg, RD,
++			xform_name(server->server_name, server), RD,
++			server->address, RD,
++			server->protocol_version, RD,
++			server->map_name, RD,
++			server->max_players, RD,
++			server->num_players, RD,
++			server->max_spectators, RD,
++			server->num_spectators, RD,
++			ping_time, RD,
++			server->n_retries,
++			show_game_in_raw ? RD : "",
++			show_game_in_raw ? get_qw_game(server): ""
+ 		);
+-		fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_ravenshield_player_info( struct qserver *server)
+-{
+-    static char fmt[24]= "%s" "%s%d" "%s%s";
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player= player->next )
+-	{
+-		fprintf( OF, fmt,
+-			xform_name( player->name, server),
+-			RD, player->frags,
+-			RD, play_time( player->connect_time,1)
++	}
++	else if (server->type->flags &TF_RAW_STYLE_TRIBES)
++	{
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ?	server->host_name: server->arg, RD,
++			xform_name(server->server_name, server), RD,
++			(server->map_name) ? server->map_name: "?", RD,
++			server->num_players, RD,
++			server->max_players
+ 		);
+-		fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_savage_player_info( struct qserver *server)
+-{
+-    static char fmt[24]= "%s" "%s%d" "%s%s";
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player= player->next )
+-	{
+-		fprintf( OF, fmt,
+-			xform_name( player->name, server),
+-			RD, player->frags,
+-			RD, play_time( player->connect_time,1)
++	}
++	else if (server->type->flags &TF_RAW_STYLE_GHOSTRECON)
++	{
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ?	server->host_name: server->arg, RD,
++			xform_name(server->server_name,	server), RD,
++			(server->map_name) ? server->map_name: "?", RD,
++			server->num_players, RD,
++			server->max_players
+ 		);
+-		fputs( "\n", OF);
+-    }
+-}
+-
+-void
+-raw_display_farcry_player_info( struct qserver *server)
+-{
+-    static char fmt[24]= "%s" "%s%d" "%s%s";
+-    struct player *player = server->players;
+-    for ( ; player != NULL; player= player->next )
+-	{
+-		fprintf( OF, fmt,
+-			xform_name( player->name, server),
+-			RD, player->frags,
+-			RD, play_time( player->connect_time,1)
++	}
++	else if (server->type->master)
++	{
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%d",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ? server->host_name: server->arg, RD,
++			server->n_servers
+ 		);
+-		fputs( "\n", OF);
+-    }
+-}
+-
+-/* XML output
+- * Contributed by <sgarner at gameplanet.co.nz> :-)
+- */
+-
+-void
+-xml_display_server( struct qserver *server)
+-{
+-	char *prefix;
+-	prefix= server->type->type_prefix;
+-
+-	if ( server->server_name == DOWN)
++	}
++	else
+ 	{
+-		if ( ! up_servers_only)
+-		{
+-			fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+-				xml_escape(prefix), xml_escape(server->arg), xml_escape(DOWN));
+-			fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+-				xml_escape((hostname_lookup)?server->host_name:server->arg));
+-			fprintf( OF, "\t</server>\n");
+-		}
+-		return;
++		fprintf(OF, "%s""%.*s%.*s""%s%s""%s%s""%s%s""%s%d""%s%d""%s%d""%s%d""%s%s",
++			prefix,
++			raw_arg, RD,
++			raw_arg,
++			server->arg, RD,
++			(hostname_lookup) ? server->host_name: server->arg, RD,
++			xform_name(server->server_name,	server), RD,
++			(server->map_name) ? xform_name(server->map_name, server): "?", RD,
++			server->max_players, RD,
++			server->num_players, RD,
++			ping_time, RD,
++			server->n_retries,
++			show_game_in_raw ? RD : "",
++			show_game_in_raw ? get_qw_game(server): ""
++		);
+ 	}
+-	if ( server->server_name == TIMEOUT)
++	fputs("\n", OF);
++
++	if (server->type->master || server->error != NULL)
+ 	{
+-		if ( server->flags & FLAG_BROADCAST && server->n_servers)
+-		{
+-			fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
+-				xml_escape(prefix), xml_escape(server->arg),
+-				xml_escape(TIMEOUT), server->n_servers);
+-			fprintf( OF, "\t</server>\n");
+-		}
+-		else if ( ! up_servers_only)
+-		{
+-			fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+-				xml_escape(prefix), xml_escape(server->arg), xml_escape(TIMEOUT));
+-			fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+-				xml_escape((hostname_lookup)?server->host_name:server->arg));
+-			fprintf( OF, "\t</server>\n");
+-		}
+-		return;
+-    }
++		fputs("\n", OF);
++		return ;
++	}
+ 
+-	if ( server->error != NULL)
++	if (get_server_rules && NULL != server->type->display_raw_rule_func )
+ 	{
+-		fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+-			xml_escape(prefix), xml_escape(server->arg), "ERROR");
+-		fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+-			xml_escape((hostname_lookup)?server->host_name:server->arg));
+-		fprintf( OF, "\t\t<error>%s</error>\n",
+-			xml_escape(server->error));
++		server->type->display_raw_rule_func(server);
+ 	}
+-	else if ( server->type->master)
++	if (get_player_info && NULL != server->type->display_raw_player_func)
+ 	{
+-		fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
+-			xml_escape(prefix), xml_escape(server->arg), "UP", server->n_servers);
++		server->type->display_raw_player_func(server);
+ 	}
+-	else  {
+-		fprintf( OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n",
+-			xml_escape(prefix), xml_escape(server->arg), "UP");
+-		fprintf( OF, "\t\t<hostname>%s</hostname>\n",
+-			xml_escape((hostname_lookup)?server->host_name:server->arg));
+-		fprintf( OF, "\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( server->server_name, server)));
+-		fprintf( OF, "\t\t<gametype>%s</gametype>\n",
+-			xml_escape(get_qw_game(server)));
+-		fprintf( OF, "\t\t<map>%s</map>\n",
+-			xml_escape(server->map_name));
+-		fprintf( OF, "\t\t<numplayers>%d</numplayers>\n",
+-			server->num_players);
+-		fprintf( OF, "\t\t<maxplayers>%d</maxplayers>\n",
+-			server->max_players);
++	fputs("\n", OF);
++}
+ 
+-		if ( !(server->type->flags & TF_RAW_STYLE_TRIBES))
++void raw_display_server_rules(struct qserver *server)
++{
++	struct rule *rule;
++	int printed = 0;
++	rule = server->rules;
++	for (; rule != NULL; rule = rule->next)
++	{
++		if (server->type->id == TRIBES2_SERVER)
+ 		{
+-			fprintf( OF, "\t\t<ping>%d</ping>\n",
+-				server->n_requests ? server->ping_total/server->n_requests : 999);
+-			fprintf( OF, "\t\t<retries>%d</retries>\n",
+-				server->n_retries);
++			char *v;
++			for (v = rule->value; *v; v++)
++			if (*v == '\n')
++			{
++				*v = ' ';
++			}
+ 		}
+ 
+-		if ( server->type->flags & TF_RAW_STYLE_QUAKE)
+-		{
+-			fprintf( OF, "\t\t<address>%s</address>\n",
+-				xml_escape(server->address));
+-			fprintf( OF, "\t\t<protocolversion>%d</protocolversion>\n",
+-				server->protocol_version);
+-		}
++		fprintf(OF, "%s%s=%s", (printed) ? RD : "", rule->name, rule->value);
++		printed++;
+ 	}
+-
+-	if ( ! server->type->master && server->error == NULL)
++	if (server->missing_rules)
+ 	{
+-	    if ( get_server_rules)
+-			server->type->display_xml_rule_func( server);
+-	    if ( get_player_info)
+-			server->type->display_xml_player_func( server);
++		fprintf(OF, "%s?", (printed) ? RD : "");
+ 	}
+-
+-	fprintf( OF, "\t</server>\n");
++	fputs("\n", OF);
+ }
+ 
+-void
+-xml_header()
++void raw_display_q_player_info(struct qserver *server)
+ {
+-	fprintf( OF, "<?xml version=\"1.0\" encoding=\"%s\"?>\n<qstat>\n",
+-		xml_encoding == ENCODING_LATIN_1 ? "iso-8859-1" : "UTF-8");
+-}
++	char fmt[] = "%d""%s%s""%s%s""%s%d""%s%s""%s%s""%s%s";
++	struct player *player;
+ 
+-void
+-xml_footer()
+-{
+-	fprintf( OF, "</qstat>\n");
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			player->number, RD,
++			xform_name(player->name, server), RD,
++			player->address, RD,
++			player->frags, RD,
++			play_time(player->connect_time, 1), RD,
++			quake_color(player->shirt_color), RD,
++			quake_color(player->pants_color)
++		);
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_server_rules( struct qserver *server)
++void raw_display_qw_player_info(struct qserver *server)
+ {
+-	struct rule *rule;
+-	rule= server->rules;
++	char fmt[128];
++	struct player *player;
++
++	strcpy(fmt, "%d""%s%s""%s%d""%s%s""%s%s""%s%s");
++	strcat(fmt, "%s%d""%s%s""%s%s");
+ 
+-	fprintf( OF, "\t\t<rules>\n");
+-	for ( ; rule != NULL; rule= rule->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<rule name=\"%s\">%s</rule>\n",
+-			xml_escape(rule->name), xml_escape(rule->value));
+-    }
+-	fprintf( OF, "\t\t</rules>\n");
++		fprintf(OF, fmt,
++			player->number, RD,
++			xform_name(player->name, server), RD,
++			player->frags, RD,
++			play_time(player->connect_time, 1), RD,
++			quake_color(player->shirt_color), RD,
++			quake_color(player->pants_color), RD,
++			player->ping, RD,
++			player->skin ? player->skin: "", RD,
++			player->team_name ? player->team_name: ""
++		);
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_q_player_info( struct qserver *server)
++void raw_display_q2_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%d";
++	static const char *fmt_team = "%s""%s%d""%s%d""%s%d";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+-			player->number);
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<address>%s</address>\n",
+-			xml_escape(player->address));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time(player->connect_time,1)));
+-
+-		if ( color_names)
++		if (server->flags &FLAG_PLAYER_TEAMS)
+ 		{
+-			fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+-				xml_escape(quake_color(player->shirt_color)));
+-			fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+-				xml_escape(quake_color(player->pants_color)));
++			fprintf(OF, fmt_team, xform_name(player->name, server), RD, player->frags, RD, player->ping, RD, player->team);
+ 		}
+ 		else
+ 		{
+-			fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+-				quake_color(player->shirt_color));
+-			fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+-				quake_color(player->pants_color));
++			fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->ping);
+ 		}
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_qw_player_info( struct qserver *server)
++void raw_display_unreal_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s""%s%s";
++	static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s""%s%s";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+-			player->number);
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time(player->connect_time,1)));
+-
+-		if ( color_names)
++		if (player->team_name)
+ 		{
+-			fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+-				xml_escape(quake_color(player->shirt_color)));
+-			fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+-				xml_escape(quake_color(player->pants_color)));
++			fprintf(OF, fmt_team_name,
++				xform_name(player->name,server), RD,
++				player->frags, RD,
++				player->ping, RD,
++				player->team_name, RD,
++				player->skin ? player->skin: "", RD,
++				player->mesh ? player->mesh: "", RD,
++				player->face ? player->face: ""
++			);
+ 		}
+ 		else
+ 		{
+-			fprintf( OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n",
+-				quake_color(player->shirt_color));
+-			fprintf( OF, "\t\t\t\t<color for=\"pants\">%s</color>\n",
+-				quake_color(player->pants_color));
++			fprintf(OF, fmt,
++				xform_name(player->name, server), RD,
++				player->frags, RD,
++				player->ping, RD,
++				player->team, RD,
++				player->skin ? player->skin: "", RD,
++				player->mesh ? player->mesh: "", RD,
++				player->face ? player->face: ""
++			);
+ 		}
+-
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+-			player->skin ? xml_escape(player->skin) : "");
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_q2_player_info( struct qserver *server)
++void raw_display_halflife_player_info(struct qserver *server)
+ {
++	static char fmt[24] = "%s""%s%d""%s%s";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n", player->frags);
+-		if ( server->flags & FLAG_PLAYER_TEAMS)
+-			fprintf( OF, "\t\t\t\t<team>%d</team>\n", player->team);
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void xml_display_player_info_info(struct player* player)
++void raw_display_fl_player_info(struct qserver *server)
+ {
+-	struct info *info;
++	static char fmt[24] = "%s""%s%d""%s%s""%s%d""%s%d";
++	struct player *player;
+ 
+-	for (info = player->info; info; info = info->next )
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if ( info->name )
+-		{
+-			char *name = xml_escape( info->name );
+-			char *value = xml_escape( info->value );
+-			fprintf( OF, "\t\t\t\t<%s>%s</%s>\n", name, value, name );
+-		}
++		fprintf(
++			OF, fmt,
++			xform_name(player->name, server), RD,
++			player->frags, RD,
++			play_time(player->connect_time, 1), RD,
++			player->ping, RD,
++			player->team
++		);
++		fputs("\n", OF);
+ 	}
+ }
+ 
+-void
+-xml_display_unreal_player_info( struct qserver *server)
++void raw_display_tribes_player_info(struct qserver *server)
+ {
++	static char fmt[24] = "%s""%s%d""%s%d""%s%d""%s%d";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		if ( -999 != player->deaths )
+-		{
+-			fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
+-		}
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->ping, RD, player->team, RD, player->packet_loss);
++		fputs("\n", OF);
++	}
++}
+ 
+-		if ( player->team_name)
+-		{
+-		    fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+-			xml_escape(player->team_name));
+-		}
+-		else if ( -1 != player->team )
+-		{
+-		    fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
+-		}
++void raw_display_tribes2_player_info(struct qserver *server)
++{
++	static char fmt[] = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
++	struct player *player;
++	char *type;
+ 
+-		// Some games dont provide
+-		// so only display if they do
+-		if ( player->skin )
+-		{
+-			fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+-				player->skin ? xml_escape(player->skin) : "");
+-		}
+-		if ( player->mesh )
+-		{
+-			fprintf( OF, "\t\t\t\t<mesh>%s</mesh>\n",
+-				player->mesh ? xml_escape(player->mesh) : "");
+-		}
+-		if ( player->face )
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		switch (player->type_flag)
+ 		{
+-			fprintf( OF, "\t\t\t\t<face>%s</face>\n",
+-				player->face ? xml_escape(player->face) : "");
++			case PLAYER_TYPE_BOT:
++				type = "Bot";
++				break;
++			case PLAYER_TYPE_ALIAS:
++				type = "Alias";
++				break;
++			default:
++				type = "";
++				break;
+ 		}
+-
+-		xml_display_player_info_info(player);
+-		fprintf( OF, "\t\t\t</player>\n");
++		fprintf(OF, fmt,
++			xform_name(player->name, server), RD,
++			player->frags, RD,
++			player->team, RD,
++			player->team_name ? player->team_name : "TEAM", RD,
++			type, RD,
++			player->tribe_tag ? xform_name(player->tribe_tag, server): ""
++		);
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_halflife_player_info( struct qserver *server)
++void raw_display_bfris_player_info(struct qserver *server)
+ {
++	static char fmt[] = "%d""%s%d""%s%s""%s%d""%s%d""%s%d""%s%s";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		fprintf(OF, fmt,
++			player->number, RD,
++			player->ship, RD,
++			player->team_name, RD,
++			player->ping, RD,
++			player->score, RD,
++			player->frags, RD,
++			xform_name(player->name, server)
++		);
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_tribes_player_info( struct qserver *server)
++void raw_display_descent3_player_info(struct qserver *server)
+ {
++	static char fmt[] = "%s""%s%d""%s%d""%s%d""%s%d";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, player->deaths, RD, player->ping, RD, player->team);
++		fputs("\n", OF);
++	}
++}
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		fprintf( OF, "\t\t\t\t<packetloss>%d</packetloss>\n",
+-			player->packet_loss);
++void raw_display_ghostrecon_player_info(struct qserver *server)
++{
++	static char fmt[28] = "%s""%s%d""%s%d";
++	struct player *player;
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->deaths, RD, player->team);
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_tribes2_player_info( struct qserver *server)
++void raw_display_eye_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s";
++	static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
+ 	struct player *player;
+-	char *type;
+-
+-	fprintf( OF, "\t\t<players>\n");
+ 
+-	player= server->players;
+-	for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if ( player->team_name)
++		if (player->team_name)
+ 		{
+-			switch( player->type_flag)
+-			{
+-				case PLAYER_TYPE_BOT:
+-					type= "Bot";
+-					break;
+-				case PLAYER_TYPE_ALIAS:
+-					type= "Alias";
+-					break;
+-				default:
+-					type= "";
+-					break;
+-			}
+-
+-			fprintf( OF, "\t\t\t<player>\n");
+-
+-			fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-				xml_escape(xform_name( player->name, server)));
+-			fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-				player->frags);
+-			fprintf( OF, "\t\t\t\t<team number=\"%d\">%s</team>\n",
+-				player->team, xml_escape(player->team_name));
+-			fprintf( OF, "\t\t\t\t<type>%s</type>\n",
+-				xml_escape(type));
+-			fprintf( OF, "\t\t\t\t<clan>%s</clan>\n",
+-				player->tribe_tag ? xml_escape(xform_name(player->tribe_tag,server)) : "");
+-
+-			fprintf( OF, "\t\t\t</player>\n");
++			fprintf(OF, fmt_team_name,
++				xform_name(player->name,server), RD,
++				player->score, RD,
++				player->ping, RD,
++				player->team_name, RD,
++				player->skin ? player->skin: "", RD,
++				play_time(player->connect_time,1)
++			);
+ 		}
+-    }
+-
+-	fprintf( OF, "\t\t</players>\n");
++		else
++		{
++			fprintf(OF, fmt,
++				xform_name(player->name, server), RD,
++				player->score, RD,
++				player->ping, RD,
++				player->team, RD,
++				player->skin ? player->skin: "", RD,
++				play_time(player->connect_time, 1)
++			);
++		}
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_bfris_player_info( struct qserver *server)
++void raw_display_doom3_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%u";
++	static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%u";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player number=\"%d\">\n",
+-			player->number);
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score type=\"score\">%d</score>\n",
+-			player->score);
+-		fprintf( OF, "\t\t\t\t<score type=\"frags\">%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+-			xml_escape(player->team_name));
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		fprintf( OF, "\t\t\t\t<ship>%d</ship>\n",
+-			player->ship);
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		if (player->tribe_tag)
++		{
++			fprintf(OF, fmt_team_name, xform_name(player->name, server), RD, player->score, RD, player->ping, RD, player->tribe_tag, RD, player->number);
++		}
++		else
++		{
++			fprintf(OF, fmt, xform_name(player->name, server), RD, player->score, RD, player->ping, RD, player->team, RD, player->number);
++		}
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_descent3_player_info( struct qserver *server)
++void raw_display_gs2_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%d""%s%d""%s%s""%s%s";
++	static const char *fmt_team_name = "%s""%s%d""%s%d""%s%s""%s%s""%s%s";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n",
+-			player->deaths);
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		if (player->team_name)
++		{
++			fprintf(OF, fmt_team_name, xform_name(player->name, server), RD,
++				player->score, RD,
++				player->ping, RD,
++				player->team_name, RD,
++				player->skin ? player->skin: "", RD,
++				play_time(player->connect_time,1)
++			);
++		}
++		else
++		{
++			fprintf(OF, fmt,
++				xform_name(player->name,server), RD,
++				player->score, RD,
++				player->ping, RD,
++				player->team, RD,
++				player->skin ? player->skin: "", RD,
++				play_time(player->connect_time,1)
++			);
++		}
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_ravenshield_player_info( struct qserver *server)
++void raw_display_armyops_player_info(struct qserver *server)
+ {
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
+-
+-		fprintf( OF, "\t\t\t</player>\n");
++		player->score = calculate_armyops_score(player);
+ 	}
+ 
+-	fprintf( OF, "\t\t</players>\n");
++	raw_display_gs2_player_info(server);
+ }
+ 
+-
+-void
+-xml_display_ghostrecon_player_info( struct qserver *server)
++void raw_display_ts2_player_info(struct qserver *server)
+ {
++	static const char *fmt = "%s""%s%d""%s%s""%s%s";
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
+-
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, fmt,
++			xform_name(player->name,server), RD,
++			player->ping, RD,
++			player->skin ? player->skin: "", RD,
++			play_time(player->connect_time, 1)
++		);
++		fputs("\n", OF);
++	}
++}
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n",
+-			player->deaths);
+-		fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
++void raw_display_ts3_player_info(struct qserver *server)
++{
++	static const char *fmt = "%s""%s%s""%s%s";
++	struct player *player;
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			xform_name(player->name,server), RD,
++			player->skin ? player->skin: "", RD,
++			play_time(player->connect_time, 1)
++		);
++		fputs("\n", OF);
+ 	}
+-
+-	fprintf( OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_eye_player_info( struct qserver *server)
++void raw_display_bfbc2_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	static const char *fmt = "%s""%s%s""%s%s";
++	struct player *player;
+ 
+-    fprintf( OF, "\t\t<players>\n");
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			xform_name(player->name,server), RD,
++			player->skin ? player->skin: "", RD,
++			play_time(player->connect_time, 1)
++		);
++		fputs("\n", OF);
++	}
++}
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-		fprintf( OF, "\t\t\t<player>\n");
++void raw_display_wic_player_info(struct qserver *server)
++{
++	static const char *fmt = "%s""%s%d""%s%s""%s%s";
++	struct player *player;
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->score);
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		if ( player->team_name)
+-		    fprintf( OF, "\t\t\t\t<team>%s</team>\n",
+-			xml_escape(player->team_name));
+-		else
+-		    fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
+-		if ( player->skin)
+-		    fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+-			xml_escape(player->skin));
+-		if ( player->connect_time)
+-		    fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			xform_name(player->name,server), RD,
++			player->score, RD,
++			player->team_name, RD,
++			player->tribe_tag ? player->tribe_tag : ""
++		);
++		fputs("\n", OF);
++	}
++}
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
+-    }
++void raw_display_ventrilo_player_info(struct qserver *server)
++{
++	static const char *fmt = "%s""%s%d""%s%s""%s%s";
++	struct player *player;
+ 
+-    fprintf( OF, "\t\t</players>\n");
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(
++			OF, fmt,
++			xform_name(player->name, server),
++			RD, player->team,
++			RD,	player->team_name,
++			RD,	play_time(player->connect_time, 1)
++		);
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_doom3_player_info( struct qserver *server)
++void raw_display_tm_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	static const char *fmt = "%s""%s%d""%s%s""%s%s";
++	struct player *player;
+ 
+-    fprintf( OF, "\t\t<players>\n");
+-
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)  {
+-		fprintf( OF, "\t\t\t<player>\n");
+-
+-		fprintf( OF, "\t\t\t\t<number>%u</number>\n",
+-			player->number);
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->score);
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n",
+-			player->ping);
+-		if ( player->tribe_tag)
+-		    fprintf( OF, "\t\t\t\t<clan>%s</clan>\n",
+-			player->tribe_tag ? xml_escape(xform_name(player->tribe_tag,server)) : "");
+-		else
+-		    fprintf( OF, "\t\t\t\t<team>%d</team>\n",
+-			player->team);
+-		if ( player->skin)
+-		    fprintf( OF, "\t\t\t\t<skin>%s</skin>\n",
+-			xml_escape(player->skin));
+-		if ( player->connect_time)
+-		    fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt,
++			xform_name(player->name,server), RD,
++			player->ping, RD,
++			player->skin ? player->skin: "", RD,
++			play_time(player->connect_time,1)
++		);
++		fputs("\n", OF);
++	}
++}
+ 
+-		xml_display_player_info_info(player);
++void raw_display_tee_player_info(struct qserver *server)
++{
++	static const char *fmt = "%s";
++	struct player *player;
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt, xform_name(player->name,server) );
++		fputs("\n", OF);
++	}
++}
+ 
+-    fprintf( OF, "\t\t</players>\n");
++void raw_display_ravenshield_player_info(struct qserver *server)
++{
++	static char fmt[24] = "%s""%s%d""%s%s";
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_hl2_player_info( struct qserver *server )
++void raw_display_savage_player_info(struct qserver *server)
+ {
+-	// ATM this looks like halflife player info
+-	xml_display_halflife_player_info( server );
++	static char fmt[24] = "%s""%s%d""%s%s";
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++		fputs("\n", OF);
++	}
+ }
+ 
+-void
+-xml_display_gs2_player_info( struct qserver *server)
++void raw_display_farcry_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	static char fmt[24] = "%s""%s%d""%s%s";
++	struct player *player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, fmt, xform_name(player->name, server), RD, player->frags, RD, play_time(player->connect_time, 1));
++		fputs("\n", OF);
++	}
++}
+ 
+-    fprintf( OF, "\t\t<players>\n");
++/* XML output
++ * Contributed by <sgarner at gameplanet.co.nz> :-)
++ */
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
+-	{
+-		fprintf( OF, "\t\t\t<player>\n");
++void xml_display_server(struct qserver *server)
++{
++	char *prefix;
++	prefix = server->type->type_prefix;
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
+-		if ( NA_INT != player->score )
++	if (server->server_name == DOWN)
++	{
++		if (!up_servers_only)
+ 		{
+-			fprintf( OF, "\t\t\t\t<score>%d</score>\n", player->score);
++			fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), xml_escape(DOWN));
++			fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++			fprintf(OF, "\t</server>\n");
+ 		}
+-		if ( NA_INT != player->deaths )
++		return;
++	}
++	if (server->server_name == TIMEOUT)
++	{
++		if (server->flags &FLAG_BROADCAST && server->n_servers)
+ 		{
+-			fprintf( OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++			fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n",
++				xml_escape(prefix),
++				xml_escape(server->arg),
++				xml_escape(TIMEOUT),
++				server->n_servers
++			);
++			fprintf(OF, "\t</server>\n");
+ 		}
+-		if ( NA_INT != player->frags )
++		else if (!up_servers_only)
+ 		{
+-			fprintf( OF, "\t\t\t\t<frags>%d</frags>\n", player->frags);
++			fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), xml_escape(TIMEOUT));
++			fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++			fprintf(OF, "\t</server>\n");
+ 		}
+-		if ( player->team_name )
++		return ;
++	}
++
++	if (server->error != NULL)
++	{
++		fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), "ERROR");
++		fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++		fprintf(OF, "\t\t<error>%s</error>\n", xml_escape(server->error));
++	}
++	else if (server->type->master)
++	{
++		fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\" servers=\"%d\">\n", xml_escape(prefix), xml_escape(server->arg), "UP", server->n_servers);
++	}
++	else
++	{
++		fprintf(OF, "\t<server type=\"%s\" address=\"%s\" status=\"%s\">\n", xml_escape(prefix), xml_escape(server->arg), "UP");
++		fprintf(OF, "\t\t<hostname>%s</hostname>\n", xml_escape((hostname_lookup) ? server->host_name: server->arg));
++		fprintf(OF, "\t\t<name>%s</name>\n", xml_escape(xform_name(server->server_name, server)));
++		fprintf(OF, "\t\t<gametype>%s</gametype>\n", xml_escape(get_qw_game(server)));
++		fprintf(OF, "\t\t<map>%s</map>\n", xml_escape(xform_name(server->map_name, server)));
++		fprintf(OF, "\t\t<numplayers>%d</numplayers>\n", server->num_players);
++		fprintf(OF, "\t\t<maxplayers>%d</maxplayers>\n", server->max_players);
++		fprintf(OF, "\t\t<numspectators>%d</numspectators>\n", server->num_spectators);
++		fprintf(OF, "\t\t<maxspectators>%d</maxspectators>\n", server->max_spectators);
++
++		if (!(server->type->flags &TF_RAW_STYLE_TRIBES))
+ 		{
+-		    fprintf( OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++			fprintf(OF, "\t\t<ping>%d</ping>\n", server->n_requests ? server->ping_total / server->n_requests: 999);
++			fprintf(OF, "\t\t<retries>%d</retries>\n", server->n_retries);
+ 		}
+-		else
++
++		if (server->type->flags &TF_RAW_STYLE_QUAKE)
+ 		{
+-		    fprintf( OF, "\t\t\t\t<team>%d</team>\n",player->team);
++			fprintf(OF, "\t\t<address>%s</address>\n", xml_escape(server->address));
++			fprintf(OF, "\t\t<protocolversion>%d</protocolversion>\n", server->protocol_version);
+ 		}
++	}
+ 
+-		if ( player->skin)
++	if (!server->type->master && server->error == NULL)
++	{
++		if (get_server_rules && NULL != server->type->display_xml_rule_func )
+ 		{
+-		    fprintf( OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++			server->type->display_xml_rule_func(server);
+ 		}
+-
+-		if ( player->connect_time)
++		if (get_player_info && NULL != server->type->display_xml_player_func )
+ 		{
+-		    fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++			server->type->display_xml_player_func(server);
+ 		}
++	}
+ 
+-		xml_display_player_info_info(player);
++	fprintf(OF, "\t</server>\n");
++}
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
+-    }
++void xml_header()
++{
++	if (xml_encoding == ENCODING_LATIN_1)
++	{
++		fprintf(OF, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<qstat>\n");
++	}
++	else if (output_bom)
++	{
++		fprintf(OF, "%c%c%c<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<qstat>\n", 0xEF, 0xBB, 0xBF);
++	}
++	else
++	{
++		fprintf(OF, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<qstat>\n");
++	}
++}
+ 
+-    fprintf( OF, "\t\t</players>\n");
++void xml_footer()
++{
++	fprintf(OF, "</qstat>\n");
+ }
+ 
+-void
+-xml_display_armyops_player_info( struct qserver *server)
++void xml_display_server_rules(struct qserver *server)
+ {
+-	struct player *player;
++	struct rule *rule;
++	rule = server->rules;
+ 
+-	player= server->players;
+-	for ( ; player != NULL; player= player->next)
++	fprintf(OF, "\t\t<rules>\n");
++	for (; rule != NULL; rule = rule->next)
+ 	{
+-		player->score = calculate_armyops_score( player );
++		fprintf(OF, "\t\t\t<rule name=\"%s\">%s</rule>\n", xml_escape(rule->name), xml_escape(rule->value));
+ 	}
+-
+-	xml_display_gs2_player_info(server);
++	fprintf(OF, "\t\t</rules>\n");
+ }
+ 
+-void
+-xml_display_ts2_player_info( struct qserver *server)
++void xml_display_q_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    fprintf( OF, "\t\t<players>\n");
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<address>%s</address>\n", xml_escape(player->address));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-		if ( player->connect_time )
++		if (color_names)
++		{
++			fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", xml_escape(quake_color(player->shirt_color)));
++			fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", xml_escape(quake_color(player->pants_color)));
++		}
++		else
+ 		{
+-		    fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++			fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", quake_color(player->shirt_color));
++			fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", quake_color(player->pants_color));
+ 		}
+ 
+-		xml_display_player_info_info(player);
+-		fprintf( OF, "\t\t\t</player>\n");
+-    }
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-    fprintf( OF, "\t\t</players>\n");
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_tm_player_info( struct qserver *server)
++void xml_display_qw_player_info(struct qserver *server)
+ {
+-    struct player *player;
++	struct player *player;
+ 
+-    fprintf( OF, "\t\t<players>\n");
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-		if ( player->connect_time )
++		if (color_names)
++		{
++			fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", xml_escape(quake_color(player->shirt_color)));
++			fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", xml_escape(quake_color(player->pants_color)));
++		}
++		else
+ 		{
+-		    fprintf( OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time( player->connect_time,1)));
++			fprintf(OF, "\t\t\t\t<color for=\"shirt\">%s</color>\n", quake_color(player->shirt_color));
++			fprintf(OF, "\t\t\t\t<color for=\"pants\">%s</color>\n", quake_color(player->pants_color));
+ 		}
+ 
+-		xml_display_player_info_info(player);
+-		fprintf( OF, "\t\t\t</player>\n");
+-    }
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", player->skin ? xml_escape(player->skin): "");
++		fprintf(OF, "\t\t\t\t<team>%s</team>\n", player->team_name ? xml_escape(player->team_name): "");
+ 
+-    fprintf( OF, "\t\t</players>\n");
+-}
+ 
++		fprintf(OF, "\t\t\t</player>\n");
++	}
++
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-void
+-xml_display_savage_player_info( struct qserver *server)
++void xml_display_q2_player_info(struct qserver *server)
+ {
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		if (server->flags &FLAG_PLAYER_TEAMS)
++		{
++			fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		}
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+ 
+-	fprintf( OF, "\t\t</players>\n");
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-void
+-xml_display_farcry_player_info( struct qserver *server)
++void xml_display_player_info_info(struct player *player)
++{
++	struct info *info;
++
++	for (info = player->info; info; info = info->next)
++	{
++		if (info->name)
++		{
++			char *name = xml_escape(info->name);
++			char *value = xml_escape(info->value);
++			fprintf(OF, "\t\t\t\t<%s>%s</%s>\n", name, value, name);
++		}
++	}
++}
++
++void xml_display_unreal_player_info(struct qserver *server)
+ {
+ 	struct player *player;
+ 
+-	fprintf( OF, "\t\t<players>\n");
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	player= server->players;
+-    for ( ; player != NULL; player= player->next)
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		fprintf( OF, "\t\t\t<player>\n");
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		if ( - 999 != player->deaths)
++		{
++			fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++		} fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++		if (player->team_name)
++		{
++			fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++		}
++		else if ( - 1 != player->team)
++		{
++			fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		}
+ 
+-		fprintf( OF, "\t\t\t\t<name>%s</name>\n",
+-			xml_escape(xform_name( player->name, server)));
+-		fprintf( OF, "\t\t\t\t<score>%d</score>\n",
+-			player->frags);
+-		fprintf( OF, "\t\t\t\t<time>%s</time>\n",
+-			xml_escape(play_time( player->connect_time,1)));
++		// Some games dont provide
++		// so only display if they do
++		if (player->skin)
++		{
++			fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", player->skin ? xml_escape(player->skin): "");
++		}
++		if (player->mesh)
++		{
++			fprintf(OF, "\t\t\t\t<mesh>%s</mesh>\n", player->mesh ? xml_escape(player->mesh): "");
++		}
++		if (player->face)
++		{
++			fprintf(OF, "\t\t\t\t<face>%s</face>\n", player->face ? xml_escape(player->face): "");
++		}
+ 
+-		fprintf( OF, "\t\t\t</player>\n");
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+ 
+-	fprintf( OF, "\t\t</players>\n");
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-
+-void
+-display_progress()
++void xml_display_halflife_player_info(struct qserver *server)
+ {
+-    static struct timeval rate_start= {0,0};
+-    char rate[32];
+-    struct timeval now;
++	struct player *player;
+ 
+-    gettimeofday( &now, NULL);
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    if ( ! rate_start.tv_sec)
+-	{
+-		rate_start= now;
+-		rate[0]='\0';
+-    }
+-    else
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		int delta= time_delta( &now, &rate_start);
+-		if ( delta > 1500 )
+-			sprintf( rate, "  %d servers/sec  ", (num_servers_returned+num_servers_timed_out)*1000 / delta);
+-		else
+-			rate[0]='\0';
+-    }
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-	// only print out every 'progress' number of servers.
+-	if (
+-		0 != num_servers_returned+num_servers_timed_out &&
+-		( progress == 1 ||
+-		(num_servers_returned+num_servers_timed_out) % progress == 0 )
+-	)
+-	{
+-		fprintf( stderr, "\r%d/%d (%d timed out, %d down)%s",
+-			num_servers_returned+num_servers_timed_out,
+-			num_servers_total,
+-			num_servers_timed_out,
+-			num_servers_down,
+-			rate
+-		);
+-	}
+-}
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-/* ----- END MODIFICATION ----- Don't need to change anything below here. */
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-void set_non_blocking( int fd);
+-int set_fds( fd_set *fds);
+-void get_next_timeout( struct timeval *timeout);
++void xml_display_fl_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-void set_file_descriptors();
+-int wait_for_file_descriptors( struct timeval *timeout);
+-struct qserver * get_next_ready_server();
++	fprintf(OF, "\t\t<players>\n");
+ 
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-/* Misc flags
+- */
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-struct timeval packet_recv_time;
+-int one_server_type_id= ~ MASTER_SERVER;
+-static int one= 1;
+-static int little_endian;
+-static int big_endian;
+-unsigned int swap_long( void *);
+-unsigned short swap_short( void *);
+-float swap_float_from_little( void *f);
+-char * strndup( const char *string, size_t len);
+-#define FORCE 1
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-/* Print an error message and the program usage notes
+- */
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-void
+-usage( char *msg, char **argv, char *a1)
++void xml_display_tribes_player_info(struct qserver *server)
+ {
+-	int i;
+-    server_type *type;
+-	server_type **sorted_types;
+-
+-    if ( msg)
+-	fprintf( stderr, msg, a1);
++	struct player *player;
+ 
+-    printf( "Usage: %s [options ...]\n", argv[0]);
+-    printf( "\t[-default server-type] [-cfg file] [-f file] [host[:port]] ...\n");
+-    printf( "Where host is an IP address or host name\n");
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	sorted_types = (server_type **) malloc( sizeof(server_type *) * n_server_types );
+-	type = &types[0];
+-	for ( i = 0; type->id != Q_UNKNOWN_TYPE; type++, i++ )
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		sorted_types[i] = type;
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<packetloss>%d</packetloss>\n", player->packet_loss);
++
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+-    quicksort( (void**)sorted_types, 0, n_server_types-1, (int (*)(void*,void*)) type_option_compare );
+ 
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-    for ( i = 0; i < n_server_types; i++ )
+-    {
+-    	type = sorted_types[i];
+-		printf( "%s\t\tquery %s server\n", type->type_option, type->game_name );
+-	}
+-
+-    quicksort( (void**)sorted_types, 0, n_server_types-1, (int (*)(void*,void*)) type_string_compare );
+-    printf( "-default\tset default server type:");
+-    for ( i = 0; i < n_server_types; type++, i++ )
+-    {
+-        type = sorted_types[i];
+-		printf( " %s", type->type_string );
+-	}
+-    puts("");
+-	printf( "-nocfg\t\tIgnore qstat configuration loaded from any default location. Must be the first option on the command-line.\n" );
+-    printf( "-cfg\t\tread the extended types from given file not the default one\n");
+-    printf( "-f\t\tread hosts from file\n");
+-    printf( "-R\t\tfetch and display server rules\n");
+-    printf( "-P\t\tfetch and display player info\n");
+-    printf( "-sort\t\tsort servers and/or players\n");
+-    printf( "-u\t\tonly display servers that are up\n");
+-    printf( "-nf\t\tdo not display full servers\n");
+-    printf( "-ne\t\tdo not display empty servers\n");
+-	printf( "-nh\t\tdo not display header line.\n" );
+-    printf( "-cn\t\tdisplay color names instead of numbers\n");
+-    printf( "-ncn\t\tdisplay color numbers instead of names\n");
+-    printf( "-hc\t\tdisplay colors in #rrggbb format\n");
+-    printf( "-tc\t\tdisplay time in clock format (DhDDmDDs)\n");
+-    printf( "-tsw\t\tdisplay time in stop-watch format (DD:DD:DD)\n");
+-    printf( "-ts\t\tdisplay time in seconds\n");
+-    printf( "-pa\t\tdisplay player address\n");
+-    printf( "-hpn\t\tdisplay player names in hex\n");
+-    printf( "-hsn\t\tdisplay server names in hex\n");
+-    printf( "-nh\t\tdo not display header\n");
+-    printf( "-old\t\told style display\n");
+-    printf( "-progress\tdisplay progress meter (text only)\n");
+-    printf( "-retry\t\tnumber of retries, default is %d\n", DEFAULT_RETRIES);
+-    printf( "-interval\tinterval between retries, default is %.2f seconds\n",
+-	DEFAULT_RETRY_INTERVAL / 1000.0);
+-    printf( "-mi\t\tinterval between master server retries, default is %.2f seconds\n",
+-	(DEFAULT_RETRY_INTERVAL*4) / 1000.0);
+-    printf( "-timeout\ttotal time in seconds before giving up\n");
+-    printf( "-maxsim\t\tset maximum simultaneous queries\n");
+-    printf( "-sendinterval\t\tset time in ms between sending packets, default %u\n", sendinterval);
+-    printf( "-errors\t\tdisplay errors\n");
+-    printf( "-allowserverdups\t\tallow adding multiple servers with same ip:port (needed for ts2)\n");
+-    printf( "-of\t\toutput file\n");
+-	printf( "-af\t\tLike -of, but append to the file\n" );
+-    printf( "-raw <delim>\toutput in raw format using <delim> as delimiter\n");
+-	printf( "-mdelim <delim>\tFor rules with multi values use <delim> as delimiter\n");
+-    printf( "-xml\t\toutput status data as an XML document\n");
+-    printf( "-Th,-Ts,-Tpt\toutput templates: header, server and player\n");
+-	printf( "-Tr,-Tt\t\toutput templates: rule, and trailer\n");
+-    printf( "-srcport <range>\tSend packets from these network ports\n");
+-    printf( "-srcip <IP>\tSend packets using this IP address\n");
+-    printf( "-H\t\tresolve host names\n");
+-    printf( "-Hcache\t\thost name cache file\n");
+-	printf( "-carets\t\tDisplay carets in Quake 3 player names\n" );
+-	printf( "-d\t\tEnable debug options. Specify multiple times to increase debug level.\n");
+-#ifdef ENABLE_DUMP
+-	printf( "-dump\t\twrite received raw packets to dumpNNN files which must not exist before\n");
+-	printf( "-pkt <file>\tuse file as server reply instead of quering the server. Works only with TF_SINGLE_QUERY servers\n");
+-#endif
+-	printf( "-htmlmode\tConvert <, >, and & to the equivalent HTML entities\n" );
+-	printf( "-htmlnames\tColorize Quake 3 and Tribes 2 player names using html font tags\n" );
+-	printf( "-nohtmlnames\tDo not colorize Quake 3 and Tribes 2 player names even if $HTML is used in an output template.\n" );
+-	printf( "-showgameport\tAlways display the game port in QStat output.\n" );
+-	printf( "-noportoffset\tDont use builtin status port offsets ( assume query port was specified ).\n" );
+-	printf( "-raw-arg\tWhen used with -raw, always display the server address as it appeared in a file or on the command-line.\n" );
+-	printf( "-utf8\t\tUse the UTF-8 character encoding for XML output.\n" );
+-#ifdef _WIN32
+-	printf( "-noconsole\t\tFree the console\n" );
+-#endif
+-    printf( "\n");
+-    printf( "Sort keys:\n");
+-    printf( "  servers: p=by-ping, g=by-game, i=by-IP-address, h=by-hostname, n=by-#-players, l=by-list-order\n");
+-    printf( "  players: P=by-ping, F=by-frags, T=by-team, N=by-name\n");
+-    printf( "\nqstat version %s\n", VERSION);
+-    exit(0);
+-}
++void xml_display_tribes2_player_info(struct qserver *server)
++{
++	struct player *player;
++	char *type;
+ 
+-struct server_arg  {
+-    int type_id;
+-    server_type *type;
+-    char *arg;
+-    char *outfilename;
+-    char *query_arg;
+-};
++	fprintf(OF, "\t\t<players>\n");
+ 
+-server_type*
+-find_server_type_id( int type_id)
+-{
+-    server_type *type= &types[0];
+-    for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+-	if ( type->id == type_id)
+-	    return type;
+-    return NULL;
+-}
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		if (player->team_name)
++		{
++			switch (player->type_flag)
++			{
++				case PLAYER_TYPE_BOT:
++					type = "Bot";
++					break;
++				case PLAYER_TYPE_ALIAS:
++					type = "Alias";
++					break;
++				default:
++					type = "";
++					break;
++			}
+ 
+-server_type*
+-find_server_type_string( char* type_string)
+-{
+-    server_type *type= &types[0];
+-    char *t= type_string;
+-    for ( ; *t; t++) *t= tolower( *t);
++			fprintf(OF, "\t\t\t<player>\n");
++
++			fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++			fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++			fprintf(OF, "\t\t\t\t<team number=\"%d\">%s</team>\n", player->team, xml_escape(player->team_name));
++			fprintf(OF, "\t\t\t\t<type>%s</type>\n", xml_escape(type));
++			fprintf(OF, "\t\t\t\t<clan>%s</clan>\n", player->tribe_tag ? xml_escape(xform_name(player->tribe_tag, server)): "");
++
++			fprintf(OF, "\t\t\t</player>\n");
++		}
++	}
+ 
+-    for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+-	if ( strcmp( type->type_string, type_string) == 0)
+-	    return type;
+-    return NULL;
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-server_type*
+-find_server_type_option( char* option)
++void xml_display_bfris_player_info(struct qserver *server)
+ {
+-    server_type *type= &types[0];
+-    for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+-	if ( strcmp( type->type_option, option) == 0)
+-	    return type;
+-    return NULL;
++	struct player *player;
++
++	fprintf(OF, "\t\t<players>\n");
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player number=\"%d\">\n", player->number);
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score type=\"score\">%d</score>\n", player->score);
++		fprintf(OF, "\t\t\t\t<score type=\"frags\">%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<ship>%d</ship>\n", player->ship);
++
++		fprintf(OF, "\t\t\t</player>\n");
++	}
++
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-server_type*
+-parse_server_type_option( char* option, int *outfile, char **query_arg)
++void xml_display_descent3_player_info(struct qserver *server)
+ {
+-    server_type *type= &types[0];
+-    char *comma, *arg;
+-    int len;
++	struct player *player;
+ 
+-    *outfile= 0;
+-    *query_arg= 0;
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    comma= strchr( option, ',');
+-    if ( comma)
+-	*comma++= '\0';
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-    for ( ; type->id != Q_UNKNOWN_TYPE; type++)
+-	if ( strcmp( type->type_option, option) == 0)
+-	    break;
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+ 
+-    if ( type->id == Q_UNKNOWN_TYPE)
+-	return NULL;
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-    if ( ! comma)
+-	return type;
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-    if ( strcmp( comma, "outfile") == 0)  {
+-	*outfile= 1;
+-	comma= strchr( comma, ',');
+-	if ( ! comma)
+-	    return type;
+-	*comma++= '\0';
+-    }
++void xml_display_ravenshield_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-    *query_arg= strdup(comma);
+-    arg= comma;
+-    do  {
+-	comma= strchr( arg, ',');
+-	if (comma)
+-	    len= comma-arg;
+-	else
+-	    len= strlen( arg);
+-	if ( strncmp( arg, "outfile", len) == 0)
+-	    *outfile= 1;
+-	arg= comma+1;
+-    } while ( comma);
+-    return type;
+-}
+-
+-void
+-add_server_arg( char *arg, int type, char *outfilename, char *query_arg,
+-	struct server_arg **args, int *n, int *max)
+-{
+-    if ( *n == *max)  {
+-	if ( *max == 0)  {
+-	    *max= 4;
+-	    *args= (struct server_arg*)malloc(sizeof(struct server_arg) * (*max));
+-	}
+-	else  {
+-	    (*max)*= 2;
+-	    *args= (struct server_arg*) realloc( *args,
+-		sizeof(struct server_arg) * (*max));
++	fprintf(OF, "\t\t<players>\n");
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+-    }
+-    (*args)[*n].type_id= type;
+-/*    (*args)[*n].type= find_server_type_id( type); */
+-    (*args)[*n].type= NULL;
+-    (*args)[*n].arg= arg;
+-    (*args)[*n].outfilename= outfilename;
+-    (*args)[*n].query_arg= query_arg;
+-    (*n)++;
++
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+ 
+-void
+-add_query_param( struct qserver *server, char *arg)
++void xml_display_ghostrecon_player_info(struct qserver *server)
+ {
+-    char *equal;
+-    struct query_param *param;
++	struct player *player;
+ 
+-    equal= strchr( arg, '=');
+-    *equal++= '\0';
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    param= (struct query_param *) malloc( sizeof(struct query_param));
+-    param->key= arg;
+-    param->value= equal;
+-    sscanf( equal, "%i", &param->i_value);
+-    sscanf( equal, "%i", &param->ui_value);
+-    param->next= server->params;
+-    server->params= param;
+-}
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-char *
+-get_param_value( struct qserver *server, const char *key, char *default_value)
+-{
+-    struct query_param *p= server->params;
+-    for ( ; p; p= p->next)
+-	if ( strcasecmp( key, p->key) == 0)
+-	    return p->value;
+-    return default_value;
+-}
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++		fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+ 
+-int
+-get_param_i_value( struct qserver *server, char *key, int default_value)
+-{
+-    struct query_param *p= server->params;
+-    for ( ; p; p= p->next)
+-	if ( strcasecmp( key, p->key) == 0)
+-	    return p->i_value;
+-    return default_value;
+-}
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-unsigned int
+-get_param_ui_value( struct qserver *server, char *key,
+-	unsigned int default_value)
+-{
+-    struct query_param *p= server->params;
+-    for ( ; p; p= p->next)
+-	if ( strcasecmp( key, p->key) == 0)
+-	    return p->ui_value;
+-    return default_value;
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-int
+-parse_source_address( char *addr, unsigned int *ip, unsigned short *port)
++void xml_display_eye_player_info(struct qserver *server)
+ {
+-    char *colon;
+-    colon= strchr( addr, ':');
+-    if ( colon)  {
+-	*colon= '\0';
+-	*port= atoi( colon+1);
+-	if ( colon == addr)
+-	    return 0;
+-    }
+-    else
+-	*port= 0;
++	struct player *player;
+ 
+-    *ip= inet_addr( addr);
+-    if ( *ip == INADDR_NONE && !isdigit( (unsigned char)*ip))
+-	*ip= hcache_lookup_hostname( addr);
+-    if ( *ip == INADDR_NONE)  {
+-	fprintf( stderr, "%s: Not an IP address or unknown host name\n", addr);
+-	return -1;
+-    }
+-    *ip= ntohl( *ip);
+-    return 0;
+-}
++	fprintf(OF, "\t\t<players>\n");
+ 
+-int
+-parse_source_port( char *port, unsigned short *low, unsigned short *high)
+-{
+-    char *dash;
+-    *low= atoi( port);
+-    dash= strchr( port, '-');
+-    *high= 0;
+-    if ( dash)
+-	*high= atoi( dash+1);
+-    if ( *high == 0)
+-	*high= *low;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-    if ( *high < *low)  {
+-	fprintf( stderr, "%s: Invalid port range\n", port);
+-	return -1;
+-    }
+-    return 0;
+-}
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		if (player->team_name)
++		{
++			fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++		}
++		else
++		{
++			fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		}
++		if (player->skin)
++		{
++			fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++		}
++		if (player->connect_time)
++		{
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 1)));
++		}
+ 
+-void
+-add_config_server_types()
+-{
+-    int n_config_types, n_builtin_types, i;
+-    server_type **config_types;
+-    server_type *new_types, *type;
+-    config_types= qsc_get_config_server_types( &n_config_types);
+-
+-    if ( n_config_types == 0)
+-	return;
+-
+-    n_builtin_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
+-    new_types= (server_type*) malloc( sizeof(server_type) * (n_builtin_types +
+-		n_config_types + 1));
+-
+-    memcpy( new_types, &builtin_types[0], n_builtin_types*sizeof(server_type));
+-    type= &new_types[n_builtin_types];
+-    for ( i= n_config_types; i; i--, config_types++, type++)
+-	*type= **config_types;
+-    n_server_types= n_builtin_types + n_config_types;
+-    new_types[n_server_types].id= Q_UNKNOWN_TYPE;
+-    if ( types != &builtin_types[0])
+-	free( types);
+-    types= new_types;
+-}
+-
+-void
+-revert_server_types()
+-{
+-    if ( types != &builtin_types[0])
+-	free( types);
+-    n_server_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
+-    types= &builtin_types[0];
+-}
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-#ifdef ENABLE_DUMP
+-unsigned pkt_dump_pos = 0;
+-const char* pkt_dumps[64] = {0};
+-static void add_pkt_from_file(const char* file)
+-{
+-	if(pkt_dump_pos >= sizeof(pkt_dumps)/sizeof(pkt_dumps[0]))
+-		return;
+-	pkt_dumps[pkt_dump_pos++] = file;
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-static void replay_pkt_dumps()
++void xml_display_doom3_player_info(struct qserver *server)
+ {
+-	struct qserver* server = servers;
+-	char* pkt = NULL;
+-	int fd;
+-	int bytes_read = 0; // should be ssize_t but for ease with win32
+-	int i;
+-	struct stat statbuf;
+-	gettimeofday( &packet_recv_time, NULL);
++	struct player *player;
++
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	for ( i = 0; i < pkt_dump_pos; i++ )
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if((fd = open(pkt_dumps[i], O_RDONLY)) == -1)
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<number>%u</number>\n", player->number);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		if (player->tribe_tag)
+ 		{
+-			goto err;
++			fprintf(OF, "\t\t\t\t<clan>%s</clan>\n", player->tribe_tag ? xml_escape(xform_name(player->tribe_tag, server)): "");
+ 		}
+-		if( fstat( fd, &statbuf) == -1 )
++		else
+ 		{
+-			goto err;
++			fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
+ 		}
+-		pkt = malloc( statbuf.st_size );
+-		if ( NULL == pkt )
++		if (player->skin)
+ 		{
+-			goto err;
++			fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
+ 		}
+-		bytes_read = read( fd, pkt, statbuf.st_size );
+-		if ( bytes_read != statbuf.st_size )
++		if (player->type_flag)
+ 		{
+-			fprintf( stderr, "Failed to read entire packet from disk got %d of %ld bytes\n", bytes_read, (long)statbuf.st_size );
+-			goto err;
++			fprintf(OF, "\t\t\t\t<type>bot</type>\n");
++		}
++		else
++		{
++			fprintf(OF, "\t\t\t\t<type>player</type>\n");
+ 		}
+ 
+-		server->type->packet_func( server, pkt, statbuf.st_size);
+-		close(fd);
+-		fd = 0;
++		if (player->connect_time)
++		{
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++		}
++
++		xml_display_player_info_info(player);
++
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+-	goto out;
+ 
+-err:
+-	perror(__FUNCTION__);
+-	close(fd);
+-out:
+-	fd = 0; // NOP
++	fprintf(OF, "\t\t</players>\n");
+ }
+-#endif // ENABLE_DUMP
+ 
+-struct rcv_pkt
++void xml_display_player_info(struct qserver *server)
+ {
+-	struct qserver* server;
+-	struct sockaddr_in addr;
+-	struct timeval recv_time;
+-	char data[PACKET_LEN];
+-	int len;
+-	int _errno;
+-};
++	struct player *player;
+ 
+-void do_work(void)
+-{
+-	int pktlen, rc, fd;
+-	char *pkt= NULL;
+-	int bind_retry= 0;
+-	struct timeval timeout;
+-	struct rcv_pkt* buffer;
+-	unsigned buffill = 0, i = 0;
+-	unsigned bufsize = max_simultaneous*2;
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	struct timeval t,ts;
+-	gettimeofday(&t, NULL);
+-	ts = t;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-	buffer = malloc(sizeof(struct rcv_pkt) * bufsize);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		if (NA_INT != player->ping)
++		{
++			fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		}
++		if (NA_INT != player->score)
++		{
++			fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++		}
++		if (NA_INT != player->deaths)
++		{
++			fprintf(OF, "\t\t\t\t<deaths>%d</deaths>\n", player->deaths);
++		}
++		if (NA_INT != player->frags)
++		{
++			fprintf(OF, "\t\t\t\t<frags>%d</frags>\n", player->frags);
++		}
++		if (player->team_name)
++		{
++			fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++		}
++		else if (NA_INT != player->team)
++		{
++			fprintf(OF, "\t\t\t\t<team>%d</team>\n", player->team);
++		}
+ 
+-	if(!buffer) return;
++		if (player->skin)
++		{
++			fprintf(OF, "\t\t\t\t<skin>%s</skin>\n", xml_escape(player->skin));
++		}
+ 
+-#ifdef ENABLE_DUMP
+-	if(pkt_dump_pos)
+-	{
+-		replay_pkt_dumps();
++		if (player->connect_time)
++		{
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 1)));
++		}
++
++		xml_display_player_info_info(player);
++
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+-	else
+-#endif
++
++	fprintf(OF, "\t\t</players>\n");
++}
++
++void xml_display_armyops_player_info(struct qserver *server)
++{
++	struct player *player;
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		bind_retry = bind_sockets();
++		player->score = calculate_armyops_score(player);
+ 	}
+ 
+-	send_packets();
++	xml_display_player_info(server);
++}
+ 
+-	debug(2, "connected: %d", connected);
++void xml_display_ts2_player_info(struct qserver *server)
++{
++	struct player *player;
++
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	while ( connected || (!connected && bind_retry==-2))
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		if ( ! connected && bind_retry==-2)
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++		if (player->connect_time)
+ 		{
+-			rc= wait_for_timeout( 60);
+-			bind_retry= bind_sockets();
+-			continue;
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 		}
+-		bind_retry= 0;
+ 
+-		set_file_descriptors();
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-		if ( progress)
+-			display_progress();
++	fprintf(OF, "\t\t</players>\n");
++}
++
++void xml_display_ts3_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-		get_next_timeout( &timeout);
++	fprintf(OF, "\t\t<players>\n");
+ 
+-		rc= wait_for_file_descriptors( &timeout);
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-		debug(2, "rc %d", rc);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
+ 
+-		if ( rc == SOCKET_ERROR)
++		if (player->connect_time)
+ 		{
+-#ifndef _WIN32
+-			if ( errno == EINTR)
+-				continue;
+-#endif
+-			perror("select");
+-			break;
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 		}
+ 
+-		fd= 0;
+-		for ( ; rc && buffill < bufsize; rc--)
+-		{
+-			int addrlen= sizeof(buffer[buffill].addr);
+-			struct qserver* server= get_next_ready_server();
+-			if ( server == NULL)
+-				break;
+-			gettimeofday( &buffer[buffill].recv_time, NULL);
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-			pktlen = recvfrom( server->fd,
+-						buffer[buffill].data,
+-						sizeof(buffer[buffill].data),
+-						0,
+-						(struct sockaddr*)&buffer[buffill].addr,
+-						(void*)&addrlen);
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-			if ( pktlen == SOCKET_ERROR)
+-			{
+-				if(errno == EAGAIN)
+-				{
+-					malformed_packet(server, "EAGAIN on UDP socket, probably incorrect checksum");
+-				}
+-				else if ( connection_refused())
+-				{
+-					server->server_name= DOWN;
+-					num_servers_down++;
+-					cleanup_qserver( server, 1);
+-				}
+-				continue;
+-			}
++void xml_display_bfbc2_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-			debug(1, "recv %3d %3d %d.%d.%d.%d:%hu\n",
+-					time_delta(&buffer[buffill].recv_time, &ts),
+-					time_delta(&buffer[buffill].recv_time, &t),
+-					server->ipaddr&0xff,
+-					(server->ipaddr>>8)&0xff,
+-					(server->ipaddr>>16)&0xff,
+-					(server->ipaddr>>24)&0xff,
+-					server->port);
++	fprintf(OF, "\t\t<players>\n");
+ 
+-			t = buffer[buffill].recv_time;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-			buffer[buffill].server = server;
+-			buffer[buffill].len = pktlen;
+-			++buffill;
+-		}
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
+ 
+-		for(i = 0; i < buffill; ++i)
++		if (player->connect_time)
+ 		{
+-			struct qserver* server = buffer[i].server;
+-			pkt = buffer[i].data;
+-			pktlen = buffer[i].len;
+-			memcpy(&packet_recv_time, &buffer[i].recv_time, sizeof(packet_recv_time));
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++		}
+ 
+-			if ( get_debug_level() > 0 )
+-				print_packet( server, pkt, pktlen);
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-#ifdef ENABLE_DUMP
+-			if (do_dump)
+-				dump_packet(pkt, pktlen);
+-#endif
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-			if ( server->flags & FLAG_BROADCAST)
+-			{
+-				struct qserver *broadcast= server;
+-				unsigned short port= ntohs(buffer[i].addr.sin_port);
+-				/* create new server and init */
+-				if ( ! (no_port_offset || server->flags & TF_NO_PORT_OFFSET))
+-				{
+-					port-= server->type->port_offset;
+-				}
+-				server= add_qserver_byaddr( ntohl(buffer[i].addr.sin_addr.s_addr), port, server->type, NULL);
+-				if ( server == NULL)
+-				{
+-					server= find_server_by_address( buffer[i].addr.sin_addr.s_addr,
+-					ntohs(buffer[i].addr.sin_port));
+-					if ( server == NULL)
+-					{
+-						continue;
+-					}
+-/*
+-					if ( show_errors)
+-					{
+-						fprintf(stderr,
+-							"duplicate or invalid packet received from 0x%08x:%hu\n",
+-							ntohl(buffer[i].addr.sin_addr.s_addr), ntohs(buffer[i].addr.sin_port));
+-						print_packet( NULL, pkt, pktlen);
+-					}
+-					continue;
+-*/
+-				}
+-				else
+-				{
+-					server->packet_time1= broadcast->packet_time1;
+-					server->packet_time2= broadcast->packet_time2;
+-					server->ping_total= broadcast->ping_total;
+-					server->n_requests= broadcast->n_requests;
+-					server->n_packets= broadcast->n_packets;
+-					broadcast->n_servers++;
+-				}
+-			}
++void xml_display_wic_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-			debug(2, "connected: %d", connected);
+-			server->type->packet_func( server, pkt, pktlen);
+-			debug(2, "connected: %d", connected);
+-		}
+-		buffill = 0;
++	fprintf(OF, "\t\t<players>\n");
++
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-		if ( run_timeout && time(0)-start_time >= run_timeout)
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
++		fprintf(OF, "\t\t\t\t<team>%s</team>\n", player->team_name);
++		fprintf(OF, "\t\t\t\t<bot>%d</bot>\n", player->type_flag );
++		if ( player->tribe_tag )
+ 		{
+-			debug(2, "run timeout reached");
+-			break;
++			fprintf(OF, "\t\t\t\t<role>%s</role>\n", player->tribe_tag );
+ 		}
+ 
+-		send_packets();
+-		if ( connected < max_simultaneous)
+-			bind_retry= bind_sockets();
+ 
+-		debug(2, "connected: %d", connected);
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
+ 	}
+ 
+-	free(buffer);
+-}
+-#ifndef _WIN32
+-void sigpipe( int sig )
+-{
+-	fprintf( stderr, "SIG: %d\n", sig );
++	fprintf(OF, "\t\t</players>\n");
+ }
+-#endif
+ 
+-int
+-main( int argc, char *argv[])
++void xml_display_ventrilo_player_info(struct qserver *server)
+ {
+-    int arg, n_files, i;
+-    char **files, *outfilename, *query_arg;
+-    struct server_arg *server_args= NULL;
+-    int n_server_args= 0, max_server_args= 0;
+-    int default_server_type_id;
++	struct player *player;
+ 
+-#ifdef _WIN32
+-    WORD version= MAKEWORD(1,1);
+-    WSADATA wsa_data;
+-    if ( WSAStartup(version,&wsa_data) != 0)  {
+-	fprintf( stderr, "Could not open winsock\n");
+-	exit(1);
+-    }
+-#else
+-	signal( SIGPIPE, &sigpipe );
+-#endif
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    types= &builtin_types[0];
+-    n_server_types= (sizeof( builtin_types) / sizeof(server_type)) - 1;
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-    i= qsc_load_default_config_files();
+-    if ( i == -1)
+-	return 1;
+-    else if ( i == 0)
+-	add_config_server_types();
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++		fprintf(OF, "\t\t\t\t<team>%s</team>\n", xml_escape(player->team_name));
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-    if ( argc == 1)
+-	usage(NULL,argv,NULL);
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-    OF= stdout;
++void xml_display_tm_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-    files= (char **) malloc( sizeof(char*) * (argc/2));
+-    n_files= 0;
+-
+-    default_server_type_id= Q_SERVER;
+-    little_endian= ((char*)&one)[0];
+-    big_endian= !little_endian;
+-
+-    for ( arg= 1; arg < argc; arg++)  {
+-	if ( argv[arg][0] != '-')
+-	    break;
+-	outfilename= NULL;
+-	if ( strcmp( argv[arg], "-nocfg") == 0 && arg == 1)  {
+-	    revert_server_types();
+-	}
+-	else if ( strcmp( argv[arg], "--help") == 0)  {
+-		usage(NULL,argv,NULL);
+-	}
+-	else if ( strcmp( argv[arg], "-f") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -f\n", argv,NULL);
+-	    files[n_files++]= argv[arg];
+-	}
+-	else if ( strcmp( argv[arg], "-retry") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -retry\n", argv,NULL);
+-	    n_retries= atoi( argv[arg]);
+-	    if ( n_retries <= 0)  {
+-		fprintf( stderr, "retries must be greater than zero\n");
+-		exit(1);
+-	    }
+-	}
+-	else if ( strcmp( argv[arg], "-interval") == 0)  {
+-	    double value= 0.0;
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -interval\n", argv,NULL);
+-	    sscanf( argv[arg], "%lf", &value);
+-	    if ( value < 0.1)  {
+-		fprintf( stderr, "retry interval must be greater than 0.1\n");
+-		exit(1);
+-	    }
+-	    retry_interval= (int)(value * 1000);
+-	}
+-	else if ( strcmp( argv[arg], "-mi") == 0)  {
+-	    double value= 0.0;
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -mi\n", argv,NULL);
+-	    sscanf( argv[arg], "%lf", &value);
+-	    if ( value < 0.1)  {
+-		fprintf( stderr, "interval must be greater than 0.1\n");
+-		exit(1);
+-	    }
+-	    master_retry_interval= (int)(value * 1000);
+-	}
+-	else if ( strcmp( argv[arg], "-H") == 0)
+-	    hostname_lookup= 1;
+-	else if ( strcmp( argv[arg], "-u") == 0)
+-	    up_servers_only= 1;
+-	else if ( strcmp( argv[arg], "-nf") == 0)
+-	    no_full_servers= 1;
+-	else if ( strcmp( argv[arg], "-ne") == 0)
+-	    no_empty_servers= 1;
+-	else if ( strcmp( argv[arg], "-nh") == 0)
+-	    no_header_display= 1;
+-	else if ( strcmp( argv[arg], "-old") == 0)
+-	    new_style= 0;
+-	else if ( strcmp( argv[arg], "-P") == 0)
+-	    get_player_info= 1;
+-	else if ( strcmp( argv[arg], "-R") == 0)
+-	    get_server_rules= 1;
+-	else if ( strncmp( argv[arg], "-raw", 4) == 0)  {
+-	    if ( argv[arg][4] == ',')
+-		{
+-			if ( strcmp( &argv[arg][5], "game") == 0)
+-			{
+-				show_game_in_raw= 1;
+-			}
+-			else
+-			{
+-				usage( "Unknown -raw option\n", argv, NULL);
+-			}
+-	    }
+-	    arg++;
+-	    if ( arg >= argc)
+-		{
+-			usage( "missing argument for -raw\n", argv,NULL);
+-		}
+-	    raw_delimiter= argv[arg];
++	fprintf(OF, "\t\t<players>\n");
+ 
+-		// Check the multi rule delimiter isnt the same
+-		// If it is fix to maintain backwards compatibility
+-		if ( 0 == strcmp( raw_delimiter, multi_delimiter ) &&
+-			0 == strcmp( raw_delimiter, "|" )
+-		)
+-		{
+-			multi_delimiter = ":";
+-		}
+-	    raw_display= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-mdelim" ) == 0 )
++	player = server->players;
++	for (; player != NULL; player = player->next)
+ 	{
+-		arg++;
+-		if ( arg >= argc)
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<ping>%d</ping>\n", player->ping);
++
++		if (player->connect_time)
+ 		{
+-			usage( "missing argument for -mdelim\n", argv,NULL);
+-		}
+-		multi_delimiter = argv[arg];
+-	}
+-	else if ( strcmp( argv[arg], "-xml") == 0) {
+-	    xml_display= 1;
+-	    if (raw_display == 1)
+-		usage( "cannot specify both -raw and -xml\n", argv,NULL);
+-	}
+-	else if ( strcmp( argv[arg], "-utf8") == 0) {
+-	    xml_encoding= ENCODING_UTF_8;
+-	    name_xforms= 0;
+-	}
+-	else if ( strcmp( argv[arg], "-ncn") == 0)  {
+-	    color_names= 0;
+- 	}
+-	else if ( strcmp( argv[arg], "-cn") == 0)  {
+-	    color_names= 1;
+- 	}
+-	else if ( strcmp( argv[arg], "-hc") == 0)  {
+-	    color_names= 2;
+- 	}
+-	else if ( strcmp( argv[arg], "-nx") == 0)  {
+-	    name_xforms= 1;
+- 	}
+-	else if ( strcmp( argv[arg], "-nnx") == 0)  {
+-	    name_xforms= 0;
+- 	}
+-	else if ( strcmp( argv[arg], "-tc") == 0)  {
+-	    time_format= CLOCK_TIME;
+-	}
+-	else if ( strcmp( argv[arg], "-tsw") == 0)  {
+-	    time_format= STOPWATCH_TIME;
+-	}
+-	else if ( strcmp( argv[arg], "-ts") == 0)  {
+-	    time_format= SECONDS;
+-	}
+-	else if ( strcmp( argv[arg], "-pa") == 0)  {
+-	    player_address= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-hpn") == 0)  {
+-	    hex_player_names= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-hsn") == 0)  {
+-	    hex_server_names= 1;
+-	}
+-	else if ( strncmp( argv[arg], "-maxsimultaneous", 7) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -maxsimultaneous\n", argv,NULL);
+-	    max_simultaneous= atoi(argv[arg]);
+-	    if ( max_simultaneous <= 0)
+-		usage( "value for -maxsimultaneous must be > 0\n", argv,NULL);
+-	    if ( max_simultaneous > FD_SETSIZE)
+-		max_simultaneous= FD_SETSIZE;
+- 	}
+-	else if ( strcmp( argv[arg], "-sendinterval") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -sendinterval\n", argv,NULL);
+-	    sendinterval= atoi(argv[arg]);
+-	    if ( sendinterval < 0)
+-		usage( "value for -sendinterval must be >= 0\n", argv,NULL);
+- 	}
+-	else if ( strcmp( argv[arg], "-raw-arg") == 0)  {
+-	    raw_arg= 1000;
+- 	}
+-	else if ( strcmp( argv[arg], "-timeout") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -timeout\n", argv,NULL);
+-	    run_timeout= atoi( argv[arg]);
+-	    if ( run_timeout <= 0)
+-		usage( "value for -timeout must be > 0\n", argv,NULL);
+-	}
+-	else if ( strncmp(argv[arg], "-progress", sizeof("-progress")-1) == 0) {
+-	    char *p= argv[arg] + sizeof("-progress")-1;
+-	    progress= 1;
+-	    if ( *p == ',')
+-		progress= atoi(p+1);
+- 	}
+-	else if ( strcmp( argv[arg], "-Hcache") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -Hcache\n", argv,NULL);
+-	    if ( hcache_open( argv[arg], 0) == -1)
+-		return 1;
+-	}
+-	else if ( strcmp( argv[arg], "-default") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -default\n", argv,NULL);
+-	    default_server_type= find_server_type_string( argv[arg]);
+-	    if ( default_server_type == NULL)  {
+-		char opt[256], *o= &opt[0];
+-		sprintf( opt, "-%s", argv[arg]);
+-		for ( ; *o; o++) *o= tolower(*o);
+-		default_server_type= find_server_type_option( opt);
+-	    }
+-	    if ( default_server_type == NULL)  {
+-		fprintf( stderr, "unknown server type \"%s\"\n", argv[arg]);
+-		usage( NULL, argv,NULL);
+-	    }
+-	    default_server_type_id= default_server_type->id;
+-	    default_server_type= NULL;
+-	}
+-	else if ( strncmp( argv[arg], "-Tserver", 3) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( read_qserver_template( argv[arg]) == -1)
+-		return 1;
+-	}
+-	else if ( strncmp( argv[arg], "-Trule", 3) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( read_rule_template( argv[arg]) == -1)
+-		return 1;
+-	}
+-	else if ( strncmp( argv[arg], "-Theader", 3) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( read_header_template( argv[arg]) == -1)
+-		return 1;
+-	}
+-	else if ( strncmp( argv[arg], "-Ttrailer", 3) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( read_trailer_template( argv[arg]) == -1)
+-		return 1;
+-	}
+-	else if ( strncmp( argv[arg], "-Tplayer", 3) == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( read_player_template( argv[arg]) == -1)
+-		return 1;
+-	}
+-	else if ( strcmp( argv[arg], "-sort") == 0)  {
+-		size_t pos;
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for -sort\n", argv, NULL);
+-	    strncpy( sort_keys, argv[arg], sizeof(sort_keys)-1);
+-	    pos= strspn( sort_keys, SUPPORTED_SORT_KEYS);
+-	    if ( pos != strlen( sort_keys))  {
+-		fprintf( stderr, "Unknown sort key \"%c\", valid keys are \"%s\"\n",
+-			sort_keys[pos], SUPPORTED_SORT_KEYS);
+-		return 1;
+-	    }
+-	    server_sort= strpbrk( sort_keys, SUPPORTED_SERVER_SORT) != NULL;
+-	    if ( strchr( sort_keys, 'l'))
+-		server_sort= 1;
+-	    player_sort= strpbrk( sort_keys, SUPPORTED_PLAYER_SORT) != NULL;
+-	}
+-	else if ( strcmp( argv[arg], "-errors") == 0)  {
+-	    show_errors++;
+-	}
+-	else if ( strcmp( argv[arg], "-of") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( argv[arg][0] == '-' && argv[arg][1] == '\0')
+-		OF= stdout;
+-	    else
+-		OF= fopen( argv[arg], "w");
+-	    if ( OF == NULL)  {
+-		perror( argv[arg]);
+-		return 1;
+-	    }
+-	}
+-	else if ( strcmp( argv[arg], "-af") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( argv[arg][0] == '-' && argv[arg][1] == '\0')
+-		OF= stdout;
+-	    else
+-		OF= fopen( argv[arg], "a");
+-	    if ( OF == NULL)  {
+-		perror( argv[arg]);
+-		return 1;
+-	    }
+-	}
+-	else if ( strcmp( argv[arg], "-htmlnames") == 0)  {
+-	    html_names= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-nohtmlnames") == 0)  {
+-	    html_names= 0;
+-	}
+-	else if ( strcmp( argv[arg], "-htmlmode") == 0)  {
+-	    html_mode= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-carets") == 0)  {
+-	    strip_carets= 0;
+-	}
+-	else if ( strcmp( argv[arg], "-d") == 0)  {
+-	    set_debug_level(get_debug_level() + 1);
+-	}
+-	else if ( strcmp( argv[arg], "-showgameport") == 0)  {
+-	    show_game_port= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-noportoffset") == 0)  {
+-	    no_port_offset= 1;
+-	}
+-	else if ( strcmp( argv[arg], "-srcip") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( parse_source_address( argv[arg], &source_ip, &source_port) == -1)
+-		return 1;
+-	    if ( source_port)  {
+-		source_port_low= source_port;
+-		source_port_high= source_port;
+-	    }
+-	}
+-	else if ( strcmp( argv[arg], "-srcport") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( parse_source_port( argv[arg], &source_port_low, &source_port_high) == -1)
+-		return 1;
+-	    source_port= source_port_low;
+-	}
+-	else if ( strcmp( argv[arg], "-cfg") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    if ( qsc_load_config_file( argv[arg]) == -1)
+-		return 1;
+-	    add_config_server_types();
+-	}
+-	else if ( strcmp( argv[arg], "-allowserverdups") == 0)  {
+-	    noserverdups = 0;
+-	}
+-#ifdef ENABLE_DUMP
+-	else if ( strcmp( argv[arg], "-dump") == 0)  {
+-	    do_dump = 1;
+-	}
+-	else if ( strcmp( argv[arg], "-pkt") == 0)  {
+-	    arg++;
+-	    if ( arg >= argc)
+-		usage( "missing argument for %s\n", argv, argv[arg-1]);
+-	    add_pkt_from_file(argv[arg]);
+-	}
+-#endif
+-#ifdef _WIN32
+-	else if ( strcmp( argv[arg], "-noconsole") == 0)  {
+-	    FreeConsole();
+-	}
+-#endif
+-	else  {
+-	    int outfile;
+-	    server_type *type;
+-	    arg++;
+-	    if ( arg >= argc)  {
+-		fprintf( stderr, "missing argument for \"%s\"\n", argv[arg-1]);
+-		return 1;
+-	    }
+-	    type= parse_server_type_option( argv[arg-1], &outfile, &query_arg);
+-	    if ( type == NULL)  {
+-		fprintf( stderr, "unknown option \"%s\"\n", argv[arg-1]);
+-		return 1;
+-	    }
+-	    outfilename= NULL;
+-	    if ( outfile)  {
+-		outfilename= strchr( argv[arg], ',');
+-		if ( outfilename == NULL)  {
+-		    fprintf( stderr, "missing file name for \"%s,outfile\"\n",
+-			argv[arg-1]);
+-		    return 1;
++			fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 		}
+-		*outfilename++= '\0';
+-	    }
+-/*
+-	    if ( query_arg && !(type->flags & TF_QUERY_ARG))  {
+-		fprintf( stderr, "option flag \"%s\" not allowed for this server type\n",
+-			query_arg);
+-		return 1;
+-	    }
+-*/
+-	    if ( type->flags & TF_QUERY_ARG_REQUIRED && !query_arg)  {
+-		fprintf( stderr, "option flag missing for server type \"%s\"\n",
+-			argv[arg-1]);
+-		return 1;
+-	    }
+-	    add_server_arg( argv[arg], type->id, outfilename, query_arg,
+-		&server_args, &n_server_args, &max_server_args);
+-	}
+-	}
+ 
+-	start_time= time(0);
+-
+-	default_server_type= find_server_type_id( default_server_type_id);
++		xml_display_player_info_info(player);
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-	for ( i= 0; i < n_files; i++)
+-	add_file( files[i]);
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-	for ( ; arg < argc; arg++)
+-	add_qserver( argv[arg], default_server_type, NULL, NULL);
+ 
+-	for ( i= 0; i < n_server_args; i++)  {
+-	server_type *server_type= find_server_type_id( server_args[i].type_id);
+-	add_qserver( server_args[i].arg, server_type,
+-		server_args[i].outfilename, server_args[i].query_arg);
+-	}
++void xml_display_savage_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-	free( server_args);
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	if ( servers == NULL)
+-		exit(1);
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-	max_connmap= max_simultaneous + 10;
+-	connmap= (struct qserver**) calloc( 1, sizeof(struct qserver*) * max_connmap);
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%s</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-	if ( color_names == -1)
+-		color_names= ( raw_display) ? DEFAULT_COLOR_NAMES_RAW :
+-		DEFAULT_COLOR_NAMES_DISPLAY;
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-	if ( time_format == -1)
+-		time_format= ( raw_display) ? DEFAULT_TIME_FMT_RAW :
+-		DEFAULT_TIME_FMT_DISPLAY;
++	fprintf(OF, "\t\t</players>\n");
++}
+ 
+-	if ( (one_server_type_id & MASTER_SERVER) || one_server_type_id == 0)
+-		display_prefix= 1;
++void xml_display_farcry_player_info(struct qserver *server)
++{
++	struct player *player;
+ 
+-	if ( xml_display)
+-		xml_header();
+-	else if ( new_style && ! raw_display && ! have_server_template())
+-		display_header();
+-	else if ( have_header_template())
+-		template_display_header();
++	fprintf(OF, "\t\t<players>\n");
+ 
+-	q_serverinfo.length= htons( q_serverinfo.length);
+-	h2_serverinfo.length= htons( h2_serverinfo.length);
+-	q_player.length= htons( q_player.length);
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
+ 
+-	do_work();
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->frags);
++		fprintf(OF, "\t\t\t\t<time>%su</time>\n", xml_escape(play_time(player->connect_time, 2)));
+ 
+-	finish_output();
+-	free_server_hash();
+-	free(files);
+-	free(connmap);
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-	return 0;
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+-void
+-finish_output()
++void xml_display_tee_player_info(struct qserver *server)
+ {
+-    int i;
+-    hcache_update_file();
++	struct player *player;
+ 
+-    if ( progress)  {
+-	display_progress();
+-	fputs( "\n", stderr);
+-    }
++	fprintf(OF, "\t\t<players>\n");
+ 
+-    if ( server_sort)  {
+-	struct qserver **array, *server, *next_server;
+-	if ( strchr( sort_keys, 'l') &&
+-		strpbrk( sort_keys, SUPPORTED_SERVER_SORT) == NULL)  {
+-	    server= servers;
+-	    for ( ; server; server= next_server)  {
+-		next_server= server->next;
+-		display_server( server);
+-	    }
+-	}
+-	else  {
+-	    array= (struct qserver **) malloc( sizeof(struct qserver *) *
+-		    num_servers_total);
+-	    server= servers;
+-	    for ( i= 0; server != NULL; i++)  {
+-		array[i]= server;
+-		server= server->next;
+-	    }
+-	    sort_servers( array, num_servers_total);
+-	    if ( progress)
+-	    fprintf( stderr, "\n");
+-	    for ( i= 0; i < num_servers_total; i++)
+-		display_server( array[i]);
+-	    free( array);
+-	}
+-    }
+-    else  {
+-	struct qserver *server, *next_server;
+-	server= servers;
+-	for ( ; server; server= next_server)  {
+-	    next_server= server->next;
+-	    if ( server->server_name == HOSTNOTFOUND)
+-		display_server( server);
+-	}
+-    }
++	player = server->players;
++	for (; player != NULL; player = player->next)
++	{
++		fprintf(OF, "\t\t\t<player>\n");
++
++		fprintf(OF, "\t\t\t\t<name>%s</name>\n", xml_escape(xform_name(player->name, server)));
++		fprintf(OF, "\t\t\t\t<score>%d</score>\n", player->score);
+ 
+-    if ( xml_display)
+-	xml_footer();
+-    else if ( have_trailer_template())
+-	template_display_trailer();
++		fprintf(OF, "\t\t\t</player>\n");
++	}
+ 
+-    if ( OF != stdout)
+-	fclose( OF);
++	fprintf(OF, "\t\t</players>\n");
+ }
+ 
+ 
+-void
+-add_file( char *filename)
++void display_progress()
+ {
+-	FILE *file;
+-	char name[200], *comma, *query_arg = NULL;
+-	server_type* type;
+-
+-	if ( strcmp( filename, "-") == 0)
++	static struct timeval rate_start =
+ 	{
+-		file= stdin;
+-		current_filename= NULL;
+-	}
+-	else
+-	{
+-		file= fopen( filename, "r");
+-		current_filename= filename;
+-	}
+-	current_fileline= 1;
++		0, 0
++	};
++	char rate[32];
++	struct timeval now;
++
++	gettimeofday(&now, NULL);
+ 
+-	if ( file == NULL)
++	if (!rate_start.tv_sec)
+ 	{
+-		perror( filename);
+-		return;
++		rate_start = now;
++		rate[0] = '\0';
+ 	}
+-	for ( ; fscanf( file, "%s", name) == 1; current_fileline++)
++	else
+ 	{
+-		comma= strchr( name, ',');
+-		if ( comma)
+-		{
+-			*comma++= '\0';
+-			query_arg= strdup( comma);
+-		}
+-		type = find_server_type_string( name);
+-		if ( type == NULL)
++		int delta = time_delta(&now, &rate_start);
++		if (delta > 1500)
+ 		{
+-			add_qserver( name, default_server_type, NULL, NULL);
++			sprintf(rate, "  %d servers/sec  ", (num_servers_returned + num_servers_timed_out) *1000 / delta);
+ 		}
+-		else if ( fscanf( file, "%s", name) == 1)
++		else
+ 		{
+-			if ( type->flags & TF_QUERY_ARG && comma && *query_arg)
+-			{
+-				add_qserver( name, type, NULL, query_arg );
+-			}
+-			else
+-			{
+-				add_qserver( name, type, NULL, NULL );
+-			}
++			rate[0] = '\0';
+ 		}
+ 	}
+ 
+-	if ( file != stdin)
+-	fclose(file);
+-
+-	current_fileline= 0;
++	// only print out every 'progress' number of servers.
++	if (0 != num_servers_returned + num_servers_timed_out && (progress == 1 || (num_servers_returned + num_servers_timed_out) %	progress == 0))
++	{
++		fprintf(stderr, "\r%d/%d (%d timed out, %d down)%s", num_servers_returned + num_servers_timed_out, num_servers_total, num_servers_timed_out, num_servers_down, rate);
++	}
+ }
+ 
+-void
+-print_file_location()
++/* ----- END MODIFICATION ----- Don't need to change anything below here. */
++
++
++void set_non_blocking(int fd);
++int set_fds(fd_set *fds);
++void get_next_timeout(struct timeval *timeout);
++
++void set_file_descriptors();
++int wait_for_file_descriptors(struct timeval *timeout);
++struct qserver *get_next_ready_server();
++
++
++/* Misc flags
++ */
++
++struct timeval packet_recv_time;
++int one_server_type_id = ~MASTER_SERVER;
++static int one = 1;
++static int little_endian;
++static int big_endian;
++unsigned int swap_long(void*);
++unsigned short swap_short(void*);
++float swap_float_from_little(void *f);
++char *strndup(const char *string, size_t len);
++#define FORCE 1
++
++/* Print an error message and the program usage notes
++ */
++
++void usage(char *msg, char **argv, char *a1)
+ {
+-    if ( current_fileline != 0)
+-	fprintf( stderr, "%s:%d: ", current_filename?current_filename:"<stdin>",
+-		current_fileline);
++	int i;
++	server_type *type;
++	server_type **sorted_types;
++
++	if (msg)
++	{
++		fprintf(stderr, msg, a1);
++	}
++
++	printf("Usage: %s [options ...]\n", argv[0]);
++	printf("\t[-default server-type] [-cfg file] [-f file] [host[:port]] ...\n");
++	printf("Where host is an IP address or host name\n");
++
++	sorted_types = (server_type **)malloc(sizeof(server_type*) * n_server_types);
++	type = &types[0];
++	for (i = 0; type->id != Q_UNKNOWN_TYPE; type++, i++)
++	{
++		sorted_types[i] = type;
++	}
++
++	quicksort((void **)sorted_types, 0, n_server_types - 1, (int(*)(void *, void*))type_option_compare);
++
++
++	for (i = 0; i < n_server_types; i++)
++	{
++		type = sorted_types[i];
++		printf("%s\t\tquery %s server\n", type->type_option, type->game_name);
++	}
++
++	quicksort((void **)sorted_types, 0, n_server_types - 1, (int(*)(void *, void*))type_string_compare);
++	printf("-default\tset default server type:");
++	for (i = 0; i < n_server_types; type++, i++)
++	{
++		type = sorted_types[i];
++		printf(" %s", type->type_string);
++	}
++	puts("");
++	printf("-nocfg\t\tIgnore qstat configuration loaded from any default location. Must be the first option on the command-line.\n");
++	printf("-cfg\t\tread the extended types from given file not the default one\n");
++	printf("-f\t\tread hosts from file\n");
++	printf("-R\t\tfetch and display server rules\n");
++	printf("-P\t\tfetch and display player info\n");
++	printf("-sort\t\tsort servers and/or players\n");
++	printf("-u\t\tonly display servers that are up\n");
++	printf("-nf\t\tdo not display full servers\n");
++	printf("-ne\t\tdo not display empty servers\n");
++	printf("-nh\t\tdo not display header line.\n");
++	printf("-cn\t\tdisplay color names instead of numbers\n");
++	printf("-ncn\t\tdisplay color numbers instead of names\n");
++	printf("-hc\t\tdisplay colors in #rrggbb format\n");
++	printf("-tc\t\tdisplay time in clock format (DhDDmDDs)\n");
++	printf("-tsw\t\tdisplay time in stop-watch format (DD:DD:DD)\n");
++	printf("-ts\t\tdisplay time in seconds\n");
++	printf("-pa\t\tdisplay player address\n");
++	printf("-hpn\t\tdisplay player names in hex\n");
++	printf("-hsn\t\tdisplay server names in hex\n");
++	printf("-nh\t\tdo not display header\n");
++	printf("-old\t\told style display\n");
++	printf("-progress\tdisplay progress meter (text only)\n");
++	printf("-retry\t\tnumber of retries, default is %d\n", DEFAULT_RETRIES);
++	printf("-interval\tinterval between retries, default is %.2f seconds\n", DEFAULT_RETRY_INTERVAL / 1000.0);
++	printf("-mi\t\tinterval between master server retries, default is %.2f seconds\n", (DEFAULT_RETRY_INTERVAL *4) / 1000.0);
++	printf("-timeout\ttotal time in seconds before giving up\n");
++	printf("-maxsim\t\tset maximum simultaneous queries\n");
++	printf("-sendinterval\t\tset time in ms between sending packets, default %u\n", sendinterval);
++	printf("-errors\t\tdisplay errors\n");
++	printf("-allowserverdups\t\tallow adding multiple servers with same ip:port (needed for ts2)\n");
++	printf("-of\t\toutput file\n");
++	printf("-af\t\tLike -of, but append to the file\n");
++	printf("-raw <delim>\toutput in raw format using <delim> as delimiter\n");
++	printf("-mdelim <delim>\tFor rules with multi values use <delim> as delimiter\n");
++	printf("-xml\t\toutput status data as an XML document\n");
++	printf("-Th,-Ts,-Tpt\toutput templates: header, server and player\n");
++	printf("-Tr,-Tt\t\toutput templates: rule, and trailer\n");
++	printf("-srcport <range>\tSend packets from these network ports\n");
++	printf("-srcip <IP>\tSend packets using this IP address\n");
++	printf("-H\t\tresolve host names\n");
++	printf("-Hcache\t\thost name cache file\n");
++	printf("-carets\t\tDisplay carets in Quake 3 player names\n");
++	printf("-d\t\tEnable debug options. Specify multiple times to increase debug level.\n");
++#ifdef ENABLE_DUMP
++	printf("-dump\t\twrite received raw packets to dumpNNN files which must not exist before\n");
++	printf("-pkt <file>\tuse file as server reply instead of quering the server. Works only with TF_SINGLE_QUERY servers\n");
++#endif
++	printf("-htmlmode\tConvert <, >, and & to the equivalent HTML entities\n");
++	printf("-htmlnames\tColorize Quake 3 and Tribes 2 player names using html font tags\n");
++	printf("-nohtmlnames\tDo not colorize Quake 3 and Tribes 2 player names even if $HTML is used in an output template.\n");
++	printf("-syncconnect\tProcess connect initialisation synchronously.\n");
++	printf("-stripunprintable\tDisable stripping of unprintable characters.\n");
++	printf("-showgameport\tAlways display the game port in QStat output.\n");
++	printf("-noportoffset\tDont use builtin status port offsets ( assume query port was specified ).\n");
++	printf("-raw-arg\tWhen used with -raw, always display the server address as it appeared in a file or on the command-line.\n");
++	printf("-utf8\t\tUse the UTF-8 character encoding for XML output.\n");
++	printf("-bom\t\tOutput Byte-Order-Mark for XML output.\n");
++#ifdef _WIN32
++	printf("-noconsole\t\tFree the console\n");
++#endif
++	printf("\n");
++	printf("Sort keys:\n");
++	printf("  servers: p=by-ping, g=by-game, i=by-IP-address, h=by-hostname, n=by-#-players, l=by-list-order\n");
++	printf("  players: P=by-ping, F=by-frags, T=by-team, N=by-name\n");
++	printf("\nqstat version %s\n", VERSION);
++	exit(0);
+ }
+ 
+-void
+-parse_query_params( struct qserver *server, char *params)
++struct server_arg
++{
++	int type_id;
++	server_type *type;
++	char *arg;
++	char *outfilename;
++	char *query_arg;
++};
++
++server_type *find_server_type_id(int type_id)
+ {
+-    char *comma, *arg= params;
+-    do  {
+-	comma= strchr(arg,',');
+-	if ( comma)
+-	    *comma= '\0';
+-	if ( strchr( arg, '='))
+-	    add_query_param( server, arg);
+-	else if ( strcmp( arg, "noportoffset") == 0 || strcmp( arg, "qp") == 0)
+-	    server->flags |= TF_NO_PORT_OFFSET;
+-	else if ( strcmp( arg, "showgameport") == 0 || strcmp( arg, "gp") == 0)
+-	    server->flags |= TF_SHOW_GAME_PORT;
+-	arg= comma+1;
+-    } while ( comma);
++	server_type *type = &types[0];
++	for (; type->id != Q_UNKNOWN_TYPE; type++)
++	if (type->id == type_id)
++	{
++		return type;
++	}
++	return NULL;
+ }
+ 
+-int
+-add_qserver( char *arg, server_type* type, char *outfilename, char *query_arg)
++server_type *find_server_type_string(char *type_string)
+ {
+-	struct qserver *server, *prev_server;
+-	int flags= 0;
+-	char *colon= NULL, *arg_copy, *hostname= NULL;
+-	unsigned int ipaddr;
+-	unsigned short port, port_max;
+-	int portrange = 0;
+-	unsigned colonpos = 0;
+-
+-	if ( run_timeout && time(0)-start_time >= run_timeout)
++	server_type *type = &types[0];
++	char *t = type_string;
++	for (; *t; t++)
+ 	{
+-		finish_output();
+-		exit(0);
++		*t = tolower(*t);
+ 	}
+ 
+-	port = port_max = type->default_port;
++	for (; type->id != Q_UNKNOWN_TYPE; type++)
++	if (strcmp(type->type_string, type_string) == 0)
++	{
++		return type;
++	}
++	return NULL;
++}
+ 
+-	if ( outfilename && strcmp( outfilename, "-") != 0)
++server_type *find_server_type_option(char *option)
++{
++	server_type *type = &types[0];
++	for (; type->id != Q_UNKNOWN_TYPE; type++)
++	if (strcmp(type->type_option, option) == 0)
+ 	{
+-		FILE *outfile= fopen( outfilename, "r+");
+-		if ( outfile == NULL && (errno == EACCES || errno == EISDIR ||
+-			errno == ENOSPC || errno == ENOTDIR))
+-		{
+-			perror( outfilename);
+-			return -1;
+-		}
+-		if ( outfile)
+-		{
+-			fclose(outfile);
+-		}
+-    }
++		return type;
++	}
++	return NULL;
++}
+ 
+-    arg_copy= strdup(arg);
++server_type *parse_server_type_option(char *option, int *outfile, char **query_arg)
++{
++	server_type *type = &types[0];
++	char *comma, *arg;
++	int len;
+ 
+-    colon= strchr( arg, ':');
+-    if ( colon != NULL)
+-    {
+-		if(sscanf( colon+1, "%hu-%hu", &port, &port_max) == 2)
+-		{
+-			portrange = 1;
+-		}
+-		else
+-		{
+-			port_max = port;
+-		}
+-		*colon= '\0';
+-		colonpos = colon-arg;
+-    }
++	*outfile = 0;
++	*query_arg = 0;
+ 
+-    if ( *arg == '+')
+-    {
+-		flags|= FLAG_BROADCAST;
+-		arg++;
+-    }
++	comma = strchr(option, ',');
++	if (comma)
++	{
++		*comma++ = '\0';
++	}
+ 
+-    ipaddr= inet_addr(arg);
+-    if ( ipaddr == INADDR_NONE)
+-    {
+-		if ( strcmp( arg, "255.255.255.255") != 0)
+-		{
+-			ipaddr= htonl( hcache_lookup_hostname(arg));
+-		}
+-    }
+-    else if ( hostname_lookup && !(flags&FLAG_BROADCAST))
+-    {
+-		hostname= hcache_lookup_ipaddr( ntohl(ipaddr));
++	for (; type->id != Q_UNKNOWN_TYPE; type++)
++	if (strcmp(type->type_option, option) == 0)
++	{
++		break;
+ 	}
+ 
+-    if ( (ipaddr == INADDR_NONE || ipaddr == 0) && strcmp( arg, "255.255.255.255") != 0)
++	if (type->id == Q_UNKNOWN_TYPE)
+ 	{
+-		print_file_location();
+-		fprintf( stderr, "%s: %s\n", arg, strherror(h_errno));
+-		server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+-		for(;port <= port_max; ++port)
+-		{
+-			init_qserver( server, type);
+-			if(portrange)
+-			{
+-				server->arg = ( port==port_max ) ? arg_copy : strdup(arg_copy);
+-			}
+-			if(portrange)
+-			{
+-				sprintf(server->arg+colonpos+1, "%hu", port);
+-			}
+-			server->server_name= HOSTNOTFOUND;
+-			server->error= strdup( strherror(h_errno));
+-			server->query_port = server->port= port;
+-			if ( last_server != &servers)
+-			{
+-				prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+-				server->prev= prev_server;
+-			}
+-			*last_server= server;
+-			last_server= & server->next;
+-			if ( one_server_type_id == ~MASTER_SERVER)
+-			{
+-				one_server_type_id= type->id;
+-			}
+-			else if ( one_server_type_id != type->id)
+-			{
+-				one_server_type_id= 0;
+-			}
+-		}
+-		return -1;
++		return NULL;
+ 	}
+ 
+-	for(; port > 0 && port <= port_max; ++port)
++	if (!comma)
+ 	{
+-		if ( noserverdups && find_server_by_address( ipaddr, port) != NULL)
++		return type;
++	}
++
++	if (strcmp(comma, "outfile") == 0)
++	{
++		*outfile = 1;
++		comma = strchr(comma, ',');
++		if (!comma)
+ 		{
+-			continue;
++			return type;
+ 		}
++		*comma++ = '\0';
++	}
+ 
+-		server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+-		server->arg= port==port_max?arg_copy:strdup(arg_copy);
+-		if(portrange)
++	*query_arg = strdup(comma);
++	arg = comma;
++	do
++	{
++		comma = strchr(arg, ',');
++		if (comma)
+ 		{
+-			sprintf(server->arg+colonpos+1, "%hu", port);
++			len = comma - arg;
+ 		}
+-		if ( hostname && colon)
++		else
+ 		{
+-			server->host_name= (char*)malloc( strlen(hostname) + 5 +2);
+-			sprintf( server->host_name, "%s:%hu", hostname, port);
++			len = strlen(arg);
+ 		}
+-		else
++		if (strncmp(arg, "outfile", len) == 0)
+ 		{
+-			server->host_name= strdup((hostname)?hostname:arg);
++			*outfile = 1;
+ 		}
++		arg = comma + 1;
++	}
++	while (comma);
++	return type;
++}
+ 
+-		server->ipaddr= ipaddr;
+-		server->query_port = server->port = port;
+-		server->type= type;
+-		server->outfilename= outfilename;
+-		server->flags= flags;
+-		if (query_arg)
++void add_server_arg(char *arg, int type, char *outfilename, char *query_arg, struct server_arg **args, int *n, int *max)
++{
++	if (*n == *max)
++	{
++		if (*max == 0)
+ 		{
+-			server->query_arg = ( port == port_max ) ? query_arg : strdup(query_arg);
+-			parse_query_params( server, server->query_arg);
++			*max = 4;
++			*args = (struct server_arg*)malloc(sizeof(struct server_arg)*(*max));
+ 		}
+ 		else
+ 		{
+-			server->query_arg = NULL;
++			(*max) *= 2;
++			*args = (struct server_arg*)realloc(*args, sizeof(struct server_arg)*(*max));
+ 		}
+-		init_qserver( server, type);
++	}
++	(*args)[ *n].type_id = type;
++	/*    (*args)[*n].type= find_server_type_id( type); */
++	(*args)[ *n].type = NULL;
++	(*args)[ *n].arg = arg;
++	(*args)[ *n].outfilename = outfilename;
++	(*args)[ *n].query_arg = query_arg;
++	(*n)++;
++}
+ 
+-		if ( server->type->master)
+-		{
+-			waiting_for_masters++;
+-		}
+ 
+-		if ( num_servers_total % 10 == 0)
+-		{
+-			hcache_update_file();
+-		}
+-
+-		if ( last_server != &servers )
+-		{
+-			prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+-			server->prev= prev_server;
+-		}
+-		*last_server= server;
+-		last_server= &server->next;
++void add_query_param(struct qserver *server, char *arg)
++{
++	char *equal;
++	struct query_param *param;
+ 
+-		add_server_to_hash( server );
++	equal = strchr(arg, '=');
++	*equal++ = '\0';
+ 
+-		if ( one_server_type_id == ~MASTER_SERVER)
+-		{
+-			one_server_type_id= type->id;
+-		}
+-		else if ( one_server_type_id != type->id)
+-		{
+-			one_server_type_id= 0;
+-		}
++	param = (struct query_param*)malloc(sizeof(struct query_param));
++	param->key = arg;
++	param->value = equal;
++	sscanf(equal, "%i", &param->i_value);
++	sscanf(equal, "%i", &param->ui_value);
++	param->next = server->params;
++	server->params = param;
++}
+ 
+-		++num_servers;
+-	}
+-	return 0;
++char *get_param_value(struct qserver *server, const char *key, char *default_value)
++{
++	struct query_param *p = server->params;
++	for (; p; p = p->next)
++	if (strcasecmp(key, p->key) == 0)
++	{
++		return p->value;
++	} return default_value;
+ }
+ 
+-struct qserver *
+-add_qserver_byaddr( unsigned int ipaddr, unsigned short port,
+-	server_type* type, int *new_server)
++int get_param_i_value(struct qserver *server, char *key, int default_value)
+ {
+-	char arg[36];
+-	struct qserver *server, *prev_server;
+-	char *hostname= NULL;
++	struct query_param *p = server->params;
++	for (; p; p = p->next)
++	if (strcasecmp(key, p->key) == 0)
++	{
++		return p->i_value;
++	} return default_value;
++}
+ 
+-	if ( run_timeout && time(0)-start_time >= run_timeout)
++unsigned int get_param_ui_value(struct qserver *server, char *key, unsigned int default_value)
++{
++	struct query_param *p = server->params;
++	for (; p; p = p->next)
++	if (strcasecmp(key, p->key) == 0)
+ 	{
+-		finish_output();
+-		exit(0);
+-	}
++		return p->ui_value;
++	} return default_value;
++}
+ 
+-	if ( new_server)
++int parse_source_address(char *addr, unsigned int *ip, unsigned short *port)
++{
++	char *colon;
++	colon = strchr(addr, ':');
++	if (colon)
+ 	{
+-		*new_server= 0;
++		*colon = '\0';
++		*port = atoi(colon + 1);
++		if (colon == addr)
++		{
++			return 0;
++		}
+ 	}
+-	ipaddr= htonl(ipaddr);
+-	if ( ipaddr == 0)
++	else
+ 	{
+-		return 0;
++		*port = 0;
+ 	}
+ 
+-	// TODO: this prevents servers with the same ip:port being queried
+-	// and hence breaks virtual servers support e.g. Teamspeak 2
+-	if ( find_server_by_address( ipaddr, port) != NULL)
++	*ip = inet_addr(addr);
++	if (*ip == INADDR_NONE && !isdigit((unsigned char) *ip))
+ 	{
+-		return 0;
++		*ip = hcache_lookup_hostname(addr);
+ 	}
+-
+-	if ( new_server)
++	if (*ip == INADDR_NONE)
+ 	{
+-		*new_server= 1;
++		fprintf(stderr, "%s: Not an IP address or unknown host name\n", addr);
++		return -1;
+ 	}
++	*ip = ntohl(*ip);
++	return 0;
++}
+ 
+-	server= (struct qserver *) calloc( 1, sizeof( struct qserver));
+-	server->ipaddr= ipaddr;
+-	ipaddr= ntohl(ipaddr);
+-	sprintf( arg, "%d.%d.%d.%d:%hu", ipaddr>>24, (ipaddr>>16)&0xff,
+-	(ipaddr>>8)&0xff, ipaddr&0xff, port);
+-	server->arg= strdup(arg);
+-
+-	if ( hostname_lookup)
++int parse_source_port(char *port, unsigned short *low, unsigned short *high)
++{
++	char *dash;
++	*low = atoi(port);
++	dash = strchr(port, '-');
++	*high = 0;
++	if (dash)
+ 	{
+-		hostname= hcache_lookup_ipaddr( ipaddr);
++		*high = atoi(dash + 1);
+ 	}
+-
+-	if ( hostname)
++	if (*high == 0)
+ 	{
+-		server->host_name= (char*)malloc( strlen(hostname) + 6 + 2);
+-		sprintf( server->host_name, "%s:%hu", hostname, port);
++		*high = *low;
+ 	}
+-	else
++
++	if (*high < *low)
+ 	{
+-		server->host_name= strdup( arg);
++		fprintf(stderr, "%s: Invalid port range\n", port);
++		return -1;
+ 	}
++	return 0;
++}
+ 
+-	server->query_port = server->port = port;
+-	init_qserver( server, type);
++void add_config_server_types()
++{
++	int n_config_types, n_builtin_types, i;
++	server_type **config_types;
++	server_type *new_types, *type;
++	config_types = qsc_get_config_server_types(&n_config_types);
+ 
+-	if ( num_servers_total % 10 == 0)
++	if (n_config_types == 0)
+ 	{
+-		hcache_update_file();
++		return ;
+ 	}
+ 
+-	if ( last_server != &servers)
++	n_builtin_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++	new_types = (server_type*)malloc(sizeof(server_type)*(n_builtin_types + n_config_types + 1));
++
++	memcpy(new_types, &builtin_types[0], n_builtin_types *sizeof(server_type));
++	type = &new_types[n_builtin_types];
++	for (i = n_config_types; i; i--, config_types++, type++)
+ 	{
+-		prev_server= (struct qserver*) ((char*)last_server - ((char*)&server->next - (char*)server));
+-		server->prev= prev_server;
++		*type = **config_types;
+ 	}
+-	*last_server= server;
+-	last_server= & server->next;
+-
+-	add_server_to_hash( server);
+-
+-	++num_servers;
+-
+-	return server;
++	n_server_types = n_builtin_types + n_config_types;
++	new_types[n_server_types].id = Q_UNKNOWN_TYPE;
++	if (types != &builtin_types[0])
++	{
++		free(types);
++	}
++	types = new_types;
+ }
+ 
+-void
+-add_servers_from_masters()
++void revert_server_types()
+ {
+-    struct qserver *server;
+-    unsigned int ipaddr, i;
+-    unsigned short port;
+-    int n_servers, new_server, port_adjust= 0;
+-    char *pkt;
+-    server_type* server_type;
+-    FILE *outfile;
+-
+-    for ( server= servers; server != NULL; server= server->next)  {
+-	if ( !server->type->master || server->master_pkt == NULL)
+-	    continue;
+-	pkt= server->master_pkt;
+-
+-	if ( server->query_arg && server->type->id == GAMESPY_MASTER)  {
+-	    server_type= find_server_type_string( server->query_arg);
+-	    if ( server_type == NULL)
+-		server_type= find_server_type_id( server->type->master);
+-	}
+-	else
+-	    server_type= find_server_type_id( server->type->master);
+-
+-	if ( server->type->id == GAMESPY_MASTER && server_type)  {
+-	    if ( server_type->id == UN_SERVER)
+-		port_adjust= -1;
+-	    else if ( server_type->id == KINGPIN_SERVER)
+-		port_adjust= 10;
+-	}
+-
+-	outfile= NULL;
+-	if ( server->outfilename)  {
+-	    if ( strcmp( server->outfilename, "-") == 0)
+-		outfile= stdout;
+-	    else
+-	        outfile= fopen( server->outfilename, "w");
+-	    if ( outfile == NULL)  {
+-		perror( server->outfilename);
+-		continue;
+-	    }
+-	}
+-	n_servers= 0;
+-	for ( i= 0; i < server->master_pkt_len; i+= 6)  {
+-	    memcpy( &ipaddr, &pkt[i], 4);
+-	    memcpy( &port, &pkt[i+4], 2);
+-	    ipaddr= ntohl( ipaddr);
+-	    port= ntohs( port) + port_adjust;
+-	    new_server= 1;
+-	    if ( outfile)
+-		fprintf( outfile, "%s %d.%d.%d.%d:%hu\n",
+-			server_type ? server_type->type_string : "",
+-			(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-			(ipaddr>>8)&0xff, ipaddr&0xff, port);
+-	    else if ( server_type == NULL)
+-		fprintf( OF, "%d.%d.%d.%d:%hu\n",
+-			(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-			(ipaddr>>8)&0xff, ipaddr&0xff, port);
+-	    else
+-		add_qserver_byaddr( ipaddr, port, server_type, &new_server);
+-	    n_servers+= new_server;
+-	}
+-	free( server->master_pkt);
+-	server->master_pkt= NULL;
+-	server->master_pkt_len= 0;
+-	server->n_servers= n_servers;
+-	if ( outfile)
+-	    fclose( outfile);
+-    }
+-    if ( hostname_lookup)
+-	hcache_update_file();
+-    bind_sockets();
++	if (types != &builtin_types[0])
++	{
++		free(types);
++	}
++	n_server_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++	types = &builtin_types[0];
+ }
+ 
+-void
+-init_qserver( struct qserver *server, server_type* type)
++#ifdef ENABLE_DUMP
++unsigned pkt_dump_pos = 0;
++const char *pkt_dumps[64] = { 0 };
++static void add_pkt_from_file(const char *file)
+ {
+-    server->server_name= NULL;
+-    server->map_name= NULL;
+-    server->game= NULL;
+-    server->num_players= 0;
+-    server->fd= -1;
+-    if ( server->flags & FLAG_BROADCAST)  {
+-	server->retry1= 1;
+-	server->retry2= 1;
+-    }
+-    else  {
+-	server->retry1= n_retries;
+-	server->retry2= n_retries;
+-    }
+-    server->n_retries= 0;
+-    server->ping_total= 0;
+-    server->n_packets= 0;
+-    server->n_requests= 0;
+-
+-    server->n_servers= 0;
+-    server->master_pkt_len= 0;
+-    server->master_pkt= NULL;
+-    server->error= NULL;
+-
+-    server->saved_data.data= NULL;
+-    server->saved_data.datalen= 0;
+-    server->saved_data.pkt_index= -1;
+-    server->saved_data.pkt_max= 0;
+-    server->saved_data.next= NULL;
+-
+-    server->type = type;
+-    server->next_rule= (get_server_rules) ? "" : NO_SERVER_RULES;
+-    server->next_player_info= (get_player_info &&
+-		    type->player_packet) ? 0 : NO_PLAYER_INFO;
+-
+-    server->n_player_info= 0;
+-    server->players= NULL;
+-    server->n_rules= 0;
+-    server->rules= NULL;
+-    server->last_rule= &server->rules;
+-    server->missing_rules= 0;
+-
+-    num_servers_total++;
++	if (pkt_dump_pos >= sizeof(pkt_dumps) / sizeof(pkt_dumps[0]))
++	{
++		return ;
++	}
++	pkt_dumps[pkt_dump_pos++] = file;
+ }
+ 
+-// ipaddr should be network byte-order
+-// port should be host byte-order
+-struct qserver *
+-find_server_by_address( unsigned int ipaddr, unsigned short port)
++static void replay_pkt_dumps()
+ {
+-    struct qserver **hashed;
+-    unsigned int hash, i;
+-    hash= (ipaddr + port) % ADDRESS_HASH_LENGTH;
+-
+-    if ( ipaddr == 0)  {
+-	for ( hash= 0; hash < ADDRESS_HASH_LENGTH; hash++)
+-	    printf( "%3d %d\n", hash, server_hash_len[hash]);
+-	return NULL;
+-    }
++	struct qserver *server = servers;
++	char *pkt = NULL;
++	int fd;
++	int bytes_read = 0; // should be ssize_t but for ease with win32
++	int i;
++	struct stat statbuf;
++	gettimeofday(&packet_recv_time, NULL);
+ 
+-    hashed = server_hash[hash];
+-    for ( i= server_hash_len[hash]; i; i--, hashed++)
+-	if ( *hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port)
+-	    return *hashed;
+-    return NULL;
+-}
+-
+-void
+-add_server_to_hash( struct qserver *server)
+-{
+-    unsigned int hash;
+-    hash = (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
+-
+-    if ( server_hash_len[hash] % 16 == 0)
+-    {
+-		server_hash[hash]= (struct qserver**) realloc( server_hash[hash],
+-			sizeof( struct qserver **) * (server_hash_len[hash]+16));
+-		memset( &server_hash[hash][server_hash_len[hash]], 0,
+-			sizeof( struct qserver **) * 16);
+-    }
+-    server_hash[hash][server_hash_len[hash]]= server;
+-    server_hash_len[hash]++;
+-}
++	for (i = 0; i < pkt_dump_pos; i++)
++	{
++		if ((fd = open(pkt_dumps[i], O_RDONLY)) == -1)
++		{
++			goto err;
++		}
+ 
+-void
+-remove_server_from_hash( struct qserver *server)
+-{
+-    struct qserver **hashed;
+-    unsigned int hash, i, ipaddr;
+-    unsigned short port;
+-    hash= (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
++		if (fstat(fd, &statbuf) == -1)
++		{
++			goto err;
++		}
++		pkt = malloc(statbuf.st_size);
++		if (NULL == pkt)
++		{
++			goto err;
++		}
++		bytes_read = read(fd, pkt, statbuf.st_size);
++		if (bytes_read != statbuf.st_size)
++		{
++			fprintf(stderr, "Failed to read entire packet from disk got %d of %ld bytes\n", bytes_read, (long)statbuf.st_size);
++			goto err;
++		}
++		close(fd);
++		fd = 0;
+ 
+-    ipaddr= server->ipaddr;
+-    port= server->port;
+-    hashed= server_hash[hash];
+-    for ( i= server_hash_len[hash]; i; i--, hashed++)
+-	if ( *hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port)  {
+-	    *hashed= NULL;
+-	    break;
++		debug(2, "replay, pre-packet_func");
++		process_func_ret( server, server->type->packet_func( server, pkt, statbuf.st_size ) );
++		debug(2, "replay, post-packet_func");
+ 	}
+-}
++	goto out;
+ 
+-void
+-free_server_hash()
+-{
+-    int i;
+-    for ( i= 0; i < ADDRESS_HASH_LENGTH; i++)
+-	if ( server_hash[i]) free( server_hash[i]);
++	err: perror(__FUNCTION__);
++	close(fd);
++	out: fd = 0; // NOP
+ }
++#endif // ENABLE_DUMP
+ 
++struct rcv_pkt
++{
++	struct qserver *server;
++	struct sockaddr_in addr;
++	struct timeval recv_time;
++	char data[PACKET_LEN];
++	int len;
++	int _errno;
++};
+ 
+-/* Functions for binding sockets to Quake servers
+- */
+-int
+-bind_qserver( struct qserver *server)
++void do_work(void)
+ {
+-    struct sockaddr_in addr;
+-    static int one= 1;
++	int pktlen, rc, fd;
++	char *pkt = NULL;
++	int bind_retry = 0;
++	struct timeval timeout;
++	struct rcv_pkt *buffer;
++	unsigned buffill = 0, i = 0;
++	unsigned bufsize = max_simultaneous * 2;
+ 
+-    if ( server->type->flags & TF_TCP_CONNECT)
+-	server->fd= socket( AF_INET, SOCK_STREAM, 0);
+-    else
+-	server->fd= socket( AF_INET, SOCK_DGRAM, 0);
+-
+-    if ( server->fd == INVALID_SOCKET) {
+-	if ( sockerr() == EMFILE)  {
+-	    server->fd= -1;
+-	    return -2;
+-	}
+-        perror( "socket" );
+-	server->server_name= SYSERROR;
+-        return -1;
+-    }
++	struct timeval t, ts;
++	gettimeofday(&t, NULL);
++	ts = t;
+ 
+-    addr.sin_family = AF_INET;
+-    addr.sin_addr.s_addr = htonl( source_ip);
+-    if ( server->type->id == Q2_MASTER)
+-	addr.sin_port= htons(26500);
+-    else if ( source_port == 0)
+-	addr.sin_port= 0;
+-    else  {
+-	addr.sin_port= htons( source_port);
+-	source_port++;
+-	if ( source_port > source_port_high)
+-	    source_port= source_port_low;
+-    }
+-    memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero) );
++	buffer = malloc(sizeof(struct rcv_pkt) *bufsize);
+ 
+-    if ( bind( server->fd, (struct sockaddr *)&addr,
+-		sizeof(struct sockaddr)) == SOCKET_ERROR) {
+-	if ( sockerr() != EADDRINUSE)  {
+-	    perror( "bind" );
+-	    server->server_name= SYSERROR;
++	if (!buffer)
++	{
++		return ;
+ 	}
+-	close(server->fd);
+-	server->fd= -1;
+-        return -1;
+-    }
+ 
+-    if ( server->flags & FLAG_BROADCAST)
+-	setsockopt( server->fd, SOL_SOCKET, SO_BROADCAST, (char*)&one,
+-		sizeof(one));
+-
+-    if ( server->type->id != Q2_MASTER &&
+-		!(server->flags & FLAG_BROADCAST))  {
+-	addr.sin_family= AF_INET;
+-	if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
+-	    addr.sin_port= htons(server->port);
+-	else
+-	    addr.sin_port= htons( (unsigned short)( server->port + server->type->port_offset ) );
+-	addr.sin_addr.s_addr= server->ipaddr;
+-	memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero) );
+-
+-	if ( connect( server->fd, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)  {
+-		char error[50];
+-	    if ( server->type->id == UN_MASTER)  {
+-		if ( connection_refused())  {
+-		/*  server->fd= -2; */
+-		/* set up for connect retry */
+-		}
+-	    }
+-		sprintf( error, "connect:%s:%u", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
+-	    perror( error );
+-	    server->server_name= SYSERROR;
+-	    close(server->fd);
+-	    server->fd= -1;
+-	    return -1;
++#ifdef ENABLE_DUMP
++	if (pkt_dump_pos)
++	{
++		replay_pkt_dumps();
+ 	}
+-    }
+-
+-    // we need nonblocking always. poll on an udp socket would wake
+-    // up and recv blocks if a packet with incorrect checksum is
+-    // received
+-    set_non_blocking( server->fd);
+-
+-    if ( server->type->flags & TF_TCP_CONNECT)  {
+-	int one= 1;
+-	setsockopt( server->fd, IPPROTO_TCP, TCP_NODELAY,
+-		(char*) &one, sizeof(one));
+-    }
+-
+-#ifndef _WIN32
+-    if ( server->fd >= max_connmap)  {
+-	int old_max= max_connmap;
+-	max_connmap= server->fd + 32;
+-	connmap= (struct qserver **) realloc( connmap, max_connmap *
+-		sizeof( struct qserver *));
+-	memset( &connmap[old_max], 0, (max_connmap - old_max) *
+-		sizeof( struct qserver *));
+-    }
+-    connmap[server->fd]= server;
++	else
+ #endif
+-#ifdef _WIN32
+-    { int i;
+-    for ( i= 0; i < max_connmap; i++)  {
+-	if ( connmap[i] == NULL)  {
+-	    connmap[i]= server;
+-	    break;
++	{
++		bind_retry = bind_sockets();
+ 	}
+-    }
+-	if ( i >= max_connmap) printf( "could not put server in connmap\n");
+-    }
+-#endif
+-
+-    return 0;
+-}
+ 
+-static struct timeval t_lastsend = {0,0};
++	send_packets();
+ 
+-int
+-bind_sockets()
+-{
+-	struct qserver *server;
+-	struct timeval now;
+-	int rc, retry_count= 0;;
++	debug(2, "connected: %d", connected);
+ 
+-	gettimeofday(&now, NULL);
+-	if(connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
+-	{
+-		server = NULL;
+-	}
+-	else if ( !waiting_for_masters)
++	while (connected || (!connected && bind_retry == -2))
+ 	{
+-		if ( last_server_bind == NULL)
++		if (!connected && bind_retry == -2)
+ 		{
+-			last_server_bind= servers;
++			rc = wait_for_timeout(60);
++			bind_retry = bind_sockets();
++			continue;
+ 		}
+-		server= last_server_bind;
+-	}
+-	else
+-	{
+-		server= servers;
+-	}
++		bind_retry = 0;
+ 
+-	for ( ; server != NULL && connected < max_simultaneous; server= server->next)
+-	{
+-		if ( server->server_name == NULL && server->fd == -1)
++		set_file_descriptors();
++
++		if (progress)
++		{
++			display_progress();
++		}
++
++		get_next_timeout(&timeout);
++
++		rc = wait_for_file_descriptors(&timeout);
++
++		debug(2, "rc %d", rc);
++
++		if (rc == SOCKET_ERROR)
+ 		{
+-			if ( waiting_for_masters && !server->type->master)
++#ifndef _WIN32
++			if (errno == EINTR)
+ 			{
+ 				continue;
+ 			}
++#endif
++			perror("select");
++			break;
++		}
+ 
+-			if ( (rc= bind_qserver( server)) == 0)
++		fd = 0;
++		for (; rc && buffill < bufsize; rc--)
++		{
++			int addrlen = sizeof(buffer[buffill].addr);
++			struct qserver *server = get_next_ready_server();
++			if (server == NULL)
+ 			{
+-				debug(1, "send %d.%d.%d.%d:%hu\n",
+-						server->ipaddr&0xff,
+-						(server->ipaddr>>8)&0xff,
+-						(server->ipaddr>>16)&0xff,
+-						(server->ipaddr>>24)&0xff,
+-						server->port);
+-
+-				gettimeofday(&t_lastsend, NULL);
+-				server->type->status_query_func( server);
+-				connected++;
+-				if ( !waiting_for_masters)
+-				{
+-					last_server_bind= server;
+-				}
+ 				break;
+ 			}
+-			else if ( rc == -2 && ++retry_count > 2)
+-			{
+-				return -2;
+-			}
+-		}
+-	}
+ 
+-	if ( ! connected && retry_count)
+-	{
+-		return -2;
+-	}
++			gettimeofday(&buffer[buffill].recv_time, NULL);
+ 
+-	return 0;
+-}
++			pktlen = recvfrom(server->fd, buffer[buffill].data, sizeof(buffer[buffill].data), 0, (struct sockaddr*) &buffer[buffill].addr, (void*) &addrlen);
+ 
++			debug( 2, "recvfrom: %d", pktlen );
+ 
+-/*
+- * Functions for sending packets
+- */
+-// this is so broken, someone please rewrite the timeout handling
+-void
+-send_packets()
+-{
+-	struct qserver *server;
+-	struct timeval now;
+-	int interval, n_sent=0, prev_n_sent;
+-	unsigned i;
+-	debug( 3, "processing..." );
++			// pktlen == 0 is no error condition! happens on remote tcp socket close
++			if (pktlen == SOCKET_ERROR)
++			{
++				if (connection_would_block())
++				{
++					malformed_packet(server, "EAGAIN on UDP socket, probably incorrect checksum");
++				}
++				else if (connection_refused() || connection_reset())
++				{
++					server->server_name = DOWN;
++					num_servers_down++;
++					cleanup_qserver( server, FORCE );
++				}
++				continue;
++			}
+ 
+-	gettimeofday( &now, NULL);
++			debug(1, "recv %3d %3d %d.%d.%d.%d:%hu\n",
++				time_delta(&buffer[buffill].recv_time, &ts),
++				time_delta(&buffer[buffill].recv_time, &t),
++				server->ipaddr &0xff,
++				(server->ipaddr >> 8) &0xff,
++				(server->ipaddr >> 16) &0xff,
++				(server->ipaddr >> 24) &0xff,
++				server->port
++			);
+ 
+-	if(!t_lastsend.tv_sec)
+-	{
+-		// nothing
+-	}
+-	else if(connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
+-	{
+-		return;
+-	}
++			t = buffer[buffill].recv_time;
+ 
+-	for ( i = 0; i < max_connmap; ++i )
+-	{
+-		server = connmap[i];
+-		if( ! server )
+-		{
+-			continue;
++			buffer[buffill].server = server;
++			buffer[buffill].len = pktlen;
++			++buffill;
+ 		}
+ 
+-		if(server->fd == -1)
+-		{
+-			debug(0, "invalid entry in connmap\n");
+-		}
++		debug( 2, "fill: %d < %d",  buffill, bufsize) ;
+ 
+-		if ( server->type->id & MASTER_SERVER)
++		for (i = 0; i < buffill; ++i)
+ 		{
+-			interval = master_retry_interval;
+-		}
+-		else
+-		{
+-			interval = retry_interval;
+-		}
++			struct qserver *server = buffer[i].server;
++			pkt = buffer[i].data;
++			pktlen = buffer[i].len;
++			memcpy(&packet_recv_time, &buffer[i].recv_time, sizeof(packet_recv_time));
+ 
+-		debug(2, "server %p, name %s, retry1 %d, next_rule %p, next_player_info %d, num_players %d, n_retries %d", server, server->server_name, server->retry1, server->next_rule, server->next_player_info, server->num_players, n_retries);
+-		prev_n_sent = n_sent;
+-		if ( server->server_name == NULL )
+-		{
+-			// We havent seen the server yet?
+-			if (
+-				server->retry1 != n_retries &&
+-				time_delta( &now, &server->packet_time1) < (interval*(n_retries-server->retry1+1))
+-			)
++			if (get_debug_level() > 2)
+ 			{
+-				continue;
++				print_packet(server, pkt, pktlen);
+ 			}
+ 
+-			if ( ! server->retry1 )
++#ifdef ENABLE_DUMP
++			if (do_dump)
+ 			{
+-				// No more retries
+-				cleanup_qserver( server, 1);
+-				continue;
++				dump_packet(pkt, pktlen);
+ 			}
+-
+-			if( qserver_get_timeout(server, &now) <= 0 )
++#endif
++			if (server->flags &FLAG_BROADCAST)
+ 			{
+-				// Query status
+-				debug(2, "calling status_query_func for %p", server);
+-				server->type->status_query_func( server);
+-				gettimeofday(&t_lastsend, NULL);
+-				n_sent++;
+-				continue;
+-			}
+-		}
+-
+-		if ( server->next_rule != NO_SERVER_RULES )
+-		{
+-			// We want server rules
+-			if (
+-				server->retry1 != n_retries &&
+-				time_delta( &now, &server->packet_time1) < (interval*(n_retries-server->retry1+1))
+-			)
++				struct qserver *broadcast = server;
++				unsigned short port = ntohs(buffer[i].addr.sin_port);
++				/* create new server and init */
++				if (!(no_port_offset || server->flags &TF_NO_PORT_OFFSET))
++				{
++					port -= server->type->port_offset;
++				}
++				server = add_qserver_byaddr(ntohl(buffer[i].addr.sin_addr.s_addr), port, server->type, NULL);
++				if (server == NULL)
++				{
++					server = find_server_by_address(buffer[i].addr.sin_addr.s_addr, ntohs(buffer[i].addr.sin_port));
++					if (server == NULL)
++					{
++						continue;
++					}
++					/*
++					if ( show_errors)
++					{
++					fprintf(stderr,
++					"duplicate or invalid packet received from 0x%08x:%hu\n",
++					ntohl(buffer[i].addr.sin_addr.s_addr), ntohs(buffer[i].addr.sin_port));
++					print_packet( NULL, pkt, pktlen);
++					}
++					continue;
++					 */
++				}
++				else
++				{
++					server->packet_time1 = broadcast->packet_time1;
++					server->packet_time2 = broadcast->packet_time2;
++					server->ping_total = broadcast->ping_total;
++					server->n_requests = broadcast->n_requests;
++					server->n_packets = broadcast->n_packets;
++					broadcast->n_servers++;
++				}
++			}
++
++			debug(2, "connected, pre-packet_func: %d", connected);
++			process_func_ret( server, server->type->packet_func(server, pkt, pktlen) );
++			debug(2, "connected, post-packet_func: %d", connected);
++		}
++		buffill = 0;
++
++		if (run_timeout && time(0) - start_time >= run_timeout)
++		{
++			debug(2, "run timeout reached");
++			break;
++		}
++
++		send_packets();
++		if (connected < max_simultaneous)
++		{
++			bind_retry = bind_sockets();
++		}
++
++		debug(2, "connected: %d", connected);
++	}
++
++	free(buffer);
++}
++
++int main(int argc, char *argv[])
++{
++	int arg, n_files, i;
++	char **files, *outfilename, *query_arg;
++	struct server_arg *server_args = NULL;
++	int n_server_args = 0, max_server_args = 0;
++	int default_server_type_id;
++
++#ifdef _WIN32
++	WORD version = MAKEWORD(1, 1);
++	WSADATA wsa_data;
++	if (WSAStartup(version, &wsa_data) != 0)
++	{
++		fprintf(stderr, "Could not open winsock\n");
++		exit(1);
++	}
++#else
++	signal(SIGPIPE, SIG_IGN);
++#endif
++
++	types = &builtin_types[0];
++	n_server_types = (sizeof(builtin_types) / sizeof(server_type)) - 1;
++
++	i = qsc_load_default_config_files();
++	if (i == -1)
++	{
++		return 1;
++	}
++	else if (i == 0)
++	{
++		add_config_server_types();
++	}
++
++	if (argc == 1)
++	{
++		usage(NULL, argv, NULL);
++	}
++
++	OF = stdout;
++
++	files = (char **)malloc(sizeof(char*)*(argc / 2));
++	n_files = 0;
++
++	default_server_type_id = Q_SERVER;
++	little_endian = ((char*) &one)[0];
++	big_endian = !little_endian;
++
++	for (arg = 1; arg < argc; arg++)
++	{
++		if (argv[arg][0] != '-')
++		{
++			break;
++		}
++		outfilename = NULL;
++		if (strcmp(argv[arg], "-nocfg") == 0 && arg == 1)
++		{
++			revert_server_types();
++		}
++		else if (strcmp(argv[arg], "--help") == 0)
++		{
++			usage(NULL, argv, NULL);
++		}
++		else if (strcmp(argv[arg], "-f") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -f\n", argv, NULL);
++			}
++			files[n_files++] = argv[arg];
++		}
++		else if (strcmp(argv[arg], "-retry") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -retry\n", argv, NULL);
++			}
++			n_retries = atoi(argv[arg]);
++			if (n_retries <= 0)
++			{
++				fprintf(stderr, "retries must be greater than zero\n");
++				exit(1);
++			}
++		}
++		else if (strcmp(argv[arg], "-interval") == 0)
++		{
++			double value = 0.0;
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -interval\n", argv, NULL);
++			}
++			sscanf(argv[arg], "%lf", &value);
++			if (value < 0.1)
++			{
++				fprintf(stderr, "retry interval must be greater than 0.1\n");
++				exit(1);
++			}
++			retry_interval = (int)(value *1000);
++		}
++		else if (strcmp(argv[arg], "-mi") == 0)
++		{
++			double value = 0.0;
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -mi\n", argv, NULL);
++			}
++			sscanf(argv[arg], "%lf", &value);
++			if (value < 0.1)
++			{
++				fprintf(stderr, "interval must be greater than 0.1\n");
++				exit(1);
++			}
++			master_retry_interval = (int)(value *1000);
++		}
++		else if (strcmp(argv[arg], "-H") == 0)
++		{
++			hostname_lookup = 1;
++		}
++		else if (strcmp(argv[arg], "-u") == 0)
++		{
++			up_servers_only = 1;
++		}
++		else if (strcmp(argv[arg], "-nf") == 0)
++		{
++			no_full_servers = 1;
++		}
++		else if (strcmp(argv[arg], "-ne") == 0)
++		{
++			no_empty_servers = 1;
++		}
++		else if (strcmp(argv[arg], "-nh") == 0)
++		{
++			no_header_display = 1;
++		}
++		else if (strcmp(argv[arg], "-old") == 0)
++		{
++			new_style = 0;
++		}
++		else if (strcmp(argv[arg], "-P") == 0)
++		{
++			get_player_info = 1;
++		}
++		else if (strcmp(argv[arg], "-R") == 0)
++		{
++			get_server_rules = 1;
++		}
++		else if (strncmp(argv[arg], "-raw", 4) == 0)
++		{
++			if (argv[arg][4] == ',')
++			{
++				if (strcmp(&argv[arg][5], "game") == 0)
++				{
++					show_game_in_raw = 1;
++				}
++				else
++				{
++					usage("Unknown -raw option\n", argv, NULL);
++				}
++			}
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -raw\n", argv, NULL);
++			}
++			raw_delimiter = argv[arg];
++
++			// Check the multi rule delimiter isnt the same
++			// If it is fix to maintain backwards compatibility
++			if (0 == strcmp(raw_delimiter, multi_delimiter) && 0 == strcmp(raw_delimiter, "|"))
++			{
++				multi_delimiter = ":";
++			}
++			raw_display = 1;
++		}
++		else if (strcmp(argv[arg], "-mdelim") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -mdelim\n", argv, NULL);
++			}
++			multi_delimiter = argv[arg];
++		}
++		else if (strcmp(argv[arg], "-xml") == 0)
++		{
++			xml_display = 1;
++			if (raw_display == 1)
++			{
++				usage("cannot specify both -raw and -xml\n", argv, NULL);
++			}
++		}
++		else if (strcmp(argv[arg], "-utf8") == 0)
++		{
++			xml_encoding = ENCODING_UTF_8;
++			name_xforms = 0;
++		}
++		else if (strcmp(argv[arg], "-ncn") == 0)
++		{
++			color_names = 0;
++		}
++		else if (strcmp(argv[arg], "-cn") == 0)
++		{
++			color_names = 1;
++		}
++		else if (strcmp(argv[arg], "-hc") == 0)
++		{
++			color_names = 2;
++		}
++		else if (strcmp(argv[arg], "-nx") == 0)
++		{
++			name_xforms = 1;
++		}
++		else if (strcmp(argv[arg], "-nnx") == 0)
++		{
++			name_xforms = 0;
++		}
++		else if (strcmp(argv[arg], "-tc") == 0)
++		{
++			time_format = CLOCK_TIME;
++		}
++		else if (strcmp(argv[arg], "-tsw") == 0)
++		{
++			time_format = STOPWATCH_TIME;
++		}
++		else if (strcmp(argv[arg], "-ts") == 0)
++		{
++			time_format = SECONDS;
++		}
++		else if (strcmp(argv[arg], "-pa") == 0)
++		{
++			player_address = 1;
++		}
++		else if (strcmp(argv[arg], "-hpn") == 0)
++		{
++			hex_player_names = 1;
++		}
++		else if (strcmp(argv[arg], "-hsn") == 0)
++		{
++			hex_server_names = 1;
++		}
++		else if (strncmp(argv[arg], "-maxsimultaneous", 7) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -maxsimultaneous\n", argv, NULL);
++			}
++			max_simultaneous = atoi(argv[arg]);
++			if (max_simultaneous <= 0)
++			{
++				usage("value for -maxsimultaneous must be > 0\n", argv, NULL);
++			}
++			if (max_simultaneous > FD_SETSIZE)
++			{
++				max_simultaneous = FD_SETSIZE;
++			}
++		}
++		else if (strcmp(argv[arg], "-sendinterval") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -sendinterval\n", argv, NULL);
++			}
++			sendinterval = atoi(argv[arg]);
++			if (sendinterval < 0)
++			{
++				usage("value for -sendinterval must be >= 0\n", argv, NULL);
++			}
++		}
++		else if (strcmp(argv[arg], "-raw-arg") == 0)
++		{
++			raw_arg = 1000;
++		}
++		else if (strcmp(argv[arg], "-timeout") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -timeout\n", argv, NULL);
++			}
++			run_timeout = atoi(argv[arg]);
++			if (run_timeout <= 0)
++			{
++				usage("value for -timeout must be > 0\n", argv, NULL);
++			}
++		}
++		else if (strncmp(argv[arg], "-progress", sizeof("-progress") - 1) == 0)
++		{
++			char *p = argv[arg] + sizeof("-progress") - 1;
++			progress = 1;
++			if (*p == ',')
++			{
++				progress = atoi(p + 1);
++			}
++		}
++		else if (strcmp(argv[arg], "-Hcache") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -Hcache\n", argv, NULL);
++			}
++			if (hcache_open(argv[arg], 0) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strcmp(argv[arg], "-default") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -default\n", argv, NULL);
++			}
++			default_server_type = find_server_type_string(argv[arg]);
++			if (default_server_type == NULL)
++			{
++				char opt[256], *o = &opt[0];
++				sprintf(opt, "-%s", argv[arg]);
++				for (; *o; o++)
++				{
++					*o = tolower(*o);
++				}
++				default_server_type = find_server_type_option(opt);
++			}
++			if (default_server_type == NULL)
++			{
++				fprintf(stderr, "unknown server type \"%s\"\n", argv[arg]);
++				usage(NULL, argv, NULL);
++			}
++			default_server_type_id = default_server_type->id;
++			default_server_type = NULL;
++		}
++		else if (strncmp(argv[arg], "-Tserver", 3) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (read_qserver_template(argv[arg]) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strncmp(argv[arg], "-Trule", 3) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (read_rule_template(argv[arg]) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strncmp(argv[arg], "-Theader", 3) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (read_header_template(argv[arg]) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strncmp(argv[arg], "-Ttrailer", 3) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (read_trailer_template(argv[arg]) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strncmp(argv[arg], "-Tplayer", 3) == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (read_player_template(argv[arg]) == -1)
++			{
++				return 1;
++			}
++		}
++		else if (strcmp(argv[arg], "-sort") == 0)
++		{
++			size_t pos;
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for -sort\n", argv, NULL);
++			}
++			strncpy(sort_keys, argv[arg], sizeof(sort_keys) - 1);
++			pos = strspn(sort_keys, SUPPORTED_SORT_KEYS);
++			if (pos != strlen(sort_keys))
++			{
++				fprintf(stderr, "Unknown sort key \"%c\", valid keys are \"%s\"\n", sort_keys[pos], SUPPORTED_SORT_KEYS);
++				return 1;
++			}
++			server_sort = strpbrk(sort_keys, SUPPORTED_SERVER_SORT) != NULL;
++			if (strchr(sort_keys, 'l'))
++			{
++				server_sort = 1;
++			}
++			player_sort = strpbrk(sort_keys, SUPPORTED_PLAYER_SORT) != NULL;
++		}
++		else if (strcmp(argv[arg], "-errors") == 0)
++		{
++			show_errors++;
++		}
++		else if (strcmp(argv[arg], "-of") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (argv[arg][0] == '-' && argv[arg][1] == '\0')
++			{
++				OF = stdout;
++			}
++			else
++			{
++				OF = fopen(argv[arg], "w");
++			}
++			if (OF == NULL)
++			{
++				perror(argv[arg]);
++				return 1;
++			}
++		}
++		else if (strcmp(argv[arg], "-af") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (argv[arg][0] == '-' && argv[arg][1] == '\0')
++			{
++				OF = stdout;
++			}
++			else
++			{
++				OF = fopen(argv[arg], "a");
++			}
++			if (OF == NULL)
++			{
++				perror(argv[arg]);
++				return 1;
++			}
++		}
++		else if (strcmp(argv[arg], "-htmlnames") == 0)
++		{
++			html_names = 1;
++		}
++		else if (strcmp(argv[arg], "-nohtmlnames") == 0)
++		{
++			html_names = 0;
++		}
++		else if (strcmp(argv[arg], "-htmlmode") == 0)
++		{
++			html_mode = 1;
++		}
++		else if (strcmp(argv[arg], "-carets") == 0)
++		{
++			strip_carets = 0;
++		}
++		else if (strcmp(argv[arg], "-d") == 0)
++		{
++			set_debug_level(get_debug_level() + 1);
++		}
++		else if (strcmp(argv[arg], "-showgameport") == 0)
++		{
++			show_game_port = 1;
++		}
++		else if (strcmp(argv[arg], "-noportoffset") == 0)
++		{
++			no_port_offset = 1;
++		}
++		else if (strcmp(argv[arg], "-srcip") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (parse_source_address(argv[arg], &source_ip, &source_port) == -1)
++			{
++				return 1;
++			}
++			if (source_port)
++			{
++				source_port_low = source_port;
++				source_port_high = source_port;
++			}
++		}
++		else if (strcmp(argv[arg], "-syncconnect") == 0)
++		{
++			syncconnect = 1;
++		}
++		else if (strcmp(argv[arg], "-stripunprintable") == 0)
++		{
++			name_xforms_strip_unprintable = 1;
++		}
++		else if (strcmp(argv[arg], "-srcport") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (parse_source_port(argv[arg], &source_port_low, &source_port_high) == -1)
++			{
++				return 1;
++			}
++			source_port = source_port_low;
++		}
++		else if (strcmp(argv[arg], "-cfg") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			if (qsc_load_config_file(argv[arg]) == -1)
++			{
++				return 1;
++			}
++			add_config_server_types();
++		}
++		else if (strcmp(argv[arg], "-allowserverdups") == 0)
++		{
++			noserverdups = 0;
++		}
++		else if (strcmp(argv[arg], "-bom") == 0)
++		{
++			output_bom = 1;
++		}
++#ifdef ENABLE_DUMP
++		else if (strcmp(argv[arg], "-dump") == 0)
++		{
++			do_dump = 1;
++		}
++		else if (strcmp(argv[arg], "-pkt") == 0)
++		{
++			arg++;
++			if (arg >= argc)
++			{
++				usage("missing argument for %s\n", argv, argv[arg - 1]);
++			}
++			add_pkt_from_file(argv[arg]);
++		}
++#endif
++#ifdef _WIN32
++		else if (strcmp(argv[arg], "-noconsole") == 0)
++		{
++			FreeConsole();
++		}
++#endif
++		else
++		{
++			int outfile;
++			server_type *type;
++			arg++;
++			if (arg >= argc)
++			{
++				fprintf(stderr, "missing argument for \"%s\"\n", argv[arg - 1]);
++				return 1;
++			}
++			type = parse_server_type_option(argv[arg - 1], &outfile, &query_arg);
++			if (type == NULL)
++			{
++				fprintf(stderr, "unknown option \"%s\"\n", argv[arg - 1]);
++				return 1;
++			}
++			outfilename = NULL;
++			if (outfile)
++			{
++				outfilename = strchr(argv[arg], ',');
++				if (outfilename == NULL)
++				{
++					fprintf(stderr, "missing file name for \"%s,outfile\"\n", argv[arg - 1]);
++					return 1;
++				}
++				*outfilename++ = '\0';
++			}
++			/*
++			if ( query_arg && !(type->flags & TF_QUERY_ARG)) {
++			fprintf( stderr, "option flag \"%s\" not allowed for this server type\n",
++			query_arg);
++			return 1;
++			}
++			 */
++			if (type->flags &TF_QUERY_ARG_REQUIRED && !query_arg )
++			{
++				fprintf(stderr, "option flag missing for server type \"%s\"\n", argv[arg - 1]);
++				return 1;
++			}
++			add_server_arg(argv[arg], type->id, outfilename, query_arg, &server_args, &n_server_args, &max_server_args);
++		}
++	}
++
++	start_time = time(0);
++
++	default_server_type = find_server_type_id(default_server_type_id);
++
++	for (i = 0; i < n_files; i++)
++	{
++		add_file(files[i]);
++	}
++
++	for (; arg < argc; arg++)
++	{
++		add_qserver(argv[arg], default_server_type, NULL, NULL);
++	}
++
++	for (i = 0; i < n_server_args; i++)
++	{
++		server_type *server_type = find_server_type_id(server_args[i].type_id);
++		add_qserver(server_args[i].arg, server_type, server_args[i].outfilename, server_args[i].query_arg);
++	}
++
++	free(server_args);
++
++	if (servers == NULL)
++	{
++		exit(1);
++	}
++
++	max_connmap = max_simultaneous + 10;
++	connmap = (struct qserver **)calloc(1, sizeof(struct qserver*) * max_connmap);
++
++	if (color_names == -1)
++	{
++		color_names = (raw_display) ? DEFAULT_COLOR_NAMES_RAW : DEFAULT_COLOR_NAMES_DISPLAY;
++	}
++
++	if (time_format == -1)
++	{
++		time_format = (raw_display) ? DEFAULT_TIME_FMT_RAW : DEFAULT_TIME_FMT_DISPLAY;
++	}
++
++	if ((one_server_type_id &MASTER_SERVER) || one_server_type_id == 0)
++	{
++		display_prefix = 1;
++	}
++
++	if (xml_display)
++	{
++		xml_header();
++	}
++	else if (new_style && !raw_display && !have_server_template())
++	{
++		display_header();
++	}
++	else if (have_header_template())
++	{
++		template_display_header();
++	}
++
++	q_serverinfo.length = htons(q_serverinfo.length);
++	h2_serverinfo.length = htons(h2_serverinfo.length);
++	q_player.length = htons(q_player.length);
++
++	do_work();
++
++	finish_output();
++	free_server_hash();
++	free(files);
++	free(connmap);
++
++	return 0;
++}
++
++void finish_output()
++{
++	int i;
++	hcache_update_file();
++
++	if (progress)
++	{
++		display_progress();
++		fputs("\n", stderr);
++	}
++
++	if (server_sort)
++	{
++		struct qserver **array, *server, *next_server;
++		if (strchr(sort_keys, 'l') && strpbrk(sort_keys, SUPPORTED_SERVER_SORT) == NULL)
++		{
++			server = servers;
++			for (; server; server = next_server)
++			{
++				next_server = server->next;
++				display_server(server);
++			}
++		}
++		else
++		{
++			array = (struct qserver **)malloc(sizeof(struct qserver*) * num_servers_total);
++			server = servers;
++			for (i = 0; server != NULL; i++)
++			{
++				array[i] = server;
++				server = server->next;
++			}
++			sort_servers(array, num_servers_total);
++			if (progress)
++			{
++				fprintf(stderr, "\n");
++			}
++			for (i = 0; i < num_servers_total; i++)
++			{
++				display_server(array[i]);
++			}
++			free(array);
++		}
++	}
++	else
++	{
++		struct qserver *server, *next_server;
++		server = servers;
++		for (; server; server = next_server)
++		{
++			next_server = server->next;
++			if (server->server_name == HOSTNOTFOUND)
++			{
++				display_server(server);
++			}
++		}
++	}
++
++	if (xml_display)
++	{
++		xml_footer();
++	}
++	else if (have_trailer_template())
++	{
++		template_display_trailer();
++	}
++
++	if (OF != stdout)
++	{
++		fclose(OF);
++	}
++}
++
++
++void add_file(char *filename)
++{
++	FILE *file;
++	char name[200], *comma, *query_arg = NULL;
++	server_type *type;
++	debug(4, "Loading servers from '%s'...\n", filename);
++
++	if (strcmp(filename, "-") == 0)
++	{
++		file = stdin;
++		current_filename = NULL;
++	}
++	else
++	{
++		file = fopen(filename, "r");
++		current_filename = filename;
++	}
++	current_fileline = 1;
++
++	if (file == NULL)
++	{
++		perror(filename);
++		return ;
++	}
++	for (; fscanf(file, "%s", name) == 1; current_fileline++)
++	{
++		comma = strchr(name, ',');
++		if (comma)
++		{
++			*comma++ = '\0';
++			query_arg = strdup(comma);
++		}
++		type = find_server_type_string(name);
++		if (type == NULL)
++		{
++			add_qserver(name, default_server_type, NULL, NULL);
++		}
++		else if (fscanf(file, "%s", name) == 1)
++		{
++			if (type->flags &TF_QUERY_ARG && comma && *query_arg)
++			{
++				add_qserver(name, type, NULL, query_arg);
++			}
++			else
++			{
++				add_qserver(name, type, NULL, NULL);
++			}
++		}
++	}
++
++	if (file != stdin)
++	{
++		fclose(file);
++	}
++	debug(4, "Loaded servers from '%s'\n", filename);
++
++	current_fileline = 0;
++}
++
++void print_file_location()
++{
++	if (current_fileline != 0)
++	{
++		fprintf(stderr, "%s:%d: ", current_filename ? current_filename : "<stdin>", current_fileline);
++	}
++}
++
++void parse_query_params(struct qserver *server, char *params)
++{
++	char *comma, *arg = params;
++	do
++	{
++		comma = strchr(arg, ',');
++		if (comma)
++		{
++			*comma = '\0';
++		} if (strchr(arg, '='))
++		{
++			add_query_param(server, arg);
++		}
++		else if (strcmp(arg, "noportoffset") == 0 || strcmp(arg, "qp") == 0)
++		{
++			server->flags |= TF_NO_PORT_OFFSET;
++		}
++		else if (strcmp(arg, "showgameport") == 0 || strcmp(arg, "gp") == 0)
++		{
++			server->flags |= TF_SHOW_GAME_PORT;
++		}
++		arg = comma + 1;
++	}
++	while (comma);
++}
++
++int add_qserver(char *arg, server_type *type, char *outfilename, char *query_arg)
++{
++	struct qserver *server, *prev_server;
++	int flags = 0;
++	char *colon = NULL, *arg_copy, *hostname = NULL;
++	unsigned int ipaddr;
++	unsigned short port, port_max;
++	int portrange = 0;
++	unsigned colonpos = 0;
++
++	debug(4, "%s, %s, %s, %s\n", arg, (NULL != type) ? type->type_string : "unknown", outfilename, query_arg);
++
++	if (run_timeout && time(0) - start_time >= run_timeout)
++	{
++		finish_output();
++		exit(0);
++	}
++
++	port = port_max = type->default_port;
++
++	if (outfilename && strcmp(outfilename, "-") != 0)
++	{
++		FILE *outfile = fopen(outfilename, "r+");
++		if (outfile == NULL && (errno == EACCES || errno == EISDIR || errno == ENOSPC || errno == ENOTDIR))
++		{
++			perror(outfilename);
++			return -1;
++		}
++		if (outfile)
++		{
++			fclose(outfile);
++		}
++	}
++
++	arg_copy = strdup(arg);
++
++	colon = strchr(arg, ':');
++	if (colon != NULL)
++	{
++		if (sscanf(colon + 1, "%hu-%hu", &port, &port_max) == 2)
++		{
++			portrange = 1;
++		}
++		else
++		{
++			port_max = port;
++		}
++		*colon = '\0';
++		colonpos = colon - arg;
++	}
++
++	if (*arg == '+')
++	{
++		flags |= FLAG_BROADCAST;
++		arg++;
++	}
++
++	ipaddr = inet_addr(arg);
++	if (ipaddr == INADDR_NONE)
++	{
++		if (strcmp(arg, "255.255.255.255") != 0)
++		{
++			ipaddr = htonl(hcache_lookup_hostname(arg));
++		}
++	}
++	else if (hostname_lookup && !(flags &FLAG_BROADCAST))
++	{
++		hostname = hcache_lookup_ipaddr(ntohl(ipaddr));
++	}
++
++	if ((ipaddr == INADDR_NONE || ipaddr == 0) && strcmp(arg, "255.255.255.255") != 0)
++	{
++		if (show_errors)
++		{
++			print_file_location();
++			fprintf(stderr, "%s: %s\n", arg, strherror(h_errno));
++		}
++		server = (struct qserver*)calloc(1, sizeof(struct qserver));
++		// NOTE: 0 != port to prevent infinite loop due to lack of range on unsigned short
++		for (; port <= port_max && 0 != port; ++port)
++		{
++			init_qserver(server, type);
++			if (portrange)
++			{
++				server->arg = (port == port_max) ? arg_copy : strdup(arg_copy);
++				// NOTE: arg_copy and therefore server->arg will always have enough space as it was a port range
++				sprintf(server->arg + colonpos + 1, "%hu", port);
++			}
++			else
++			{
++				server->arg = arg_copy;
++			}
++			server->server_name = HOSTNOTFOUND;
++			server->error = strdup(strherror(h_errno));
++			server->orig_port = server->query_port = server->port = port;
++			if (last_server != &servers)
++			{
++				prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++				server->prev = prev_server;
++			}
++			*last_server = server;
++			last_server = &server->next;
++			if (one_server_type_id == ~MASTER_SERVER)
++			{
++				one_server_type_id = type->id;
++			}
++			else if (one_server_type_id != type->id)
++			{
++				one_server_type_id = 0;
++			}
++		}
++		return -1;
++	}
++
++	// NOTE: 0 != port to prevent infinite loop due to lack of range on unsigned short
++	for (; port <= port_max && 0 != port; ++port)
++	{
++		if (noserverdups && find_server_by_address(ipaddr, port) != NULL)
++		{
++			continue;
++		}
++
++		server = (struct qserver*)calloc(1, sizeof(struct qserver));
++		server->arg = port == port_max ? arg_copy : strdup(arg_copy);
++		if (portrange)
++		{
++			sprintf(server->arg + colonpos + 1, "%hu", port);
++		}
++		if (hostname && colon)
++		{
++			server->host_name = (char*)malloc(strlen(hostname) + 5+2);
++			sprintf(server->host_name, "%s:%hu", hostname, port);
++		}
++		else
++		{
++			server->host_name = strdup((hostname) ? hostname : arg);
++		}
++
++		server->ipaddr = ipaddr;
++		server->orig_port = server->query_port = server->port = port;
++		server->type = type;
++		server->outfilename = outfilename;
++		server->flags = flags;
++		server->state = STATE_INIT;
++		if (query_arg)
++		{
++			server->query_arg = (port == port_max) ? query_arg : strdup(query_arg);
++			parse_query_params(server, server->query_arg);
++		}
++		else
++		{
++			server->query_arg = NULL;
++		}
++		init_qserver(server, type);
++
++		if (server->type->master)
++		{
++			waiting_for_masters++;
++		}
++
++		if (num_servers_total % 10 == 0)
++		{
++			hcache_update_file();
++		}
++
++		if (last_server != &servers)
++		{
++			prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++			server->prev = prev_server;
++		}
++		*last_server = server;
++		last_server = &server->next;
++
++		add_server_to_hash(server);
++
++		if (one_server_type_id == ~MASTER_SERVER)
++		{
++			one_server_type_id = type->id;
++		}
++		else if (one_server_type_id != type->id)
++		{
++			one_server_type_id = 0;
++		}
++
++		++num_servers;
++	}
++	return 0;
++}
++
++struct qserver *add_qserver_byaddr(unsigned int ipaddr, unsigned short port, server_type *type, int *new_server)
++{
++	char arg[36];
++	struct qserver *server, *prev_server;
++	char *hostname = NULL;
++
++	if (run_timeout && time(0) - start_time >= run_timeout)
++	{
++		finish_output();
++		exit(0);
++	}
++
++	if (new_server)
++	{
++		*new_server = 0;
++	}
++	ipaddr = htonl(ipaddr);
++	if (ipaddr == 0)
++	{
++		return 0;
++	}
++
++	// TODO: this prevents servers with the same ip:port being queried
++	// and hence breaks virtual servers support e.g. Teamspeak 2
++	if (find_server_by_address(ipaddr, port) != NULL)
++	{
++		return 0;
++	}
++
++	if (new_server)
++	{
++		*new_server = 1;
++	}
++
++	server = (struct qserver*)calloc(1, sizeof(struct qserver));
++	server->ipaddr = ipaddr;
++	ipaddr = ntohl(ipaddr);
++	sprintf(arg, "%d.%d.%d.%d:%hu", ipaddr >> 24, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
++	server->arg = strdup(arg);
++
++	if (hostname_lookup)
++	{
++		hostname = hcache_lookup_ipaddr(ipaddr);
++	}
++
++	if (hostname)
++	{
++		server->host_name = (char*)malloc(strlen(hostname) + 6+2);
++		sprintf(server->host_name, "%s:%hu", hostname, port);
++	}
++	else
++	{
++		server->host_name = strdup(arg);
++	}
++
++	server->orig_port = server->query_port = server->port = port;
++	init_qserver(server, type);
++
++	if (num_servers_total % 10 == 0)
++	{
++		hcache_update_file();
++	}
++
++	if (last_server != &servers)
++	{
++		prev_server = (struct qserver*)((char*)last_server - ((char*) &server->next - (char*)server));
++		server->prev = prev_server;
++	}
++	*last_server = server;
++	last_server = &server->next;
++
++	add_server_to_hash(server);
++
++	++num_servers;
++
++	return server;
++}
++
++void add_servers_from_masters()
++{
++	struct qserver *server;
++	unsigned int ipaddr, i;
++	unsigned short port;
++	int n_servers, new_server, port_adjust = 0;
++	char *pkt;
++	server_type *server_type;
++	FILE *outfile;
++
++	for (server = servers; server != NULL; server = server->next)
++	{
++		if (!server->type->master || server->master_pkt == NULL)
++		{
++			continue;
++		}
++		pkt = server->master_pkt;
++
++		if (server->query_arg && server->type->id == GAMESPY_MASTER)
++		{
++			server_type = find_server_type_string(server->query_arg);
++			if (server_type == NULL)
++			{
++				server_type = find_server_type_id(server->type->master);
++			}
++		}
++		else
++		{
++			server_type = find_server_type_id(server->type->master);
++		}
++
++		if (server->type->id == GAMESPY_MASTER && server_type)
++		{
++			if (server_type->id == UN_SERVER)
++			{
++				port_adjust = - 1;
++			}
++			else if (server_type->id == KINGPIN_SERVER)
++			{
++				port_adjust = 10;
++			}
++		}
++
++		outfile = NULL;
++		if (server->outfilename)
++		{
++			if (strcmp(server->outfilename, "-") == 0)
++			{
++				outfile = stdout;
++			}
++			else
++			{
++				outfile = fopen(server->outfilename, "w");
++			}
++			if (outfile == NULL)
++			{
++				perror(server->outfilename);
++				continue;
++			}
++		}
++		n_servers = 0;
++		for (i = 0; i < server->master_pkt_len; i += 6)
++		{
++			memcpy(&ipaddr, &pkt[i], 4);
++			memcpy(&port, &pkt[i + 4], 2);
++			ipaddr = ntohl(ipaddr);
++			port = ntohs(port) + port_adjust;
++			new_server = 1;
++			if (outfile)
++			{
++				fprintf(outfile, "%s %d.%d.%d.%d:%hu\n",
++					server_type ? server_type->type_string: "",
++					(ipaddr >> 24) &0xff,
++					(ipaddr >> 16) &0xff,
++					(ipaddr >> 8) &0xff,
++					ipaddr &0xff, port
++				);
++			}
++			else if (server_type == NULL)
++			{
++				fprintf(OF, "%d.%d.%d.%d:%hu\n", (ipaddr >> 24) &0xff, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
++			}
++			else
++			{
++				add_qserver_byaddr(ipaddr, port, server_type, &new_server);
++			}
++			n_servers += new_server;
++		}
++		free(server->master_pkt);
++		server->master_pkt = NULL;
++		server->master_pkt_len = 0;
++		server->n_servers = n_servers;
++		if (outfile)
++		{
++			fclose(outfile);
++		}
++	}
++	if (hostname_lookup)
++	{
++		hcache_update_file();
++	}
++	bind_sockets();
++}
++
++void init_qserver(struct qserver *server, server_type *type)
++{
++	server->server_name = NULL;
++	server->map_name = NULL;
++	server->game = NULL;
++	server->num_players = 0;
++	server->num_spectators = 0;
++	server->fd = -1;
++	server->state = STATE_INIT;
++	if (server->flags &FLAG_BROADCAST)
++	{
++		server->retry1 = 1;
++		server->retry2 = 1;
++	}
++	else
++	{
++		server->retry1 = n_retries;
++		server->retry2 = n_retries;
++	}
++	server->n_retries = 0;
++	server->ping_total = 0;
++	server->n_packets = 0;
++	server->n_requests = 0;
++
++	server->n_servers = 0;
++	server->master_pkt_len = 0;
++	server->master_pkt = NULL;
++	server->error = NULL;
++
++	server->saved_data.data = NULL;
++	server->saved_data.datalen = 0;
++	server->saved_data.pkt_index = -1;
++	server->saved_data.pkt_max = 0;
++	server->saved_data.next = NULL;
++
++	server->type = type;
++	server->next_rule = (get_server_rules) ? "" : NO_SERVER_RULES;
++	server->next_player_info = (get_player_info && type->player_packet) ? 0 : NO_PLAYER_INFO;
++
++	server->n_player_info = 0;
++	server->players = NULL;
++	server->n_rules = 0;
++	server->rules = NULL;
++	server->last_rule = &server->rules;
++	server->missing_rules = 0;
++
++	num_servers_total++;
++}
++
++// ipaddr should be network byte-order
++// port should be host byte-order
++// NOTE: This will return the first matching server, which is not nessacarily correct
++// depending on if duplicate ports are allowed
++struct qserver *find_server_by_address(unsigned int ipaddr, unsigned short port)
++{
++	struct qserver **hashed;
++	unsigned int hash, i;
++
++	if ( ! noserverdups && show_errors )
++	{
++		fprintf( stderr, "error: find_server_by_address while duplicates are allowed, this is unsafe!" );
++	}
++
++	hash = (ipaddr + port) % ADDRESS_HASH_LENGTH;
++
++	if (ipaddr == 0)
++	{
++		for (hash = 0; hash < ADDRESS_HASH_LENGTH; hash++)
++		{
++			printf("%3d %d\n", hash, server_hash_len[hash]);
++		}
++		return NULL;
++	}
++
++	hashed = server_hash[hash];
++	for (i = server_hash_len[hash]; i; i--, hashed++)
++	{
++		if (*hashed && (*hashed)->ipaddr == ipaddr && (*hashed)->port == port)
++		{
++			return *hashed;
++		}
++	}
++	return NULL;
++}
++
++void add_server_to_hash(struct qserver *server)
++{
++	unsigned int hash;
++	hash = (server->ipaddr + server->port) % ADDRESS_HASH_LENGTH;
++
++	if (server_hash_len[hash] % 16 == 0)
++	{
++		server_hash[hash] = (struct qserver **)realloc(server_hash[hash], sizeof(struct qserver **)*(server_hash_len[hash] + 16));
++		memset(&server_hash[hash][server_hash_len[hash]], 0, sizeof(struct qserver **) * 16);
++	}
++	server_hash[hash][server_hash_len[hash]] = server;
++	server_hash_len[hash]++;
++}
++
++void remove_server_from_hash(struct qserver *server)
++{
++	struct qserver **hashed;
++	unsigned int hash, i, ipaddr = server->ipaddr;
++	unsigned short port = server->orig_port;
++	hash = ( ipaddr + port ) % ADDRESS_HASH_LENGTH;
++
++	hashed = server_hash[hash];
++	for (i = server_hash_len[hash]; i; i--, hashed++)
++	{
++		// NOTE: we use direct pointer checks here to prevent issues with duplicate port servers e.g. teamspeak 2 and 3
++		if ( *hashed == server )
++		{
++			*hashed = NULL;
++			break;
++		}
++	}
++}
++
++void free_server_hash()
++{
++	int i;
++	for (i = 0; i < ADDRESS_HASH_LENGTH; i++)
++	{
++		if (server_hash[i])
++		{
++			free(server_hash[i]);
++		}
++	}
++}
++
++
++/*
++ * Functions for binding sockets to Quake servers
++ */
++int bind_qserver_post(struct qserver *server)
++{
++	server->state = STATE_CONNECTED;
++
++	if (server->type->flags &TF_TCP_CONNECT)
++	{
++		int one = 1;
++		if ( -1 == setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)) )
++		{
++			perror( "Failed to set TCP no delay" );
++		}
++	}
++
++	if ( server->type->id & MASTER_SERVER )
++	{
++		// Use a large buffer so we dont miss packets
++		int sockbuf = RECV_BUF;
++		if ( -1 == setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF, (void*)&sockbuf, sizeof(sockbuf)) )
++		{
++			perror( "Failed to set socket buffer" );
++		}
++	}
++
++#ifndef _WIN32
++	if (server->fd >= max_connmap)
++	{
++		int old_max = max_connmap;
++		max_connmap = server->fd + 32;
++		connmap = (struct qserver **)realloc(connmap, max_connmap *sizeof(struct qserver*));
++		memset(&connmap[old_max], 0, (max_connmap - old_max) *sizeof(struct qserver*));
++	}
++	connmap[server->fd] = server;
++#endif
++#ifdef _WIN32
++	{
++		int i;
++		for (i = 0; i < max_connmap; i++)
++		{
++			if (connmap[i] == NULL)
++			{
++				connmap[i] = server;
++				break;
++			}
++		}
++		if (i >= max_connmap)
++		{
++			printf("could not put server in connmap\n");
++		}
++	}
++#endif
++
++	return 0;
++}
++
++void add_ms_to_timeval(struct timeval *a, unsigned long interval_ms, struct timeval *result)
++{
++    result->tv_sec = a->tv_sec + (interval_ms / 1000);
++    result->tv_usec = a->tv_usec + ((interval_ms % 1000) * 1000);
++    if (result->tv_usec > 1000000)
++	{
++        result->tv_usec -= 1000000;
++        result->tv_sec++;
++    }
++}
++
++void qserver_sockaddr(struct qserver *server, struct sockaddr_in *addr)
++{
++	addr->sin_family = AF_INET;
++	addr->sin_port = (no_port_offset || server->flags &TF_NO_PORT_OFFSET) ?
++		htons(server->port) :
++		htons((unsigned short)(server->port + server->type->port_offset));
++	addr->sin_addr.s_addr = server->ipaddr;
++	memset(&(addr->sin_zero), 0, sizeof(addr->sin_zero));
++}
++
++
++int connected_qserver(struct qserver *server, int polling)
++{
++	struct sockaddr_in addr;
++	char error[50];
++	int ret;
++	struct timeval tv, now, to;
++	fd_set connect_set;
++
++	error[0] = '\0';
++	gettimeofday(&now, NULL);
++	add_ms_to_timeval(&server->packet_time1, retry_interval * server->retry1, &to);
++
++	if (polling)
++	{
++		// No delay
++		tv.tv_sec = 0;
++		tv.tv_usec = 0;
++	}
++	else
++	{
++		// Wait until the server would timeout
++		tv.tv_sec = to.tv_sec;
++		tv.tv_usec = to.tv_usec;
++	}
++
++	while( 1 )
++	{
++		FD_ZERO( &connect_set ); 
++		FD_SET( server->fd, &connect_set );
++
++		// NOTE: We may need to check exceptfds here on windows instead of writefds
++		ret = select( server->fd + 1, NULL, &connect_set, NULL, &tv );
++		if ( 0 == ret )
++		{ 
++			// Time limit expired
++			if (polling)
++			{
++				// Check if we have run out of time to connect
++				gettimeofday(&now, NULL);
++				if (0 < time_delta(&to, &now))
++				{
++					// We still have time to connect
++					return -2;
++				}
++			}
++
++			qserver_sockaddr(server, &addr);
++			sprintf(error, "connect:%s:%u - timeout", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++			server->server_name = TIMEOUT;
++			server->state = STATE_TIMEOUT;
++			goto connect_error;
++		} 
++		else if ( 0 < ret )
++		{ 
++			// Socket selected for write so either connected or error
++			int sockerr, orig_errno;
++			unsigned int lon = sizeof(int); 
++
++			orig_errno = errno;
++			if ( 0 != getsockopt( server->fd, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &lon) )
++			{ 
++				// Restore the original error
++				errno = orig_errno;
++				goto connect_error;
++			} 
++
++			if (sockerr)
++			{ 
++				// set the real error
++				errno = sockerr;
++				goto connect_error;
++			}
++
++			// Connection success
++			break; 
++		}
++		else
++		{
++			// select failed
++			if ( errno != EINTR )
++			{ 
++				goto connect_error;
++			}
++		} 
++	}
++
++
++	return bind_qserver_post(server);
++
++connect_error:
++	if (STATE_CONNECTING == server->state)
++	{
++		// Default error case
++		if (0 == strlen(error))
++		{
++			qserver_sockaddr(server, &addr);
++			sprintf(error, "connect: %s:%u", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++		}
++		server->server_name = SYSERROR;
++		server->state = STATE_SYS_ERROR;
++	}
++
++	if ( show_errors )
++	{
++		perror(error);
++	}
++	close(server->fd);
++	server->fd = -1;
++	connected--;
++
++	return -1;
++}
++
++int bind_qserver2(struct qserver *server, int wait)
++{
++	struct sockaddr_in addr;
++	static int one = 1;
++
++	debug(1, "start %p @ %d.%d.%d.%d:%hu\n",
++		server,
++		server->ipaddr &0xff,
++		(server->ipaddr >> 8) &0xff,
++		(server->ipaddr >> 16) &0xff,
++		(server->ipaddr >> 24) &0xff,
++		server->port
++	);
++
++	if (server->type->flags &TF_TCP_CONNECT)
++	{
++		server->fd = socket(AF_INET, SOCK_STREAM, 0);
++	}
++	else
++	{
++		server->fd = socket(AF_INET, SOCK_DGRAM, 0);
++	}
++
++	if (server->fd == INVALID_SOCKET)
++	{
++		if (sockerr() == EMFILE)
++		{
++			// Per process descriptor table is full - retry
++			server->fd = -1;
++			return -2;
++		}
++
++		perror("socket");
++		server->server_name = SYSERROR;
++		server->state = STATE_SYS_ERROR;
++		return -1;
++	}
++
++	addr.sin_family = AF_INET;
++	addr.sin_addr.s_addr = htonl(source_ip);
++	if (server->type->id == Q2_MASTER)
++	{
++		addr.sin_port = htons(26500);
++	}
++	else if (source_port == 0)
++	{
++		addr.sin_port = 0;
++	}
++	else
++	{
++		addr.sin_port = htons(source_port);
++		source_port++;
++		if (source_port > source_port_high)
++		{
++			source_port = source_port_low;
++		}
++	}
++	memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
++
++	if (bind(server->fd, (struct sockaddr*) &addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
++	{
++		perror("bind");
++		server->server_name = SYSERROR;
++		server->state = STATE_SYS_ERROR;
++		close(server->fd);
++		server->fd = -1;
++		return -1;
++	}
++
++	if (server->flags &FLAG_BROADCAST)
++	{
++		if ( -1 == setsockopt(server->fd, SOL_SOCKET, SO_BROADCAST, (char*) &one, sizeof(one)) )
++		{
++			perror( "Failed to set broadcast" );
++			server->server_name = SYSERROR;
++			server->state = STATE_SYS_ERROR;
++			close(server->fd);
++			server->fd = -1;
++			return -1;
++		}
++	}
++
++	// we need nonblocking always. poll on an udp socket would wake
++	// up and recv blocks if a packet with incorrect checksum is
++	// received
++	set_non_blocking(server->fd);
++
++	if (server->type->id != Q2_MASTER && !(server->flags &FLAG_BROADCAST))
++	{
++
++		if ( server->type->flags & TF_TCP_CONNECT )
++		{
++			// TCP set packet_time1 so it can be used for ping calculations for protocols with an initial response
++			gettimeofday( &server->packet_time1, NULL );
++		}
++
++		qserver_sockaddr(server, &addr);
++		server->state = STATE_CONNECTING;
++
++		if (connect(server->fd, (struct sockaddr*) &addr, sizeof(addr)) == SOCKET_ERROR)
++		{
++			if ( connection_inprogress() )
++			{
++				int ret;
++
++				// Ensure we don't detect the same error twice, specifically on a different server
++				clear_socketerror();
++
++				if ( ! wait )
++				{
++					debug(2, "connect:%s:%u - in progress", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++					return -3;
++				}
++				ret = connected_qserver( server, 0 );
++				if (0 != ret)
++				{
++					return ret;
++				}
++			}
++			else
++			{
++				if ( show_errors )
++				{
++					char error[50];
++					sprintf(error, "connect:%s:%u", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
++					perror(error);
++				}
++				server->server_name = SYSERROR;
++				server->state = STATE_SYS_ERROR;
++				close(server->fd);
++				server->fd = -1;
++				return -1;
++			}
++		}
++	}
++
++	return bind_qserver_post(server);
++}
++
++
++int bind_qserver(struct qserver *server)
++{
++	return bind_qserver2(server, 1);
++}
++
++static struct timeval t_lastsend = { 0, 0 };
++
++int bind_sockets()
++{
++	struct qserver *server, *next_server, *first_server, *last_server;
++	struct timeval now;
++	int rc, retry_count = 0, inprogress = 0;
++
++	gettimeofday(&now, NULL);
++	if (connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
++	{
++		server = NULL;
++	}
++	else if (!waiting_for_masters)
++	{
++		if (last_server_bind == NULL)
++		{
++			last_server_bind = servers;
++		}
++		server = last_server_bind;
++	}
++	else
++	{
++		server = servers;
++	}
++
++	first_server = server;
++
++	for (; server != NULL && connected < max_simultaneous; )
++	{
++		// note the next server for use as process_func can free the server
++		next_server = server->next;
++		if (server->server_name == NULL && server->fd == -1)
++		{
++			if (waiting_for_masters && !server->type->master)
++			{
++				server = next_server;
++				continue;
++			}
++
++			if ((rc = bind_qserver2(server, syncconnect ? 1 : 0)) == 0)
++			{
++				debug(1, "send %d.%d.%d.%d:%hu\n",
++					server->ipaddr &0xff,
++					(server->ipaddr >> 8) &0xff,
++					(server->ipaddr >> 16) &0xff,
++					(server->ipaddr >> 24) &0xff,
++					server->port
++				);
++
++				gettimeofday(&t_lastsend, NULL);
++				debug(2, "calling status_query_func for %p - connect", server);
++				process_func_ret( server, server->type->status_query_func(server) );
++
++				connected++;
++				if (!waiting_for_masters)
++				{
++					last_server_bind = server;
++				}
++				break;
++			}
++			else if (rc == -3)
++			{
++				// Connect in progress
++
++				// We add to increment connected as we need to know the total
++				// amount of connections in progress not just those that have
++				// successfuly completed their connection otherwise we could
++				// blow FD_SETSIZE
++				connected++;
++				inprogress++;
++			}
++			else if (rc == -2 && ++retry_count > 2)
++			{
++				return -2;
++			}
++			else if (-1 == rc)
++			{
++				cleanup_qserver(server, FORCE);
++			}
++		}
++
++		server = next_server;
++	}
++
++	// Wait for all connections to succeed or fail
++	last_server = server;
++	while (inprogress)
++	{
++		inprogress = 0;
++		server = first_server;
++		for (; server != last_server;)
++		{
++			next_server = server->next;
++			if (STATE_CONNECTING == server->state)
++			{
++				rc = connected_qserver(server, 1);
++				switch(rc)
++				{
++				case 0:
++					// Connected
++					gettimeofday(&t_lastsend, NULL);
++					debug(2, "calling status_query_func for %p - in progress", server);
++					process_func_ret( server, server->type->status_query_func(server) );
++
++					// NOTE: connected is already incremented
++					if (!waiting_for_masters)
++					{
++						last_server_bind = server;
++					}
++					break;
++				case -2:
++					// In progress
++					inprogress++;
++					break;
++				case -1:
++					cleanup_qserver(server, FORCE);
++					break;
++				}
++			}
++			server = next_server;
++		}
++	}
++
++	if (NULL != last_server || (!connected && retry_count))
++	{
++		// Retry later, more to process
++		return -2;
++	}
++
++	return 0;
++}
++
++int process_func_ret( struct qserver *server, int ret )
++{
++	debug( 3, "%p, %d", server, ret );
++	switch ( ret )
++	{
++	case INPROGRESS:
++		return ret;
++
++	case DONE_AUTO:
++		cleanup_qserver( server, NO_FORCE );
++		return ret;
++
++	case DONE_FORCE:
++	case SYS_ERROR:
++	case MEM_ERROR:
++	case PKT_ERROR:
++	case ORD_ERROR:
++	case REQ_ERROR:
++		cleanup_qserver( server, FORCE );
++		return ret;
++	}
++
++	debug(0, "unhandled return code %d, please report!", ret);
++
++	return ret;
++}
++
++
++/*
++ * Functions for sending packets
++ */
++// this is so broken, someone please rewrite the timeout handling
++void send_packets()
++{
++	struct qserver *server;
++	struct timeval now;
++	int interval, n_sent = 0, prev_n_sent;
++	unsigned i;
++	debug(3, "processing...");
++
++	gettimeofday(&now, NULL);
++
++	if (!t_lastsend.tv_sec)
++	{
++		// nothing
++	}
++	else if (connected && sendinterval && time_delta(&now, &t_lastsend) < sendinterval)
++	{
++		return ;
++	}
++
++	for (i = 0; i < max_connmap; ++i)
++	{
++		server = connmap[i];
++		if (!server)
++		{
++			continue;
++		}
++
++		if (server->fd == -1)
++		{
++			debug(0, "invalid entry in connmap\n");
++		}
++
++		if (server->type->id &MASTER_SERVER)
++		{
++			interval = master_retry_interval;
++		}
++		else
++		{
++			interval = retry_interval;
++		}
++
++		debug(2, "server %p, name %s, retry1 %d, next_rule %p, next_player_info %d, num_players %d, n_retries %d",
++			server,
++			server->server_name,
++			server->retry1,
++			server->next_rule,
++			server->next_player_info,
++			server->num_players,
++			n_retries
++		);
++		prev_n_sent = n_sent;
++		if (server->server_name == NULL)
++		{
++			// We havent seen the server yet?
++			if (server->retry1 != n_retries && time_delta(&now, &server->packet_time1) < (interval *(n_retries - server->retry1 + 1)))
++			{
++				continue;
++			}
++
++			if (!server->retry1)
++			{
++				// No more retries
++				cleanup_qserver( server, FORCE );
++				continue;
++			}
++
++			if ( qserver_get_timeout(server, &now) <= 0 && ! ( server->type->flags & TF_TCP_CONNECT ) )
++			{
++				// Query status
++				debug(2, "calling status_query_func for %p", server);
++				process_func_ret( server, server->type->status_query_func(server) );
++				gettimeofday(&t_lastsend, NULL);
++				n_sent++;
++				continue;
++			}
++		}
++
++		if (server->next_rule != NO_SERVER_RULES)
++		{
++			// We want server rules
++			if (server->retry1 != n_retries && time_delta(&now, &server->packet_time1) < (interval *(n_retries - server->retry1 + 1)))
++			{
++				continue;
++			}
++
++			if (!server->retry1)
++			{
++				// no more retries
++				server->next_rule = NULL;
++				server->missing_rules = 1;
++				cleanup_qserver( server, NO_FORCE );
++				continue;
++			}
++			send_rule_request_packet(server);
++			gettimeofday(&t_lastsend, NULL);
++			n_sent++;
++		}
++
++		if (server->next_player_info < server->num_players)
++		{
++			// Expecting player details
++			if (server->retry2 != n_retries && time_delta(&now, &server->packet_time2) < (interval *(n_retries - server->retry2 + 1)))
++			{
++				continue;
++			}
++			if (!server->retry2)
++			{
++				server->next_player_info++;
++				if (server->next_player_info >= server->num_players)
++				{
++					// no more retries
++					cleanup_qserver( server, FORCE );
++					continue;
++				}
++				server->retry2 = n_retries;
++			}
++			send_player_request_packet(server);
++			gettimeofday(&t_lastsend, NULL);
++			n_sent++;
++		}
++
++		if (prev_n_sent == n_sent)
++		{
++			// we didnt send any additional queries
++			debug(2, "no queries sent: %d %d", time_delta(&now, &server->packet_time1), (interval *(n_retries + 1)));
++			if (!server->retry1)
++			{
++				// no retries left
++				if (time_delta(&now, &server->packet_time1) > (interval *(n_retries + 1)))
++				{
++					cleanup_qserver( server, FORCE );
++				}
++			}
++			else
++			{
++				// decrement as we didnt send any packets
++				server->retry1--;
++			}
++		}
++	}
++
++	debug(3, "done");
++}
++
++/* server starts sending data immediately, so we need not do anything */
++query_status_t send_bfris_request_packet(struct qserver *server)
++{
++	return register_send(server);
++}
++
++
++/* First packet for a normal Quake server
++ */
++query_status_t send_qserver_request_packet(struct qserver *server)
++{
++	return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++/* First packet for a QuakeWorld server
++ */
++query_status_t send_qwserver_request_packet(struct qserver *server)
++{
++	int rc;
++
++	if (server->flags &FLAG_BROADCAST)
++	{
++		rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
++	}
++	else if (server->server_name == NULL)
++	{
++		rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
++	}
++	else if (server->server_name != NULL && server->type->rule_packet)
++	{
++		rc = send(server->fd, server->type->rule_packet, server->type->rule_len, 0);
++	}
++	else
++	{
++		rc = SOCKET_ERROR;
++	}
++
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++	if (server->retry1 == n_retries || server->flags &FLAG_BROADCAST)
++	{
++		gettimeofday(&server->packet_time1, NULL);
++		server->n_requests++;
++	}
++	else if (server->server_name == NULL)
++	{
++		server->n_retries++;
++	}
++	server->retry1--;
++	if (server->server_name == NULL)
++	{
++		server->n_packets++;
++	}
++	server->next_player_info = NO_PLAYER_INFO; // we don't have a player packet
++
++	return INPROGRESS;
++}
++
++// First packet for an Unreal Tournament 2003 server
++query_status_t send_ut2003_request_packet(struct qserver *server)
++{
++	int ret = send_packet(server, server->type->status_packet, server->type->status_len);
++	server->next_player_info = NO_PLAYER_INFO;
++
++	return ret;
++}
++
++// First packet for an Half-Life 2 server
++query_status_t send_hl2_request_packet(struct qserver *server)
++{
++	return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++/* First packet for an Unreal master
++ */
++query_status_t send_unrealmaster_request_packet(struct qserver *server)
++{
++	return send_packet(server, server->type->status_packet, server->type->status_len);
++}
++
++static const char *steam_region[] =
++{
++	"US East Coast", "US West Coast", "South America", "Europe", "Asia", "Australia", "Middle East", "Africa", NULL
++};
++
++char *build_hlmaster_packet(struct qserver *server, int *len)
++{
++	static char packet[1600];
++	char *pkt, *r, *sep = "";
++	char *gamedir, *map, *flags;
++	int flen;
++
++	pkt = &packet[0];
++	memcpy(pkt, server->type->master_packet, *len);
++
++	if (server->type->flags &TF_MASTER_STEAM)
++	{
++		// default the region to 0xff
++		const char *regionstring = get_param_value(server, "region", NULL);
++		int region = 0xFF;
++		if (regionstring)
++		{
++			char *tmp = NULL;
++			region = strtol(regionstring, &tmp, 10);
++			if (tmp == regionstring)
++			{
++				int i = 0;
++				region = 0xFF;
++				for (; steam_region[i]; ++i)
++				{
++					if (!strcmp(regionstring, steam_region[i]))
++					{
++						region = i;
++						break;
++					}
++				}
++			}
++		}
++		*(pkt + 1) = region;
++	}
++
++	pkt += *len;
++
++	gamedir = get_param_value(server, "game", NULL);
++	if (gamedir)
++	{
++		pkt += sprintf(pkt, "\\gamedir\\%s", gamedir);
++	}
++
++	// not valid for steam?
++	map = get_param_value(server, "map", NULL);
++	if (map)
++	{
++		pkt += sprintf(pkt, "\\map\\%s", map);
++	}
++
++	// steam
++	flags = get_param_value(server, "napp", NULL);
++	if ( flags )
++	{
++		pkt += sprintf( pkt, "\\napp\\%s", flags );
++	}
++
++	// not valid for steam?
++	flags = get_param_value(server, "status", NULL);
++	r = flags;
++	while (flags && sep)
++	{
++		sep = strchr(r, ':');
++		if (sep)
++		{
++			flen = sep - r;
++		}
++		else
++		{
++			flen = strlen(r);
++		}
++
++		if (strncmp(r, "notempty", flen) == 0)
++		{
++			pkt += sprintf(pkt, "\\empty\\1");
++		}
++		else if (strncmp(r, "notfull", flen) == 0)
++		{
++			pkt += sprintf(pkt, "\\full\\1");
++		}
++		else if (strncmp(r, "dedicated", flen) == 0)
++		{
++			if (server->type->flags &TF_MASTER_STEAM)
++			{
++				pkt += sprintf(pkt, "\\type\\d");
++			}
++			else
++			{
++				pkt += sprintf(pkt, "\\dedicated\\1");
++			}
++		}
++		else if (strncmp(r, "linux", flen) == 0)
++		{
++			pkt += sprintf(pkt, "\\linux\\1");
++		}
++		else if (strncmp(r, "proxy", flen) == 0)
++		// steam
++		{
++			pkt += sprintf(pkt, "\\proxy\\1");
++		}
++		else if (strncmp(r, "secure", flen) == 0)
++		// steam
++		{
++			pkt += sprintf(pkt, "\\secure\\1");
++		}
++		r = sep + 1;
++	}
++
++	// always need null terminator
++	*pkt = 0x00;
++	pkt++;
++
++	*len = pkt - packet;
++
++	return packet;
++}
++
++/* First packet for a QuakeWorld master server
++ */
++query_status_t send_qwmaster_request_packet(struct qserver *server)
++{
++	int rc = 0;
++
++	server->next_player_info = NO_PLAYER_INFO;
++
++	if (server->type->id == Q2_MASTER)
++	{
++		struct sockaddr_in addr;
++		addr.sin_family = AF_INET;
++		if (no_port_offset || server->flags &TF_NO_PORT_OFFSET)
++		{
++			addr.sin_port = htons(server->port);
++		}
++		else
++		{
++			addr.sin_port = htons((unsigned short)(server->port + server->type->port_offset));
++		}
++		addr.sin_addr.s_addr = server->ipaddr;
++		memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
++		rc = sendto(server->fd, server->type->master_packet, server->type->master_len, 0, (struct sockaddr*) &addr, sizeof(addr));
++	}
++	else
++	{
++		char *packet;
++		int packet_len;
++		char query_buf[4096] = { 0 };
++
++		packet = server->type->master_packet;
++		packet_len = server->type->master_len;
++
++		if (server->type->id == HL_MASTER)
++		{
++			memcpy(server->type->master_packet + 1, server->master_query_tag, 3);
++			if (server->query_arg)
++			{
++				packet_len = server->type->master_len;
++				packet = build_hlmaster_packet(server, &packet_len);
++			}
++		}
++		else if (server->type->flags &TF_MASTER_STEAM)
++		{
++			// region
++			int tag_len = strlen(server->master_query_tag);
++			if (tag_len < 9)
++			{
++				// initial case
++				tag_len = 9;
++				strcpy(server->master_query_tag, "0.0.0.0:0");
++			}
++
++			// 1 byte packet id
++			// 1 byte region
++			// ip:port
++			// 1 byte null
++			packet_len = 2+tag_len + 1;
++
++			if (server->query_arg)
++			{
++				// build_hlmaster_packet uses server->type->master_packet
++				// as the basis so copy from server->master_query_tag
++				strcpy(server->type->master_packet + 2, server->master_query_tag);
++				packet = build_hlmaster_packet(server, &packet_len);
++			}
++			else
++			{
++				// default region
++				*(packet + 1) = 0xff;
++				memcpy(packet + 2, server->master_query_tag, tag_len);
++
++				// filter null
++				*(packet + packet_len) = 0x00;
++				packet_len++;
++			}
++		}
++		else if (server->type->flags &TF_QUERY_ARG)
++		{
++			// fill in the master protocol details
++			char *master_protocol = server->query_arg;
++			if (master_protocol == NULL)
++			{
++				master_protocol = server->type->master_protocol;
++			}
++			packet_len = sprintf(query_buf, server->type->master_packet,
++				master_protocol ? master_protocol : "",
++				server->type->master_query 	? server->type->master_query: ""
++			);
++			packet = query_buf;
++		}
++
++		rc = send(server->fd, packet, packet_len, 0);
++	}
++
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++	if (server->retry1 == n_retries)
++	{
++		gettimeofday(&server->packet_time1, NULL);
++		server->n_requests++;
++	}
++	else
++	{
++		server->n_retries++;
++	}
++	server->retry1--;
++	server->n_packets++;
++
++	return INPROGRESS;
++}
++
++
++query_status_t send_tribes_request_packet(struct qserver *server)
++{
++	return send_packet(server, server->type->player_packet, server->type->player_len);
++}
++
++query_status_t send_tribes2_request_packet(struct qserver *server)
++{
++	int rc;
++
++	if (server->flags &FLAG_BROADCAST && server->server_name == NULL)
++	{
++		rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
++	}
++	else if (server->server_name == NULL)
++	{
++		rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
++	}
++	else
++	{
++		rc = send(server->fd, server->type->player_packet, server->type->player_len, 0);
++	}
++
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++	register_send(server);
++
++	return rc;
++}
++
++query_status_t send_savage_request_packet(struct qserver *server)
++{
++	int len;
++	char *pkt;
++
++	if (get_player_info)
++	{
++		pkt = server->type->player_packet;
++		len = server->type->player_len;
++	}
++	else
++	{
++		pkt = server->type->status_packet;
++		len = server->type->status_len;
++	}
++
++	return send_packet(server, pkt, len);
++}
++
++query_status_t send_farcry_request_packet(struct qserver *server)
++{
++	int len;
++	char *pkt;
++
++	if (get_player_info)
++	{
++		pkt = server->type->player_packet;
++		len = server->type->player_len;
++	}
++	else
++	{
++		pkt = server->type->status_packet;
++		len = server->type->status_len;
++	}
++
++	return send_packet(server, pkt, len);
++}
++
++query_status_t send_tribes2master_request_packet(struct qserver *server)
++{
++	int rc;
++	unsigned char packet[1600], *pkt;
++	unsigned int len, min_players, max_players, region_mask = 0;
++	unsigned int build_version, max_bots, min_cpu, status;
++	char *game, *mission, *buddies;
++	static char *region_list[] =
++	{
++		"naeast", "nawest", "sa", "aus", "asia", "eur", NULL
++	};
++	static char *status_list[] =
++	{
++		"dedicated", "nopassword", "linux"
++	};
++
++	if (strcmp(get_param_value(server, "query", ""), "types") == 0)
++	{
++		rc = send(server->fd, tribes2_game_types_request, sizeof(tribes2_game_types_request), 0);
++		goto send_done;
++	}
++
++	memcpy(packet, server->type->master_packet, server->type->master_len);
++
++	pkt = packet + 7;
++
++	game = get_param_value(server, "game", "any");
++	len = strlen(game);
++	if (len > 255)
++	{
++		len = 255;
++	}
++	*pkt++ = len;
++	memcpy(pkt, game, len);
++	pkt += len;
++
++	mission = get_param_value(server, "mission", "any");
++	len = strlen(mission);
++	if (len > 255)
++	{
++		len = 255;
++	}
++	*pkt++ = len;
++	memcpy(pkt, mission, len);
++	pkt += len;
++
++	min_players = get_param_ui_value(server, "minplayers", 0);
++	max_players = get_param_ui_value(server, "maxplayers", 255);
++	*pkt++ = min_players;
++	*pkt++ = max_players;
++
++	region_mask = get_param_ui_value(server, "regions", 0xffffffff);
++	if (region_mask == 0)
++	{
++		char *regions = get_param_value(server, "regions", "");
++		char *r = regions;
++		char **list, *sep;
++		do
++		{
++			sep = strchr(r, ':');
++			if (sep)
++			{
++				len = sep - r;
++			}
++			else
++			{
++				len = strlen(r);
++			}
++			for (list = region_list; *list; list++)
++			if (strncasecmp(r, *list, len) == 0)
++			{
++				break;
++			}
++			if (*list)
++			{
++				region_mask |= 1 << (list - region_list);
++			}
++			r = sep + 1;
++		}
++		while (sep);
++	}
++	if (little_endian)
++	{
++		memcpy(pkt, &region_mask, 4);
++	}
++	else
++	{
++		pkt[0] = region_mask &0xff;
++		pkt[1] = (region_mask >> 8) &0xff;
++		pkt[2] = (region_mask >> 16) &0xff;
++		pkt[3] = (region_mask >> 24) &0xff;
++	}
++	pkt += 4;
++
++	build_version = get_param_ui_value(server, "build", 0);
++	/*
++	if ( build_version && build_version < 22337) {
++	packet[1]= 0;
++	build_version= 0;
++	}
++	 */
++	if (little_endian)
++	{
++		memcpy(pkt, &build_version, 4);
++	}
++	else
++	{
++		pkt[0] = build_version &0xff;
++		pkt[1] = (build_version >> 8) &0xff;
++		pkt[2] = (build_version >> 16) &0xff;
++		pkt[3] = (build_version >> 24) &0xff;
++	}
++	pkt += 4;
++
++	status = get_param_ui_value(server, "status", - 1);
++	if (status == 0)
++	{
++		char *flags = get_param_value(server, "status", "");
++		char *r = flags;
++		char **list, *sep;
++		do
++		{
++			sep = strchr(r, ':');
++			if (sep)
++			{
++				len = sep - r;
++			}
++			else
++			{
++				len = strlen(r);
++			}
++			for (list = status_list; *list; list++)
++			if (strncasecmp(r, *list, len) == 0)
++			{
++				break;
++			}
++			if (*list)
++			{
++				status |= 1 << (list - status_list);
++			}
++			r = sep + 1;
++		}
++		while (sep);
++	}
++	else if (status == -1)
++	{
++		status = 0;
++	}
++	*pkt++ = status;
++
++	max_bots = get_param_ui_value(server, "maxbots", 255);
++	*pkt++ = max_bots;
++
++	min_cpu = get_param_ui_value(server, "mincpu", 0);
++	if (little_endian)
++	{
++		memcpy(pkt, &min_cpu, 2);
++	}
++	else
++	{
++		pkt[0] = min_cpu &0xff;
++		pkt[1] = (min_cpu >> 8) &0xff;
++	}
++	pkt += 2;
++
++	buddies = get_param_value(server, "buddies", NULL);
++	if (buddies)
++	{
++		char *b = buddies, *sep;
++		unsigned int buddy, n_buddies = 0;
++		unsigned char *n_loc = pkt++;
++		do
++		{
++			sep = strchr(b, ':');
++			if (sscanf(b, "%u", &buddy))
++			{
++				n_buddies++;
++				if (little_endian)
++				{
++					memcpy(pkt, &buddy, 4);
++				}
++				else
++				{
++					pkt[0] = buddy &0xff;
++					pkt[1] = (buddy >> 8) &0xff;
++					pkt[2] = (buddy >> 16) &0xff;
++					pkt[3] = (buddy >> 24) &0xff;
++				}
++				pkt += 4;
++			}
++			b = sep + 1;
++		}
++		while (sep && n_buddies < 255);
++		*n_loc = n_buddies;
++	}
++	else
++	{
++		*pkt++ = 0;
++	}
++
++	rc = send(server->fd, (char*)packet, pkt - packet, 0);
++
++send_done:
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++	if (server->retry1 == n_retries)
++	{
++		gettimeofday(&server->packet_time1, NULL);
++		server->n_requests++;
++	}
++	else
++	{
++		server->n_retries++;
++	}
++	server->retry1--;
++	server->n_packets++;
++
++	return INPROGRESS;
++}
++
++static struct _gamespy_query_map
++{
++	char *qstat_type;
++	char *gamespy_type;
++}
++gamespy_query_map[] =
++{
++	{ "qws", "quakeworld" },
++	{ "q2s", "quake2" },
++	{ "q3s", "quake3" },
++	{ "tbs", "tribes" },
++	{ "uns", "ut" },
++	{ "sgs", "shogo" },
++	{ "hls", "halflife" },
++	{ "kps", "kingpin" },
++	{ "hrs", "heretic2" },
++	{ "sfs", "sofretail" },
++	{ NULL, NULL }
++};
++
++query_status_t send_gamespy_master_request(struct qserver *server)
++{
++	int rc, i;
++	char request[1024];
++
++	if (server->n_packets)
++	{
++		return DONE_AUTO;
++	}
++
++	// WARNING: This only works for masters which don't check the value of validate
++	// e.g. unreal.epicgames.com
++	//
++	// What we should be doing here is recieving the challenge from the master
++	// processing the secure key and then using that as the value for validate.
++	//
++	// The details of this can be seen in gslist:
++	// http://aluigi.altervista.org/papers.htm#gslist
++	rc = send(server->fd, server->type->master_packet, server->type->master_len, 0);
++	if (rc != server->type->master_len)
++	{
++		return send_error( server, rc );
++	}
++
++	strcpy(request, server->type->status_packet);
++
++	for (i = 0; gamespy_query_map[i].qstat_type; i++)
++	{
++		if (strcasecmp(server->query_arg, gamespy_query_map[i].qstat_type) == 0)
++		{
++			break;
++		}
++	}
++
++	if (gamespy_query_map[i].gamespy_type == NULL)
++	{
++		strcat(request, server->query_arg);
++	}
++	else
++	{
++		strcat(request, gamespy_query_map[i].gamespy_type);
++	}
++	strcat(request, "\\final\\");
++	assert(strlen(request) < sizeof(request));
++
++	rc = send(server->fd, request, strlen(request), 0);
++	if (rc != strlen(request))
++	{
++		return send_error( server, rc );
++	}
++
++	if (server->retry1 == n_retries)
++	{
++		gettimeofday(&server->packet_time1, NULL);
++		server->n_requests++;
++	}
++
++	server->n_packets++;
++
++	return INPROGRESS;
++}
++
++query_status_t send_rule_request_packet(struct qserver *server)
++{
++	int rc, len;
++
++	debug(3, "send_rule_request_packet: %p", server);
++
++	/* Server created via broadcast, so bind it */
++	if (server->fd == -1)
++	{
++		if (bind_qserver(server) < 0)
++		{
++			goto setup_retry;
++		}
++	}
++
++	if (server->type->rule_query_func && server->type->rule_query_func != send_rule_request_packet)
++	{
++		return server->type->rule_query_func(server);
++	}
++
++	if (server->type->id == Q_SERVER)
++	{
++		strcpy((char*)q_rule.data, server->next_rule);
++		len = Q_HEADER_LEN + strlen((char*)q_rule.data) + 1;
++		q_rule.length = htons((short)len);
++	}
++	else
++	{
++		len = server->type->rule_len;
++	}
++
++	rc = send(server->fd, (const char*)server->type->rule_packet, len, 0);
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++setup_retry:
++	if (server->retry1 == n_retries)
++	{
++		gettimeofday(&server->packet_time1, NULL);
++		server->n_requests++;
++	}
++	else if (server->server_name == NULL)
++	{
++		server->n_retries++;
++	}
++	server->retry1--;
++	if (server->server_name == NULL)
++	{
++		server->n_packets++;
++	}
++
++	return DONE_AUTO;
++}
++
++query_status_t send_player_request_packet(struct qserver *server)
++{
++	int rc;
++
++	debug( 3, "send_player_request_packet %p", server );
++
++	if (!server->type->player_packet)
++	{
++		return 0;
++	}
++
++	/* Server created via broadcast, so bind it */
++	if (server->fd == -1)
++	{
++		if (bind_qserver(server) < 0)
++		{
++			goto setup_retry;
++		}
++	}
++
++	if (server->type->player_query_func && server->type->player_query_func != send_player_request_packet)
++	{
++		return server->type->player_query_func(server);
++	}
++
++	if (!server->type->player_packet)
++	{
++		debug(0, "error: server %p has no player_packet", server);
++		return 0;
++	}
++
++	if (server->type->id == Q_SERVER)
++	{
++		q_player.data[0] = server->next_player_info;
++	}
++	rc = send(server->fd, (const char*)server->type->player_packet, server->type->player_len, 0);
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
++
++setup_retry:
++	if (server->retry2 == n_retries)
++	{
++		gettimeofday(&server->packet_time2, NULL);
++		server->n_requests++;
++	}
++	else
++	{
++		server->n_retries++;
++	}
++	server->retry2--;
++	server->n_packets++;
++
++	return 1;
++}
++
++void qserver_disconnect(struct qserver *server)
++{
++#ifdef _WIN32
++	int i;
++#endif
++	if (server->fd != - 1)
++	{
++		close(server->fd);
++#ifndef _WIN32
++		connmap[server->fd] = NULL;
++#else
++		for (i = 0; i < max_connmap; i++)
++		{
++			if (connmap[i] == server)
++			{
++				connmap[i] = NULL;
++				break;
++			}
++		}
++#endif
++		server->fd = - 1;
++		connected--;
++	}
++}
++
++/* Functions for figuring timeouts and when to give up
++ * Returns 1 if the query is done (server may be freed) and 0 if not.
++ */
++int cleanup_qserver(struct qserver *server, int force)
++{
++	int close_it = force;
++	debug( 3, "cleanup_qserver %p, %d", server, force );
++	if ( server->server_name == NULL )
++	{
++		debug(3, "server has no name, forcing close");
++		close_it = 1;
++		if (server->type->id &MASTER_SERVER && server->master_pkt != NULL)
++		{
++			server->server_name = MASTER;
++		}
++		else
++		{
++			server->server_name = TIMEOUT;
++			num_servers_timed_out++;
++		}
++	}
++	else if (server->type->flags &TF_SINGLE_QUERY)
++	{
++		debug(3, "TF_SINGLE_QUERY, forcing close");
++		close_it = 1;
++	}
++	else if (server->next_rule == NO_SERVER_RULES && server->next_player_info >= server->num_players)
++	{
++		debug(3, "no server rules and next_player_info >= num_players, forcing close");
++		close_it = 1;
++	}
++
++	debug(3, "close_it %d", close_it);
++	if (close_it)
++	{
++		if (server->saved_data.data)
++		{
++			SavedData *sdata = server->saved_data.next;
++			free(server->saved_data.data);
++			server->saved_data.data = NULL;
++			while (sdata != NULL)
++			{
++				SavedData *next;
++				free(sdata->data);
++				next = sdata->next;
++				free(sdata);
++				sdata = next;
++			}
++			server->saved_data.next = NULL;
++		}
++
++		qserver_disconnect(server);
++
++		if (server->server_name != TIMEOUT)
++		{
++			num_servers_returned++;
++			if (server->server_name != DOWN)
++			{
++				num_players_total += server->num_players;
++				max_players_total += server->max_players;
++			}
++		}
++		if (server->server_name == TIMEOUT || server->server_name == DOWN)
++		{
++			server->ping_total = 999999;
++		}
++		if (server->type->master)
++		{
++			waiting_for_masters--;
++			if (waiting_for_masters == 0)
++			{
++				add_servers_from_masters();
++			}
++		}
++		if (!server_sort)
++		{
++			display_server(server);
++		}
++		return 1;
++	}
++	return 0;
++}
++
++/** must be called only on connected servers
++ * @returns time in ms until server needs timeout handling. timeout handling is needed if <= zero
++ */
++static int qserver_get_timeout(struct qserver *server, struct timeval *now)
++{
++	int diff, diff1, diff2, interval;
++
++	if (server->type->id &MASTER_SERVER)
++	{
++		interval = master_retry_interval;
++	}
++	else
++	{
++		interval = retry_interval;
++	}
++
++	diff2 = 0xffff;
++
++	diff1 = interval *(n_retries - server->retry1 + 1) - time_delta(now, &server->packet_time1);
++
++	if (server->next_player_info < server->num_players)
++	{
++		diff2 = interval *(n_retries - server->retry2 + 1) - time_delta(now, &server->packet_time2);
++	}
++
++	debug(2, "timeout for %p is diff1 %d diff2 %d", server, diff1, diff2);
++
++
++	diff = (diff1 < diff2) ? diff1 : diff2;
++
++	return diff;
++}
++
++void get_next_timeout(struct timeval *timeout)
++{
++	struct qserver *server = servers;
++	struct timeval now;
++	int diff, smallest = retry_interval + master_retry_interval;
++	int bind_count = 0;
++
++	if (first_server_bind == NULL)
++	{
++		first_server_bind = servers;
++	}
++
++	server = first_server_bind;
++
++	for (; server != NULL && server->fd == -1; server = server->next)
++		;
++
++	/* if there are unconnected servers and slots left we retry in 10ms */
++	if (server == NULL || (num_servers > connected && connected < max_simultaneous))
++	{
++		timeout->tv_sec = 0;
++		timeout->tv_usec = 10 * 1000;
++		return ;
++	}
++
++	first_server_bind = server;
++
++	gettimeofday(&now, NULL);
++	for (; server != NULL && bind_count < connected; server = server->next)
++	{
++		if (server->fd == -1)
++		{
++			continue;
++		}
++
++		diff = qserver_get_timeout(server, &now);
++		if (diff < smallest)
++		{
++			smallest = diff;
++		}
++		bind_count++;
++	}
++
++	if (smallest < 10)
++	{
++		smallest = 10;
++	}
++
++	timeout->tv_sec = smallest / 1000;
++	timeout->tv_usec = (smallest % 1000) *1000;
++}
++
++
++#ifdef USE_SELECT
++static fd_set select_read_fds;
++static int select_maxfd;
++static int select_cursor;
++
++int set_fds(fd_set *fds)
++{
++	int maxfd = - 1, i;
++
++	for (i = 0; i < max_connmap; i++)
++	{
++		if (connmap[i] != NULL)
++		{
++			FD_SET(connmap[i]->fd, fds);
++			if (connmap[i]->fd > maxfd)
++			{
++				maxfd = connmap[i]->fd;
++			}
++		}
++	}
++
++	return maxfd;
++}
++
++void set_file_descriptors()
++{
++	FD_ZERO(&select_read_fds);
++	select_maxfd = set_fds(&select_read_fds);
++}
++
++int wait_for_file_descriptors(struct timeval *timeout)
++{
++	select_cursor = 0;
++	return select(select_maxfd + 1, &select_read_fds, NULL, NULL, timeout);
++}
++
++struct qserver *get_next_ready_server()
++{
++	while (select_cursor < max_connmap && (connmap[select_cursor] == NULL || !FD_ISSET(connmap[select_cursor]->fd, &select_read_fds)))
++	{
++		select_cursor++;
++	}
++
++	if (select_cursor >= max_connmap)
++	{
++		return NULL;
++	}
++	return connmap[select_cursor++];
++}
++
++int wait_for_timeout(unsigned int ms)
++{
++	struct timeval timeout;
++	timeout.tv_sec = ms / 1000;
++	timeout.tv_usec = (ms % 1000) *1000;
++	return select(0, 0, NULL, NULL, &timeout);
++}
++
++#endif /* USE_SELECT */
++
++#ifdef USE_POLL
++static struct pollfd *pollfds;
++static int n_pollfds;
++static int max_pollfds = 0;
++static int poll_cursor;
++
++void set_file_descriptors()
++{
++	struct pollfd *p;
++	int i;
++
++	if (max_connmap > max_pollfds)
++	{
++		max_pollfds = max_connmap;
++		pollfds = (struct pollfd*)realloc(pollfds, max_pollfds *sizeof(struct pollfd));
++	}
++
++	p = pollfds;
++	for (i = 0; i < max_connmap; i++)
++	if (connmap[i] != NULL)
++	{
++		p->fd = connmap[i]->fd;
++		p->events = POLLIN;
++		p->revents = 0;
++		p++;
++	}
++	n_pollfds = p - pollfds;
++}
++
++int wait_for_file_descriptors(struct timeval *timeout)
++{
++	poll_cursor = 0;
++	return poll(pollfds, n_pollfds, timeout->tv_sec *1000+timeout->tv_usec / 1000);
++}
++
++struct qserver *get_next_ready_server()
++{
++	for (; poll_cursor < n_pollfds; poll_cursor++)
++	{
++		if (pollfds[poll_cursor].revents)
++		{
++			break;
++		}
++	}
++
++	if (poll_cursor >= n_pollfds)
++	{
++		return NULL;
++	}
++	return connmap[pollfds[poll_cursor++].fd];
++}
++
++int wait_for_timeout(unsigned int ms)
++{
++	return poll(0, 0, ms);
++}
++
++#endif /* USE_POLL */
++
++
++void free_server(struct qserver *server)
++{
++	struct player *player, *next_player;
++	struct rule *rule, *next_rule;
++
++	/* remove from servers list */
++	if (server == servers)
++	{
++		servers = server->next;
++		if (servers)
++		{
++			servers->prev = NULL;
++		}
++	}
++
++	if ((void*) &server->next == (void*)last_server)
++	{
++		if (server->prev)
++		{
++			last_server = &server->prev->next;
++		}
++		else
++		{
++			last_server = &servers;
++		}
++	}
++	if (server == first_server_bind)
++	{
++		first_server_bind = server->next;
++	}
++	if (server == last_server_bind)
++	{
++		last_server_bind = server->next;
++	}
++
++	if (server->prev)
++	{
++		server->prev->next = server->next;
++	}
++	if (server->next)
++	{
++		server->next->prev = server->prev;
++	}
++
++	/* remove from server hash table */
++	remove_server_from_hash(server);
++
++	/* free all the data */
++	for (player = server->players; player; player = next_player)
++	{
++		next_player = player->next;
++		free_player(player);
++	}
++
++	for (rule = server->rules; rule; rule = next_rule)
++	{
++		next_rule = rule->next;
++		free_rule(rule);
++	}
++
++	if (server->arg)
++	{
++		free(server->arg);
++	}
++	if (server->host_name)
++	{
++		free(server->host_name);
++	}
++	if (server->error)
++	{
++		free(server->error);
++	}
++	if (server->address)
++	{
++		free(server->address);
++	}
++	if (server->map_name)
++	{
++		free(server->map_name);
++	}
++	if (!(server->flags &FLAG_DO_NOT_FREE_GAME) && server->game)
++	{
++		free(server->game);
++	}
++	if (server->master_pkt)
++	{
++		free(server->master_pkt);
++	}
++	if ( server->query_arg )
++	{
++		free(server->query_arg);
++	}
++	if ( server->challenge_string )
++	{
++		free(server->challenge_string);
++	}
++
++	/* These fields are never malloc'd: outfilename
++	 */
++
++	if (
++		server->server_name != NULL &&
++		server->server_name != DOWN &&
++		server->server_name != HOSTNOTFOUND &&
++		server->server_name != SYSERROR &&
++		server->server_name != MASTER &&
++		server->server_name != SERVERERROR &&
++		server->server_name != TIMEOUT &&
++		server->server_name != GAMESPY_MASTER_NAME &&
++		server->server_name != BFRIS_SERVER_NAME
++	)
++	{
++		free(server->server_name);
++	}
++
++	/*
++	params ...
++	saved_data ...
++	 */
++
++	free(server);
++	--num_servers;
++}
++
++void free_player(struct player *player)
++{
++	if (player->name)
++	{
++		free(player->name);
++	}
++	if (!(player->flags &PLAYER_FLAG_DO_NOT_FREE_TEAM) && player->team_name)
++	{
++		free(player->team_name);
++	}
++	if (player->address)
++	{
++		free(player->address);
++	}
++	if (player->tribe_tag)
++	{
++		free(player->tribe_tag);
++	}
++	if (player->skin)
++	{
++		free(player->skin);
++	}
++	if (player->mesh)
++	{
++		free(player->mesh);
++	}
++	if (player->face)
++	{
++		free(player->face);
++	}
++	free(player);
++}
++
++void free_rule(struct rule *rule)
++{
++	if (rule->name)
++	{
++		free(rule->name);
++	}
++
++	if (rule->value)
++	{
++		free(rule->value);
++	}
++	free(rule);
++}
++
++/* Functions for handling response packets
++ */
++
++/* Packet from normal Quake server
++ */
++query_status_t deal_with_q_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	struct q_packet *pkt = (struct q_packet*)rawpkt;
++	int rc;
++
++	debug( 2, "deal_with_q_packet %p, %d", server, pktlen );
++
++	if (ntohs(pkt->length) != pktlen)
++	{
++		fprintf(stderr, "%s Ignoring bogus packet; length %d != %d\n", server->arg, ntohs(pkt->length), pktlen);
++		return PKT_ERROR;
++	}
++
++	rawpkt[pktlen] = '\0';
++
++	switch (pkt->op_code)
++	{
++		case Q_CCREP_ACCEPT:
++		case Q_CCREP_REJECT:
++			return 0;
++		case Q_CCREP_SERVER_INFO:
++			server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++			rc = server_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++			break;
++		case Q_CCREP_PLAYER_INFO:
++			server->ping_total += time_delta(&packet_recv_time, &server->packet_time2);
++			rc = player_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++			break;
++		case Q_CCREP_RULE_INFO:
++			server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++			rc = rule_info_packet(server, pkt, pktlen - Q_HEADER_LEN);
++			break;
++		case Q_CCREQ_CONNECT:
++		case Q_CCREQ_SERVER_INFO:
++		case Q_CCREQ_PLAYER_INFO:
++		case Q_CCREQ_RULE_INFO:
++		default:
++			return 0;
++	}
++
++	if ( SOCKET_ERROR == rc )
++	{
++		fprintf(stderr, "%s error on packet opcode %x\n", server->arg, (int)pkt->op_code);
++	}
++
++	return rc;
++}
++
++
++/* Packet from QuakeWorld server
++ */
++query_status_t deal_with_qw_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	debug( 2, "deal_with_qw_packet %p, %d", server, pktlen );
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	}
++
++	if (((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' || rawpkt[2] != '\377' || rawpkt[3] != '\377') &&	show_errors)
++	{
++		unsigned int ipaddr = ntohl(server->ipaddr);
++		fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
++			(ipaddr >> 24) &0xff,
++			(ipaddr >> 16) &0xff,
++			(ipaddr	>> 8) &0xff,
++			ipaddr &0xff,
++			ntohs(server->port)
++		);
++		print_packet(server, rawpkt, pktlen);
++	}
++
++	rawpkt[pktlen] = '\0';
++
++	if (rawpkt[4] == 'n')
++	{
++		if (server->type->id != QW_SERVER)
++		{
++			server->type = find_server_type_id(QW_SERVER);
++		}
++		return deal_with_q1qw_packet(server, rawpkt, pktlen);
++	}
++	else if (rawpkt[4] == '\377' && rawpkt[5] == 'n')
++	{
++		if (server->type->id != HW_SERVER)
++		{
++			server->type = find_server_type_id(HW_SERVER);
++		}
++		return deal_with_q1qw_packet(server, rawpkt, pktlen);
++	}
++	else if (strncmp(&rawpkt[4], "print\n\\", 7) == 0)
++	{
++		return deal_with_q2_packet(server, rawpkt + 10, pktlen - 10 );
++	}
++	else if (strncmp(&rawpkt[4], "print\n", 6) == 0)
++	{
++		/* work-around for occasional bug in Quake II status packets
++		 */
++		char *c, *p;
++		p = c = &rawpkt[10];
++		while (*p != '\\' && (c = strchr(p, '\n')))
++		{
++			p = c + 1;
++		}
++		if (*p == '\\' && c != NULL)
++		{
++			return deal_with_q2_packet(server, p, pktlen - (p - rawpkt) );
++		}
++	}
++	else if (strncmp(&rawpkt[4], "infoResponse", 12) == 0 || (rawpkt[4] == '\001' && strncmp(&rawpkt[5], "infoResponse", 12) == 0))
++	{
++		/* quake3 info response */
++		int ret;
++		if (rawpkt[4] == '\001')
++		{
++			rawpkt++;
++			pktlen--;
++		}
++		rawpkt += 12;
++		pktlen -= 12;
++		for (; pktlen && *rawpkt != '\\'; pktlen--, rawpkt++)
++			;
++		if (!pktlen)
++		{
++			return INPROGRESS;
++		}
++		if (rawpkt[pktlen - 1] == '"')
++		{
++			rawpkt[pktlen - 1] = '\0';
++			pktlen--;
++		}
++		if (get_player_info || get_server_rules)
++		{
++			server->next_rule = "";
++		}
++
++		ret = deal_with_q2_packet(server, rawpkt, pktlen );
++		if ( DONE_AUTO == ret && ( get_player_info || get_server_rules ) )
++		{
++			send_rule_request_packet( server);
++			server->retry1= n_retries-1;
++			return INPROGRESS;
++		}
++
++		return ret;
++	}
++	else if (strncmp(&rawpkt[4], "statusResponse\n", 15) == 0 || (rawpkt[4] == '\001' && strncmp(&rawpkt[5], "statusResponse\n", 15) 	== 0))
++	{
++		/* quake3 status response */
++		server->next_rule = NO_SERVER_RULES;
++		server->retry1 = 0;
++		if (rawpkt[4] == '\001')
++		{
++			rawpkt++;
++			pktlen--;
++		}
++		server->flags |= CHECK_DUPLICATE_RULES;
++		return deal_with_q2_packet(server, rawpkt + 19, pktlen - 19 );
++	}
++	else if (strncmp(&rawpkt[4], "infostringresponse", 19) == 0)
++	{
++		return deal_with_q2_packet(server, rawpkt + 23, pktlen - 23 );
++	}
++
++	if (show_errors)
++	{
++		unsigned int ipaddr = ntohl(server->ipaddr);
++		fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, ignoring ...\n",
++			(ipaddr >> 24) &0xff,
++			(ipaddr >> 16) &0xff,
++			(ipaddr >>	8) &0xff,
++			ipaddr &0xff,
++			ntohs(server->port)
++		);
++		print_packet(server, rawpkt, pktlen);
++		return PKT_ERROR;
++	}
++
++	return DONE_AUTO;
++}
++
++query_status_t deal_with_q1qw_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	char *key, *value, *end, *users;
++	struct player *player = NULL, **last_player = &server->players;
++	int len, rc, complete = 0;
++	int number, frags, connect_time, ping;
++	char *pkt = &rawpkt[5];
++
++	debug( 2, "deal_with_q1qw_packet %p, %d", server, pktlen );
++
++	if (server->type->id == HW_SERVER)
++	{
++		pkt = &rawpkt[6];
++	}
++
++	*(users = strchr(pkt, '\n')) = '\0';
++	while (*pkt && pkt - rawpkt < pktlen)
++	{
++		if (*pkt == '\\')
++		{
++			pkt++;
++			end = strchr(pkt, '\\');
++			if (end == NULL)
++			{
++				break;
++			}
++			*end = '\0';
++			key = pkt;
++			pkt += strlen(pkt) + 1;
++			end = strchr(pkt, '\\');
++			if (end == NULL)
++			{
++				end = users;
++			}
++			value = (char*)malloc(end - pkt + 1);
++			memcpy(value, pkt, end - pkt);
++			value[end - pkt] = '\0';
++			pkt = end;
++			if (strcmp(key, "hostname") == 0)
++			{
++				server->server_name = value;
++			}
++			else if (strcmp(key, "map") == 0)
++			{
++				server->map_name = value;
++			}
++			else if (strcmp(key, "maxclients") == 0)
++			{
++				server->max_players = atoi(value);
++				free(value);
++			}
++			else if (strcmp(key, "maxspectators") == 0)
++			{
++				server->max_spectators = atoi(value);
++				free(value);
++			}
++			else if (get_server_rules || strncmp(key, "*game", 5) == 0)
++			{
++				add_rule(server, key, value, NO_VALUE_COPY);
++				if (strcmp(key, "*gamedir") == 0)
++				{
++					server->game = value;
++					server->flags |= FLAG_DO_NOT_FREE_GAME;
++				}
++			}
++		}
++		else
++		{
++			pkt++;
++		}
++		complete = 1;
++	}
++	*pkt = '\n';
++	while (*pkt && pkt - rawpkt < pktlen)
++	{
++		if (*pkt == '\n')
++		{
++			pkt++;
++			if (pkt - rawpkt >= pktlen || *pkt == '\0')
++			{
++				break;
++			}
++			rc = sscanf(pkt, "%d %d %d %d %n", &number, &frags, &connect_time, &ping, &len);
++			if (rc != 4)
++			{
++				char *nl; /* assume it's an error packet */
++				server->error = (char*)malloc(pktlen + 1);
++				nl = strchr(pkt, '\n');
++				if (nl != NULL)
++				{
++					strncpy(server->error, pkt, nl - pkt);
++					server->error[nl - pkt] = '\0';
++				}
++				else
++				{
++					strcpy(server->error, pkt);
++				}
++				server->server_name = SERVERERROR;
++				complete = 1;
++				break;
++			}
++			if (get_player_info)
++			{
++				player = (struct player*)calloc(1, sizeof(struct player));
++				player->number = number;
++				player->frags = frags;
++				player->connect_time = connect_time * 60;
++				player->ping = ping > 0 ? ping : - ping;
++			}
++			else
++			{
++				player = NULL;
++			}
++
++			pkt += len;
++
++			if (*pkt != '"')
++			{
++				break;
++			}
++			pkt += ping > 0 ? 1 : 4; // if 4 then no "\s\" in spectators name
++			// protocol "under construction"
++			end = strchr(pkt, '"');
++			if (end == NULL)
++			{
++				break;
++			}
++			if (player != NULL)
++			{
++				player->name = (char*)malloc(end - pkt + 1);
++				memcpy(player->name, pkt, end - pkt);
++				player->name[end - pkt] = '\0';
++			}
++
++			pkt = end + 2;
++
++			if (*pkt != '"')
++			{
++				break;
++			}
++			pkt++;
++			end = strchr(pkt, '"');
++			if (end == NULL)
++			{
++				break;
++			}
++			if (player != NULL)
++			{
++				player->skin = (char*)malloc(end - pkt + 1);
++				memcpy(player->skin, pkt, end - pkt);
++				player->skin[end - pkt] = '\0';
++			}
++			pkt = end + 2;
++
++			if (player != NULL)
++			{
++				sscanf(pkt, "%d %d%n", &player->shirt_color, &player->pants_color, &len);
++				*last_player = player;
++				last_player = &player->next;
++			}
++			else
++			{
++				sscanf(pkt, "%*d %*d%n", &len);
++			}
++			pkt += len;
++
++			if (pkt + 3 < rawpkt + pktlen && *pkt == ' ')
++			// mvdsv is at last rev 377, 23.06.2006
++			{
++				pkt++;
++
++				if (*pkt != '"')
++				{
++					break;
++				}
++				pkt++;
++				end = strchr(pkt, '"');
++				if (end == NULL)
++				{
++					break;
++				}
++
++				if (player != NULL)
++				{
++					player->team_name = (char*)malloc(end - pkt + 1);
++					memcpy(player->team_name, pkt, end - pkt);
++					player->team_name[end - pkt] = '\0';
++				}
++				pkt = end + 1;
++			}
++
++			if (ping > 0)
++			{
++				server->num_players++;
++			}
++			else
++			{
++				server->num_spectators++;
++			}
++		}
++		else
++		{
++			pkt++;
++		}
++		complete = 1;
++	}
++
++	if (!complete)
++	{
++		if (rawpkt[4] != 'n' || rawpkt[5] != '\0')
++		{
++			fprintf(stderr, "Odd packet from QW server %d.%d.%d.%d:%hu ...\n",
++				(server->ipaddr >> 24) &0xff,
++				(server->ipaddr >> 16) &0xff,
++				(server->ipaddr >> 8) &0xff,
++				server->ipaddr &0xff,
++				ntohs(server->port)
++			);
++			print_packet(server, rawpkt, pktlen);
++		}
++	}
++	else if (server->server_name == NULL)
++	{
++		server->server_name = strdup("");
++	}
++
++	return DONE_AUTO;
++}
++
++query_status_t deal_with_q2_packet(struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *key, *value, *end;
++	struct player *player = NULL;
++	struct player **last_player = &server->players;
++	int len, rc, complete = 0;
++	int frags = 0, ping = 0, num_players = 0;
++	char *pkt = rawpkt;
++
++	debug( 2, "deal_with_q2_packet %p, %d", server, pktlen );
++
++	while (*pkt && pkt - rawpkt < pktlen)
++	{
++		// we have variable, value pairs seperated by slash
++		if (*pkt == '\\')
++		{
++			pkt++;
++			if (*pkt == '\n' && server->type->id == SOF_SERVER)
++			{
++				goto player_info;
++			}
++
++			// Find the key
++			end = strchr(pkt, '\\');
++			if (NULL == end)
++			{
++				break;
++			}
++			*end = '\0';
++			key = pkt;
++			pkt += strlen(key) + 1;
++
++			// Find the value
++			end = strpbrk(pkt, "\\\n");
++			if (NULL == end)
++			{
++				// Last value
++				end = rawpkt + pktlen;
++			}
++
++			// Make a copy of the value
++			value = (char*)malloc(end - pkt + 1);
++			memcpy(value, pkt, end - pkt);
++			value[end - pkt] = '\0';
++			pkt = end;
++			debug(3, "%s = %s", key, value);
++			if (server->server_name == NULL && (strcmp(key, "hostname") == 0 || strcmp(key, "sv_hostname") == 0))
++			{
++				// Server name
++				server->server_name = value;
++			}
++			else if (strcmp(key, "mapname") == 0 || (strcmp(key, "map") == 0 && server->map_name == NULL))
++			{
++				// Map name
++				if (NULL != server->map_name)
++				{
++					free(server->map_name);
++				}
++				server->map_name = value;
++			}
++			else if (strcmp(key, "maxclients") == 0 || strcmp(key, "sv_maxclients") == 0 || strcmp(key, "max") == 0)
++			{
++				// Max Players
++				server->max_players = atoi(value);
++				// Note: COD 4 infoResponse returns max as sv_maxclients - sv_privateClients where as statusResponse returns the true max
++				// MOHAA Q3 protocol max players is always 0
++				if (0 == server->max_players)
++				{
++					server->max_players = - 1;
++				}
++				free(value);
++			}
++			else if (strcmp(key, "clients") == 0 || strcmp(key, "players") == 0)
++			{
++				// Num Players
++				server->num_players = atoi(value);
++				free(value);
++			}
++			else if (server->server_name == NULL && strcmp(key, "pure") == 0)
++			{
++				add_rule(server, key, value, NO_VALUE_COPY);
++			}
++			else if (get_server_rules || strncmp(key, "game", 4) == 0)
++			{
++				int dofree = 0;
++				int flags = ( server->flags & CHECK_DUPLICATE_RULES ) ? CHECK_DUPLICATE_RULES | NO_VALUE_COPY : NO_VALUE_COPY;
++				if (add_rule(server, key, value, flags ) == NULL)
++				{
++					// duplicate, so free value
++					dofree = 1;
++				}
++
++				if (server->game == NULL && strcmp(key, server->type->game_rule) == 0)
++				{
++					server->game = value;
++					if (0 == dofree)
++					{
++						server->flags |= FLAG_DO_NOT_FREE_GAME;
++					}
++				}
++				else if (1 == dofree)
++				{
++					free(value);
++				}
++			}
++			else
++			{
++				free(value);
++			}
++		}
++		else if (*pkt == '\n')
++		{
++			player_info: debug(3, "player info");
++			pkt++;
++			if (*pkt == '\0')
++			{
++				break;
++			}
++
++			if (0 == strncmp(pkt, "\\challenge\\", 11))
+ 			{
+-				continue;
++				// qfusion
++				// This doesnt support getstatus looking at warsow source:
++				// server/sv_main.c: SV_ConnectionlessPacket
++				server->next_rule = NO_SERVER_RULES;
++				debug(3, "no more server rules");
++				break;
+ 			}
+ 
+-			if ( ! server->retry1 )
++			rc = sscanf(pkt, "%d %n", &frags, &len);
++			if (rc == 1 && pkt[len] != '"')
+ 			{
+-				// no more retries
+-				server->next_rule= NULL;
+-				server->missing_rules = 1;
+-				cleanup_qserver( server, 0);
+-				continue;
++				pkt += len;
++				rc = sscanf(pkt, "%d %n", &ping, &len);
++			}
++			else if (rc == 1)
++			{
++				/* MOHAA Q3 protocol only provides player ping */
++				ping = frags;
++				frags = 0;
+ 			}
+-			send_rule_request_packet( server);
+-			gettimeofday(&t_lastsend, NULL);
+-			n_sent++;
+-		}
+ 
+-		if ( server->next_player_info < server->num_players)
+-		{
+-			// Expecting player details
+-			if (
+-				server->retry2 != n_retries &&
+-				time_delta( &now, &server->packet_time2) < (interval*(n_retries-server->retry2+1))
+-			)
++			if (rc != 1)
+ 			{
+-				continue;
++				char *nl; /* assume it's an error packet */
++				server->error = (char*)malloc(pktlen + 1);
++				nl = strchr(pkt, '\n');
++				if (nl != NULL)
++				{
++					strncpy(server->error, pkt, nl - pkt);
++				}
++				else
++				{
++					strcpy(server->error, pkt);
++				}
++				server->server_name = SERVERERROR;
++				complete = 1;
++				break;
+ 			}
+-			if ( ! server->retry2 )
++
++			if (get_player_info)
+ 			{
+-				server->next_player_info++;
+-				if ( server->next_player_info >= server->num_players )
++				player = (struct player*)calloc(1, sizeof(struct player));
++				player->number = 0;
++				player->connect_time = - 1;
++				player->frags = frags;
++				player->ping = ping;
++			}
++			else
++			{
++				player = NULL;
++			}
++
++			pkt += len;
++
++			if (isdigit((unsigned char) *pkt))
++			{
++				/* probably an SOF2 1.01 server, includes team # */
++				int team;
++				rc = sscanf(pkt, "%d %n", &team, &len);
++				if (rc == 1)
+ 				{
+-					// no more retries
+-					cleanup_qserver( server, 0);
+-					continue;
++					pkt += len;
++					if (player)
++					{
++						player->team = team;
++						server->flags |= FLAG_PLAYER_TEAMS;
++					}
+ 				}
+-				server->retry2 = n_retries;
+ 			}
+-			send_player_request_packet( server);
+-			gettimeofday(&t_lastsend, NULL);
+-			n_sent++;
+-		}
+ 
+-		if ( prev_n_sent == n_sent )
+-		{
+-			// we didnt send any additional queries
+-			debug(2, "%d %d", time_delta( &now, &server->packet_time1 ), (interval*(n_retries+1)));
+-			if ( ! server->retry1 )
++			if (*pkt != '"')
+ 			{
+-				// no retries left
+-				if ( time_delta( &now, &server->packet_time1 ) > (interval*(n_retries+1) ) )
++				break;
++			}
++
++			pkt++;
++			end = strchr(pkt, '"');
++			if (end == NULL)
++			{
++				break;
++			}
++			if (player != NULL)
++			{
++				player->name = (char*)malloc(end - pkt + 1);
++				memcpy(player->name, pkt, end - pkt);
++				player->name[end - pkt] = '\0';
++			}
++			pkt = end + 1;
++
++			//WarSoW team number
++			if (*pkt != '\n')
++			{
++				int team;
++				rc = sscanf(pkt, "%d%n", &team, &len);
++				if (rc == 1)
+ 				{
+-					cleanup_qserver( server, 1 );
++					pkt += len;
++					if (player)
++					{
++						player->team = team;
++						server->flags |= FLAG_PLAYER_TEAMS;
++					}
+ 				}
+ 			}
+-			else
++
++			if (player != NULL)
+ 			{
+-				// decrement as we didnt send any packets
+-				server->retry1--;
++				player->skin = NULL;
++				player->shirt_color = - 1;
++				player->pants_color = - 1;
++				*last_player = player;
++				last_player = &player->next;
+ 			}
++			num_players++;
++		}
++		else
++		{
++			pkt++;
+ 		}
++		complete = 1;
++	}
++
++	if (server->num_players == 0 || num_players > server->num_players)
++	{
++		server->num_players = num_players;
+ 	}
++
++	if (!complete)
++	{
++		return PKT_ERROR;
++	}
++	else if (server->server_name == NULL)
++	{
++		server->server_name = strdup("");
++	}
++
++	return DONE_AUTO;
+ }
+ 
+-/* server starts sending data immediately, so we need not do anything */
+-void
+-send_bfris_request_packet( struct qserver *server)
++int ack_descent3master_packet(struct qserver *server, char *curtok)
+ {
+-	register_send( server );
+-}
++	int rc;
++	char packet[0x1e];
++	memcpy(packet, descent3_masterquery, 0x1a);
++	packet[1] = 0x1d;
++	packet[0x16] = 1;
++	memcpy(packet + 0x1a, curtok, 4);
++	rc = send(server->fd, packet, sizeof(packet), 0);
++	if (rc == SOCKET_ERROR)
++	{
++		return send_error( server, rc );
++	}
+ 
++	return rc;
++}
+ 
+-/* First packet for a normal Quake server
++/* Packet from Descent3 master server (PXO)
+  */
+-void
+-send_qserver_request_packet( struct qserver *server)
++query_status_t deal_with_descent3master_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
++	int i = 0, lastpacket = 0;
++	char *names = rawpkt + 0x1f;
++	char *ips = rawpkt + 0x29f;
++	char *ports = rawpkt + 0x2ef;
+ 
+-	int rc = send_packet( server, server->type->status_packet, server->type->status_len );
++	debug( 2, "deal_with_descent3master_packet %p, %d", server, pktlen );
+ 
+-    if ( rc == SOCKET_ERROR )
++	while (i < 20)
+ 	{
+-		cleanup_qserver( server, 1 );
+-    }
++		if (*names)
++		{
++			char *c;
++			server->master_pkt_len += 6;
++			server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++			c = server->master_pkt + server->master_pkt_len - 6;
++			memcpy(c, ips, 4);
++			memcpy(c + 4, ports, 2);
++		}
++		else if (i > 0)
++		{
++			lastpacket = 1;
++		}
++		names += 0x20;
++		ips += 4;
++		ports += 2;
++		i++;
++	}
++
++	ack_descent3master_packet(server, rawpkt + 0x1a);
++
++	server->n_servers = server->master_pkt_len / 6;
++
++	server->next_player_info = - 1;
++	server->retry1 = 0;
++
++	if (lastpacket)
++	{
++		return DONE_AUTO;
++	}
++
++	return INPROGRESS;
+ }
+ 
+-/* First packet for a QuakeWorld server
++/* Packet from QuakeWorld master server
+  */
+-void
+-send_qwserver_request_packet( struct qserver *server)
++query_status_t deal_with_qwmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    int rc;
++	int ret = 0;
+ 
+-    if ( server->flags & FLAG_BROADCAST)
+-	rc= send_broadcast( server, server->type->status_packet,
+-		server->type->status_len);
+-    else if ( server->server_name == NULL)
+-	rc= send( server->fd, server->type->status_packet,
+-		server->type->status_len, 0);
+-    else if ( server->server_name != NULL && server->type->rule_packet)
+-	rc= send( server->fd, server->type->rule_packet,
+-		server->type->rule_len, 0);
+-    else
+-	rc= SOCKET_ERROR;
+-
+-    if ( rc == SOCKET_ERROR)  {
+-	unsigned int ipaddr= ntohl(server->ipaddr);
+-	fprintf( stderr,
+-		"Error on %d.%d.%d.%d, skipping ...\n",
+-		(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-		(ipaddr>>8)&0xff, ipaddr&0xff);
+-	perror( "send");
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
+-    if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST)  {
+-	gettimeofday( &server->packet_time1, NULL);
+-	server->n_requests++;
+-    }
+-    else if ( server->server_name == NULL)
+-	server->n_retries++;
+-    server->retry1--;
+-    if ( server->server_name == NULL)
+-	server->n_packets++;
+-    server->next_player_info = NO_PLAYER_INFO; // we don't have a player packet
++	debug( 2, "deal_with_qwmaster_packet %p, %d", server, pktlen );
++
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++	if (rawpkt[0] == QW_NACK)
++	{
++		server->error = strdup(&rawpkt[2]);
++		server->server_name = SERVERERROR;
++		return PKT_ERROR;
++	}
++
++	if (*((unsigned int*)rawpkt) == 0xffffffff)
++	{
++		rawpkt += 4; /* QW 1.5 */
++		pktlen -= 4;
++		if (rawpkt[0] == '\377' && rawpkt[1] == QW_SERVERS)
++		{
++			rawpkt++;	/* hwmaster */
++			pktlen--;
++		}
++	}
++
++	if (rawpkt[0] == QW_SERVERS && rawpkt[1] == QW_NEWLINE)
++	{
++		rawpkt += 2;
++		pktlen -= 2;
++	}
++	else if (rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0d)
++	{
++		// 2 byte id + 4 byte sequence
++		memcpy(server->master_query_tag, rawpkt + 2, 3);
++		rawpkt += 6;
++		pktlen -= 6;
++	}
++	else if (rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0a)
++	{
++		// no sequence id for steam
++		// instead we use the ip:port of the last recieved server
++		struct in_addr *sin_addr = (struct in_addr*)(rawpkt + pktlen - 6);
++		char *ip = inet_ntoa(*sin_addr);
++		unsigned short port = htons(*((unsigned short*)(rawpkt + pktlen - 2)));
++
++		//fprintf( stderr, "NEXT IP=%s:%u\n", ip, port );
++		sprintf(server->master_query_tag, "%s:%u", ip, port);
++
++		// skip over the 2 byte id
++		rawpkt += 2;
++		pktlen -= 2;
++	}
++	else if (strncmp(rawpkt, "servers", 7) == 0)
++	{
++		rawpkt += 8;
++		pktlen -= 8;
++	}
++	else if (strncmp(rawpkt, "getserversResponse", 18) == 0)
++	{
++		rawpkt += 18;
++		pktlen -= 18;
++
++		for (; *rawpkt != '\\' && pktlen; pktlen--, rawpkt++){}
++
++		if (!pktlen)
++		{
++			return 1;
++		}
++		rawpkt++;
++		pktlen--;
++
++		debug( 2, "q3m pktlen %d lastchar %x\n", pktlen, (unsigned int)rawpkt[pktlen - 1]);
++
++		server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen + 1);
++
++		if (server->type->id == STEF_MASTER)
++		{
++			ret = decode_stefmaster_packet(server, rawpkt, pktlen);
++		}
++		else
++		{
++			ret = decode_q3master_packet(server, rawpkt, pktlen);
++		}
++		debug( 2, "q3m %d servers\n", server->n_servers);
++
++		return ret;
++	}
++	else if (show_errors)
++	{
++		unsigned int ipaddr = ntohl(server->ipaddr);
++		fprintf(stderr, "Odd packet from QW master %d.%d.%d.%d, processing ...\n",
++			(ipaddr >> 24) &0xff,
++			(ipaddr >> 16) &0xff,
++			(ipaddr >>	8) &0xff,
++			ipaddr &0xff
++		);
++		print_packet(server, rawpkt, pktlen);
++	}
++
++	server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen + 1);
++	rawpkt[pktlen] = '\0';
++	memcpy(server->master_pkt + server->master_pkt_len, rawpkt, pktlen + 1);
++	server->master_pkt_len += pktlen;
++
++	server->n_servers = server->master_pkt_len / 6;
++
++	if (server->type->flags &TF_MASTER_MULTI_RESPONSE)
++	{
++		server->next_player_info = - 1;
++		server->retry1 = 0;
++	}
++	else if (server->type->id == HL_MASTER)
++	{
++		if (server->master_query_tag[0] == 0 && server->master_query_tag[1] == 0 && server->master_query_tag[2] == 0)
++		{
++			// all done
++			server->server_name = MASTER;
++			bind_sockets();
++			ret = DONE_FORCE;
++		}
++		else
++		{
++			// more to come
++			server->retry1++;
++			send_qwmaster_request_packet(server);
++		}
++	}
++	else if (server->type->flags &TF_MASTER_STEAM)
++	{
++		// should the HL_MASTER be the same as this?
++		int i;
++		for (i = pktlen - 6; i < pktlen && 0x00 == rawpkt[i]; i++){}
++
++		if (i == pktlen)
++		{
++			// last 6 bytes where 0x00 so we have reached the last packet
++			server->n_servers--;
++			server->master_pkt_len -= 6;
++			server->server_name = MASTER;
++			bind_sockets();
++			ret = DONE_FORCE;
++		}
++		else
++		{
++			// more to come
++			server->retry1++;
++			send_qwmaster_request_packet(server);
++		}
++	}
++	else
++	{
++		server->server_name = MASTER;
++		ret = DONE_AUTO;
++		bind_sockets();
++	}
++
++	return ret;
+ }
+ 
+-// First packet for an Unreal Tournament 2003 server
+-void
+-send_ut2003_request_packet( struct qserver *server)
++int decode_q3master_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+-    send_packet( server, server->type->status_packet, server->type->status_len );
+-    server->next_player_info = NO_PLAYER_INFO;
++	char *p;
++	char *end = pkt + pktlen;
++	char *last = end - 6;
++
++	pkt[pktlen] = 0;
++	p = pkt;
++
++	while ( p < last )
++	{
++		// IP & Port
++		memcpy(server->master_pkt + server->master_pkt_len, &p[0], 6);
++		server->master_pkt_len += 6;
++		p += 6;
++		// Sometimes we get some bad IP's so we search for the entry terminator '\' to avoid issues with this
++		while ( p < end && *p != '\\' )
++		{
++			p++;
++		}
++
++		if ( p < end )
++		{
++			// Skip over the '\'
++			p++;
++		}
++
++		if ( *p && p + 3 == end && 0 == strncmp( "EOF", p, 3 ) )
++		{
++			// Last packet ID ( seen in COD4 )
++			server->n_servers = server->master_pkt_len / 6;
++			server->retry1 = 0; // received at least one packet so no need to retry
++			return DONE_FORCE;
++		}
++	}
++
++	server->n_servers = server->master_pkt_len / 6;
++	//  server->next_player_info= -1; evil, causes busy loop!
++	server->retry1 = 0; // received at least one packet so no need to retry
++
++	return INPROGRESS;
+ }
+ 
+-// First packet for an Half-Life 2 server
+-void
+-send_hl2_request_packet( struct qserver *server)
++int decode_stefmaster_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+-	send_packet( server, server->type->status_packet, server->type->status_len );
++	unsigned char *p, *m, *end;
++	unsigned int i, b;
++
++	pkt[pktlen] = 0;
++
++	p = (unsigned char*)pkt;
++	m = (unsigned char*)server->master_pkt + server->master_pkt_len;
++	end = (unsigned char*) &pkt[pktlen - 12];
++	while (*p && p < end)
++	{
++		for (i = 6; i; i--)
++		{
++			sscanf((char*)p, "%2x", &b);
++			p += 2;
++			*m++ = b;
++		}
++		server->master_pkt_len += 6;
++		while (*p && *p == '\\')
++		{
++			p++;
++		}
++	}
++	server->n_servers = server->master_pkt_len / 6;
++	server->next_player_info = - 1;
++	server->retry1 = 0;
++
++	return 1;
+ }
+ 
+-/* First packet for an Unreal master
++/* Packet from Tribes master server
+  */
+-void
+-send_unrealmaster_request_packet( struct qserver *server)
++query_status_t deal_with_tribesmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++	unsigned char *upkt = (unsigned char*)rawpkt;
++	int packet_number = upkt[2];
++	int n_packets = upkt[3];
++	unsigned char *p;
++	char *mpkt;
++	int len;
++	unsigned int ipaddr;
+ 
+-static const char* steam_region[] =
+-{
+-	"US East Coast",
+-	"US West Coast",
+-	"South America",
+-	"Europe",
+-	"Asia",
+-	"Australia",
+-	"Middle East",
+-	"Africa",
+-	NULL
+-};
++	debug( 2, "deal_with_tribesmaster_packet %p, %d", server, pktlen );
+ 
+-char *
+-build_hlmaster_packet( struct qserver *server, int *len)
+-{
+-	static char packet[1600];
+-	char *pkt, *r, *sep= "";
+-	char *gamedir, *map, *flags;
+-	int flen;
++	if (memcmp(rawpkt, tribes_master_response, sizeof(tribes_master_response)) != 0)
++	{
++		fprintf(stderr, "Odd packet from Tribes master server\n");
++		print_packet(server, rawpkt, pktlen);
++	}
++
++	/*	0x1006
++		01		packet number
++		08		# packets
++		02
++		0000
++		66
++		0d		length of following string
++		"Tribes Master"
++		3c		length of following string
++		"Check out the Starsiege demo now!   www.starsiegeplayers.com"
++		0035
++		06 d143 4764 812c
++		06 d1e2 8df3 616d
++		06 1804 6d50 616d
++		06 d81c 6dc0 616d
++	 */
++	/*	0x1006
++		02
++		08
++		02 0000
++		66
++		00 3f
++		06 cf88 344c 1227
++	 */
+ 
+-	pkt= &packet[0];
+-	memcpy( pkt, server->type->master_packet, *len );
++	/* printf( "packet_number %d n_packets %d\n", packet_number, n_packets);
++	 */
+ 
+-	if ( server->type->flags & TF_MASTER_STEAM )
++	len = upkt[8];
++	if (len > 0)
+ 	{
+-		// default the region to 0xff
+-		const char* regionstring = get_param_value( server, "region", NULL );
+-		int region = 0xFF;
+-		if(regionstring)
++		p = (unsigned char*)rawpkt + 9;
++		// printf( "%.*s\n", len, p);
++		p += len;
++		len = upkt[8+len + 1];
++		// printf( "%.*s\n", len, p+1);
++		p += len + 1;
++		p += 2;
++	}
++	else
++	{
++		p = (unsigned char*)rawpkt + 10;
++	}
++
++	if (server->master_pkt == NULL)
++	{
++		server->master_pkt = (char*)malloc(n_packets *64 * 6);
++		mpkt = server->master_pkt;
++	}
++	else
++	{
++		mpkt = server->master_pkt + server->n_servers *6;
++	}
++
++	while ((char*)p < rawpkt + pktlen)
++	{
++		if (*p != 0x6)
+ 		{
+-			char* tmp = NULL;
+-			region = strtol( regionstring, &tmp, 10);
+-			if(tmp == regionstring)
+-			{
+-				int i = 0;
+-				region = 0xFF;
+-				for(;steam_region[i]; ++i)
+-				{
+-					if(!strcmp(regionstring, steam_region[i]))
+-					{
+-						region = i;
+-						break;
+-					}
+-				}
+-			}
++			printf("*p %u\n", (unsigned) *p);
++		}
++		memcpy(mpkt, p + 1, sizeof(ipaddr));
++		if (0)
++		{
++			mpkt[4] = p[5];
++			mpkt[5] = p[6];
+ 		}
+-		*(pkt+1) = region;
++		else
++		{
++			mpkt[5] = p[5];
++			mpkt[4] = p[6];
++		}
++		//printf( "%08x:%hu %u.%u.%u.%u:%hu\n", ipaddr, port, ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port);
++		p += 7;
++		mpkt += 6;
+ 	}
++	/*
++	if ( (char*)p != rawpkt+pktlen)
++	printf( "%x %x\n", p, rawpkt+pktlen);
++	 */
++	server->master_pkt_len = mpkt - server->master_pkt;
++	server->n_servers = server->master_pkt_len / 6;
++	server->server_name = MASTER;
++	server->next_player_info = - 1;
+ 
+-	pkt+= *len;
+-
+-	gamedir = get_param_value( server, "game", NULL);
+-	if ( gamedir )
++	if (packet_number >= n_packets)
+ 	{
+-		pkt+= sprintf( pkt, "\\gamedir\\%s", gamedir);
++		return DONE_FORCE;
+ 	}
+-
+-	// not valid for steam?
+-	map = get_param_value( server, "map", NULL);
+-	if ( map )
++	else
+ 	{
+-		pkt+= sprintf( pkt, "\\map\\%s", map);
++		return DONE_AUTO;
+ 	}
++}
+ 
+-	// not valid for steam?
+-	flags= get_param_value( server, "status", NULL);
+-	r= flags;
+-	while ( flags && sep )
+-	{
+-		sep= strchr( r, ':');
+-		if ( sep )
+-			flen= sep-r;
+-		else
+-			flen= strlen( r);
+-
+-		if ( strncmp( r, "notempty", flen) == 0)
+-			pkt+= sprintf( pkt, "\\empty\\1");
+-		else if ( strncmp( r, "notfull", flen) == 0)
+-			pkt+= sprintf( pkt, "\\full\\1");
+-		else if ( strncmp( r, "dedicated", flen) == 0)
++char *display_tribes2_string_list(unsigned char *pkt)
++{
++	char *delim = "";
++	unsigned int count, len;
++	count = *pkt;
++	pkt++;
++	for (; count; count--)
++	{
++		len = *pkt;
++		pkt++;
++		if (len > 0)
+ 		{
+-			if ( server->type->flags & TF_MASTER_STEAM )
+-				pkt+= sprintf( pkt, "\\type\\d");
++			if (raw_display)
++			{
++				fprintf(OF, "%s%.*s", delim, (int)len, pkt);
++				delim = raw_delimiter;
++			}
+ 			else
+-				pkt+= sprintf( pkt, "\\dedicated\\1");
++			{
++				fprintf(OF, "%.*s\n", (int)len, pkt);
++			}
+ 		}
+-		else if ( strncmp( r, "linux", flen) == 0)
+-			pkt+= sprintf( pkt, "\\linux\\1");
+-		else if ( strncmp( r, "proxy", flen) == 0) // steam
+-			pkt+= sprintf( pkt, "\\proxy\\1");
+-		else if ( strncmp( r, "secure", flen) == 0) // steam
+-			pkt+= sprintf( pkt, "\\secure\\1");
+-		r= sep+1;
++		pkt += len;
+ 	}
+-
+-	// always need null terminator
+-	*pkt = 0x00;
+-	pkt++;
+-
+-	*len = pkt - packet;
+-
+-	return packet;
++	if (raw_display)
++	{
++		fputs("\n", OF);
++	}
++	return (char*)pkt;
+ }
+ 
+-/* First packet for a QuakeWorld master server
+- */
+-void
+-send_qwmaster_request_packet( struct qserver *server)
++query_status_t deal_with_tribes2master_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+-	int rc= 0;
++	unsigned int n_servers, index, total, server_limit;
++	char *p, *mpkt;
+ 
+-	server->next_player_info = NO_PLAYER_INFO;
++	debug( 2, "deal_with_tribes2master_packet %p, %d", server, pktlen );
+ 
+-	if ( server->type->id == Q2_MASTER)
++	if (pkt[0] == TRIBES2_RESPONSE_GAME_TYPES)
+ 	{
+-		struct sockaddr_in addr;
+-		addr.sin_family= AF_INET;
+-		if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
++		pkt += 6;
++		if (raw_display)
+ 		{
+-			addr.sin_port= htons(server->port);
++			fprintf(OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter, server->arg, raw_delimiter);
+ 		}
+ 		else
+ 		{
+-			addr.sin_port= htons((unsigned short)( server->port + server->type->port_offset ));
+-		}
+-		addr.sin_addr.s_addr= server->ipaddr;
+-		memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero));
+-		rc= sendto( server->fd, server->type->master_packet,
+-			server->type->master_len, 0,
+-			(struct sockaddr *) &addr, sizeof(addr));
+-	}
+-	else
+-	{
+-		char *packet;
+-		int packet_len;
+-		char query_buf[4096] = {0};
+-
+-		packet= server->type->master_packet;
+-		packet_len= server->type->master_len;
+-
+-		if ( server->type->id == HL_MASTER)
+-		{
+-			memcpy( server->type->master_packet+1, server->master_query_tag, 3);
+-			if ( server->query_arg)
+-			{
+-				packet_len = server->type->master_len;
+-				packet= build_hlmaster_packet( server, &packet_len);
+-			}
++			fprintf(OF, "Game Types\n");
++			fprintf(OF, "----------\n");
+ 		}
+-		else if ( server->type->flags & TF_MASTER_STEAM)
++		pkt = display_tribes2_string_list((unsigned char*)pkt);
++		if (raw_display)
+ 		{
+-			// region
+-			int tag_len = strlen( server->master_query_tag );
+-			if ( tag_len < 9 )
+-			{
+-				// initial case
+-				tag_len = 9;
+-				strcpy( server->master_query_tag, "0.0.0.0:0" );
+-			}
+-
+-			// 1 byte packet id
+-			// 1 byte region
+-			// ip:port
+-			// 1 byte null
+-			packet_len = 2 + tag_len + 1;
+-
+-			if ( server->query_arg )
+-			{
+-				// build_hlmaster_packet uses server->type->master_packet
+-				// as the basis so copy from server->master_query_tag
+-				strcpy( server->type->master_packet+2, server->master_query_tag );
+-				packet = build_hlmaster_packet( server, &packet_len );
+-			}
+-			else
+-			{
+-				// default region
+-				*(packet + 1) = 0xff;
+-				memcpy( packet+2, server->master_query_tag, tag_len );
+-
+-				// filter null
+-				*(packet + packet_len ) = 0x00;
+-				packet_len++;
+-			}
++			fprintf(OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter, server->arg, raw_delimiter);
+ 		}
+-		else if ( server->type->flags & TF_QUERY_ARG)
++		else
+ 		{
+-			// fill in the master protocol details
+-			char *master_protocol= server->query_arg;
+-			if ( master_protocol == NULL)
+-			{
+-				master_protocol= server->type->master_protocol;
+-			}
+-			packet_len = sprintf( query_buf, server->type->master_packet,
+-				master_protocol?master_protocol:"",
+-				server->type->master_query?server->type->master_query:"");
+-			packet = query_buf;
++			fprintf(OF, "\nMission Types\n");
++			fprintf(OF, "-------------\n");
+ 		}
++		display_tribes2_string_list((unsigned char*)pkt);
+ 
+-		rc= send( server->fd, packet, packet_len, 0);
++		server->master_pkt_len = 0;
++		server->n_servers = 0;
++		server->server_name = MASTER;
++		server->next_player_info = - 1;
++		return DONE_FORCE;
+ 	}
+ 
+-	if ( rc == SOCKET_ERROR)
++	if (pkt[0] != TRIBES2_RESPONSE_MASTER)
+ 	{
+-		perror( "send");
++		/* error */
++		return PKT_ERROR;
+ 	}
+ 
+-	if ( server->retry1 == n_retries)
++	server_limit = get_param_ui_value(server, "limit", ~0);
++
++	n_servers = little_endian ? *(unsigned short*)(pkt + 8): swap_short(pkt + 8);
++	index = *(unsigned char*)(pkt + 6);
++	total = *(unsigned char*)(pkt + 7);
++	if (server->master_pkt == NULL)
+ 	{
+-		gettimeofday( &server->packet_time1, NULL);
+-		server->n_requests++;
++		server->master_pkt = (char*)malloc(total *n_servers * 6);
++		mpkt = server->master_pkt;
+ 	}
+ 	else
+ 	{
+-		server->n_retries++;
++		mpkt = server->master_pkt + server->n_servers *6;
+ 	}
+-	server->retry1--;
+-	server->n_packets++;
+-}
+-
+-void
+-send_tribes_request_packet( struct qserver *server )
+-{
+-	send_packet( server, server->type->player_packet, server->type->player_len );
+-}
+-
+-void
+-send_tribes2_request_packet( struct qserver *server )
+-{
+-    int rc;
+-
+-    if ( server->flags & FLAG_BROADCAST && server->server_name == NULL)
+-	rc= send_broadcast( server, server->type->status_packet,
+-		server->type->status_len);
+-    else if ( server->server_name == NULL)
+-	rc= send( server->fd, server->type->status_packet,
+-		server->type->status_len, 0);
+-    else
+-	rc= send( server->fd, server->type->player_packet,
+-		server->type->player_len, 0);
+ 
+-    if ( rc == SOCKET_ERROR)
+-	perror( "send");
++	p = pkt + 10;
++	for (; n_servers && ((char*)mpkt - server->master_pkt) / 6 < server_limit; n_servers--, p += 6, mpkt += 6)
++	{
++		memcpy(mpkt, p, 4);
++		mpkt[4] = p[5];
++		mpkt[5] = p[4];
++	}
++	server->master_pkt_len = (char*)mpkt - server->master_pkt;
++	server->n_servers = server->master_pkt_len / 6;
++	server->server_name = MASTER;
++	server->next_player_info = - 1;
+ 
+-	register_send( server );
+-}
++	if (index >= total - 1 || server->n_servers >= server_limit)
++	{
++		return PKT_ERROR;
++	}
+ 
+-void
+-send_ghostrecon_request_packet( struct qserver *server)
+-{
+-	send_packet( server, server->type->status_packet, server->type->status_len );
++	return DONE_AUTO;
+ }
+ 
+-void
+-send_eye_request_packet( struct qserver *server)
++int server_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+-	send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++	int off = 0;
+ 
+-void
+-send_ravenshield_request_packet( struct qserver *server)
+-{
+-	send_packet( server, server->type->status_packet, server->type->status_len );
+-}
++	/* ignore duplicate packets */
++	if (server->server_name != NULL)
++	{
++		return 0;
++	}
+ 
+-void
+-send_savage_request_packet( struct qserver *server)
+-{
+-	int len;
+-	char* pkt;
++	server->address = strdup((char*) &pkt->data[off]);
++	off += strlen(server->address) + 1;
++	if (off >= datalen)
++	{
++		return -1;
++	}
+ 
+-	if ( get_player_info )
++	server->server_name = strdup((char*) &pkt->data[off]);
++	off += strlen(server->server_name) + 1;
++	if (off >= datalen)
+ 	{
+-		pkt = server->type->player_packet;
+-		len = server->type->player_len;
++		return -1;
+ 	}
+-	else
++
++	server->map_name = strdup((char*) &pkt->data[off]);
++	off += strlen(server->map_name) + 1;
++	if (off > datalen)
+ 	{
+-		pkt = server->type->status_packet;
+-		len = server->type->status_len;
++		return -1;
+ 	}
+ 
+-	send_packet( server, pkt, len );
+-}
++	server->num_players = pkt->data[off++];
++	server->max_players = pkt->data[off++];
++	server->protocol_version = pkt->data[off++];
+ 
+-void
+-send_farcry_request_packet( struct qserver *server)
+-{
+-	int len;
+-	char* pkt;
++	server->retry1 = n_retries;
+ 
+-	if ( get_player_info )
++	if (get_server_rules)
+ 	{
+-		pkt = server->type->player_packet;
+-		len = server->type->player_len;
++		send_rule_request_packet(server);
+ 	}
+-	else
++	if (get_player_info)
+ 	{
+-		pkt = server->type->status_packet;
+-		len = server->type->status_len;
++		send_player_request_packet(server);
+ 	}
+ 
+-    send_packet( server, pkt, len );
++	return 0;
+ }
+ 
+-void
+-send_tribes2master_request_packet( struct qserver *server)
++int player_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+-    int rc;
+-    unsigned char packet[1600], *pkt;
+-    unsigned int len, min_players, max_players, region_mask=0;
+-    unsigned int build_version, max_bots, min_cpu, status;
+-    char *game, *mission, *buddies;
+-    static char *region_list[]= { "naeast", "nawest", "sa", "aus", "asia", "eur", NULL };
+-    static char *status_list[]= { "dedicated", "nopassword", "linux" };
+-
+-    if ( strcmp( get_param_value( server, "query", ""), "types") == 0)  {
+-	rc= send( server->fd, tribes2_game_types_request,
+-		sizeof(tribes2_game_types_request), 0);
+-	goto send_done;
+-    }
+-
+-    memcpy( packet, server->type->master_packet, server->type->master_len);
++	char *name, *address;
++	int off, colors, frags, connect_time, player_number;
++	struct player *player, *last;
++
++	off = 0;
++	player_number = pkt->data[off++];
++	name = (char*) &pkt->data[off];
++	off += strlen(name) + 1;
++	if (off >= datalen)
++	{
++		return -1;
++	}
+ 
+-    pkt= packet + 7;
++	colors = pkt->data[off + 3];
++	colors = (colors << 8) + pkt->data[off + 2];
++	colors = (colors << 8) + pkt->data[off + 1];
++	colors = (colors << 8) + pkt->data[off];
++	off += sizeof(colors);
++
++	frags = pkt->data[off + 3];
++	frags = (frags << 8) + pkt->data[off + 2];
++	frags = (frags << 8) + pkt->data[off + 1];
++	frags = (frags << 8) + pkt->data[off];
++	off += sizeof(frags);
++
++	connect_time = pkt->data[off + 3];
++	connect_time = (connect_time << 8) + pkt->data[off + 2];
++	connect_time = (connect_time << 8) + pkt->data[off + 1];
++	connect_time = (connect_time << 8) + pkt->data[off];
++	off += sizeof(connect_time);
++
++	address = (char*) &pkt->data[off];
++	off += strlen(address) + 1;
++	if (off > datalen)
++	{
++		return -1;
++	}
+ 
+-    game= get_param_value( server, "game", "any");
+-    len= strlen(game);
+-    if ( len > 255) len= 255;
+-    *pkt++= len;
+-    memcpy( pkt, game, len);
+-    pkt+= len;
+-
+-    mission= get_param_value( server, "mission", "any");
+-    len= strlen(mission);
+-    if ( len > 255) len= 255;
+-    *pkt++= len;
+-    memcpy( pkt, mission, len);
+-    pkt+= len;
+-
+-    min_players= get_param_ui_value( server, "minplayers", 0);
+-    max_players= get_param_ui_value( server, "maxplayers", 255);
+-    *pkt++= min_players;
+-    *pkt++= max_players;
+-
+-    region_mask= get_param_ui_value( server, "regions", 0xffffffff);
+-    if ( region_mask == 0)  {
+-	char *regions= get_param_value( server, "regions", "");
+-	char *r= regions;
+-	char **list, *sep;
+-	do  {
+-	    sep= strchr( r, ':');
+-	    if ( sep)
+-		len= sep-r;
+-	    else
+-		len= strlen( r);
+-	    for ( list= region_list; *list; list++)
+-		if ( strncasecmp( r, *list, len) == 0)
+-		    break;
+-	    if ( *list)
+-		region_mask|= 1<<(list-region_list);
+-	    r= sep+1;
+-	} while ( sep);
+-    }
+-    if ( little_endian)
+-	memcpy( pkt, &region_mask, 4);
+-    else  {
+-    	pkt[0]= region_mask&0xff;
+-    	pkt[1]= (region_mask>>8)&0xff;
+-    	pkt[2]= (region_mask>>16)&0xff;
+-    	pkt[3]= (region_mask>>24)&0xff;
+-    }
+-    pkt+= 4;
++	last = server->players;
++	while (last != NULL && last->next != NULL)
++	{
++		if (last->number == player_number)
++		{
++			return 0;
++		}
++		last = last->next;
++	}
+ 
+-    build_version= get_param_ui_value( server, "build", 0);
+-/*
+-    if ( build_version && build_version < 22337)  {
+-	packet[1]= 0;
+-	build_version= 0;
+-    }
+-*/
+-    if ( little_endian)
+-	memcpy( pkt, &build_version, 4);
+-    else  {
+-    	pkt[0]= build_version&0xff;
+-    	pkt[1]= (build_version>>8)&0xff;
+-    	pkt[2]= (build_version>>16)&0xff;
+-    	pkt[3]= (build_version>>24)&0xff;
+-    }
+-    pkt+= 4;
++	if (last != NULL && last->number == player_number)
++	{
++		return 0;
++	}
+ 
+-    status= get_param_ui_value( server, "status", -1);
+-    if ( status == 0)  {
+-	char *flags= get_param_value( server, "status", "");
+-	char *r= flags;
+-	char **list, *sep;
+-	do  {
+-	    sep= strchr( r, ':');
+-	    if ( sep)
+-		len= sep-r;
+-	    else
+-		len= strlen( r);
+-	    for ( list= status_list; *list; list++)
+-		if ( strncasecmp( r, *list, len) == 0)
+-		    break;
+-	    if ( *list)
+-		status|= 1<<(list-status_list);
+-	    r= sep+1;
+-	} while ( sep);
+-    }
+-    else if ( status == -1)
+-	status= 0;
+-    *pkt++= status;
+-
+-    max_bots= get_param_ui_value( server, "maxbots", 255);
+-    *pkt++= max_bots;
+-
+-    min_cpu= get_param_ui_value( server, "mincpu", 0);
+-    if ( little_endian)
+-	memcpy( pkt, &min_cpu, 2);
+-    else  {
+-    	pkt[0]= min_cpu&0xff;
+-    	pkt[1]= (min_cpu>>8)&0xff;
+-    }
+-    pkt+= 2;
++	player = (struct player*)calloc(1, sizeof(struct player));
++	player->number = player_number;
++	player->name = strdup(name);
++	player->address = strdup(address);
++	player->connect_time = connect_time;
++	player->frags = frags;
++	player->shirt_color = colors >> 4;
++	player->pants_color = colors &0xf;
++	player->next = NULL;
+ 
+-    buddies= get_param_value( server, "buddies", NULL);
+-    if ( buddies)  {
+-	char *b= buddies, *sep;
+-	unsigned int buddy, n_buddies= 0;
+-	unsigned char *n_loc= pkt++;
+-	do  {
+-	    sep= strchr( b, ':');
+-	    if ( sscanf( b, "%u", &buddy))  {
+-		n_buddies++;
+-		if ( little_endian)
+-		    memcpy( pkt, &buddy, 4);
+-		else  {
+-		    pkt[0]= buddy&0xff;
+-		    pkt[1]= (buddy>>8)&0xff;
+-		    pkt[2]= (buddy>>16)&0xff;
+-		    pkt[3]= (buddy>>24)&0xff;
+-		}
+-		pkt+= 4;
+-	    }
+-	    b= sep+1;
+-	} while ( sep && n_buddies < 255);
+-	*n_loc= n_buddies;
+-    }
+-    else
+-	*pkt++= 0;
++	if (last == NULL)
++	{
++		server->players = player;
++	}
++	else
++	{
++		last->next = player;
++	}
+ 
+-    rc= send( server->fd, (char*)packet, pkt-packet, 0);
++	server->next_player_info++;
++	server->retry2 = n_retries;
++	if (server->next_player_info < server->num_players)
++	{
++		send_player_request_packet(server);
++	}
+ 
+-send_done:
+-    if ( rc == SOCKET_ERROR)
+-	perror( "send");
+-    if ( server->retry1 == n_retries)  {
+-	gettimeofday( &server->packet_time1, NULL);
+-	server->n_requests++;
+-    }
+-    else
+-	server->n_retries++;
+-    server->retry1--;
+-    server->n_packets++;
+-}
+-
+-static struct _gamespy_query_map  {
+-    char *qstat_type;
+-    char *gamespy_type;
+-} gamespy_query_map[] = {
+-    { "qws", "quakeworld" },
+-    { "q2s", "quake2" },
+-    { "q3s", "quake3" },
+-    { "tbs", "tribes" },
+-    { "uns", "ut" },
+-    { "sgs", "shogo" },
+-    { "hls", "halflife" },
+-    { "kps", "kingpin" },
+-    { "hrs", "heretic2" },
+-    { "sfs", "sofretail" },
+-    { NULL, NULL }
+-};
++	return 0;
++}
+ 
+-void
+-send_gamespy_master_request( struct qserver *server)
++int rule_info_packet(struct qserver *server, struct q_packet *pkt, int datalen)
+ {
+-    int rc, i;
+-    char request[1024];
++	int off = 0;
++	struct rule *rule, *last;
++	char *name, *value;
+ 
+-    if ( server->n_packets)
+-	return;
+-
+-    rc= send( server->fd, server->type->master_packet,
+-	server->type->master_len, 0);
+-    if ( rc != server->type->master_len)
+-	perror( "send");
+-
+-    strcpy( request, server->type->status_packet);
+-
+-    for ( i= 0; gamespy_query_map[i].qstat_type; i++)
+-	if ( strcasecmp( server->query_arg, gamespy_query_map[i].qstat_type) == 0)
+-	    break;
+-    if ( gamespy_query_map[i].gamespy_type == NULL)
+-	strcat( request, server->query_arg);
+-    else
+-	strcat( request, gamespy_query_map[i].gamespy_type);
+-    strcat(request, "\\final\\");
+-    assert(strlen(request) < sizeof(request));
+-
+-    rc= send( server->fd, request, strlen( request), 0);
+-    if ( rc != strlen( request))
+-	perror( "send");
++	/* Straggler packet after we've already given up fetching rules */
++	if (server->next_rule == NULL)
++	{
++		return 0;
++	}
+ 
+-    if ( server->retry1 == n_retries)  {
+-	gettimeofday( &server->packet_time1, NULL);
+-	server->n_requests++;
+-    }
+-    server->n_packets++;
+-}
++	if (ntohs(pkt->length) == Q_HEADER_LEN)
++	{
++		server->next_rule = NULL;
++		return 0;
++	}
+ 
+-void
+-send_rule_request_packet( struct qserver *server)
+-{
+-    int rc, len;
++	name = (char*) &pkt->data[off];
++	off += strlen(name) + 1;
++	if (off >= datalen)
++	{
++		return -1;
++	}
+ 
+-    debug(3, "%p", server);
++	value = (char*) &pkt->data[off];
++	off += strlen(value) + 1;
++	if (off > datalen)
++	{
++		return -1;
++	}
+ 
+-    /* Server created via broadcast, so bind it */
+-    if ( server->fd == -1)  {
+-	if ( bind_qserver( server) < 0)
+-	    goto setup_retry;
+-    }
++	last = server->rules;
++	while (last != NULL && last->next != NULL)
++	{
++		if (strcmp(last->name, name) == 0)
++		{
++			return 0;
++		}
++		last = last->next;
++	}
++	if (last != NULL && strcmp(last->name, name) == 0)
++	{
++		return 0;
++	}
+ 
+-    if(server->type->rule_query_func && server->type->rule_query_func != send_rule_request_packet)
+-    {
+-	server->type->rule_query_func(server);
+-	return;
+-    }
++	rule = (struct rule*)malloc(sizeof(struct rule));
++	rule->name = strdup(name);
++	rule->value = strdup(value);
++	rule->next = NULL;
+ 
+-    if ( server->type->id == Q_SERVER)  {
+-	strcpy( (char*)q_rule.data, server->next_rule);
+-	len= Q_HEADER_LEN + strlen((char*)q_rule.data) + 1;
+-	q_rule.length= htons( (short)len);
+-    }
+-    else
+-	len= server->type->rule_len;
++	if (last == NULL)
++	{
++		server->rules = rule;
++	}
++	else
++	{
++		last->next = rule;
++	}
+ 
+-    rc= send( server->fd, (const char *) server->type->rule_packet, len, 0);
+-    if ( rc == SOCKET_ERROR)
+-	perror( "send");
++	server->n_rules++;
++	server->next_rule = rule->name;
++	server->retry1 = n_retries;
++	send_rule_request_packet(server);
+ 
+-setup_retry:
+-    if ( server->retry1 == n_retries)  {
+-	gettimeofday( &server->packet_time1, NULL);
+-	server->n_requests++;
+-    }
+-    else if ( server->server_name == NULL)
+-	server->n_retries++;
+-    server->retry1--;
+-    if ( server->server_name == NULL)
+-	server->n_packets++;
++	return 0;
+ }
+ 
+-void
+-send_player_request_packet( struct qserver *server)
++struct info *player_add_info(struct player *player, char *key, char *value, int flags)
+ {
+-    int rc;
+-
+-    if(!server->type->player_packet)
+-	return;
++	struct info *info;
++	if ( flags & OVERWITE_DUPLICATES )
++	{
++		for (info = player->info; info; info = info->next)
++		{
++			if (0 == strcmp(info->name, key))
++			{
++				// We should be able to free this
++				free(info->value);
++				if ( flags & NO_VALUE_COPY )
++				{
++					info->value = value;
++				}
++				else
++				{
++					info->value = strdup(value);
++				}
+ 
+-    /* Server created via broadcast, so bind it */
+-    if ( server->fd == -1)  {
+-	if ( bind_qserver( server) < 0)
+-	    goto setup_retry;
+-    }
++				return info;
++			}
++		}
++	}
+ 
+-    if(server->type->player_query_func && server->type->player_query_func != send_player_request_packet)
+-    {
+-	server->type->player_query_func(server);
+-	return;
+-    }
++	if ( flags & CHECK_DUPLICATE_RULES )
++	{
++		for (info = player->info; info; info = info->next)
++		{
++			if (0 == strcmp(info->name, key))
++			{
++				return NULL;
++			}
++		}
++	}
+ 
+-    if(!server->type->player_packet)
+-    {
+-	debug(0, "error: server %p has no player_packet", server);
+-	return;
+-    }
++	if ( flags & COMBINE_VALUES )
++	{
++		for (info = player->info; info; info = info->next)
++		{
++			if (0 == strcmp(info->name, key))
++			{
++				char *full_value = (char*)calloc(sizeof(char), strlen(info->value) + strlen(value) + 2);
++				if (NULL == full_value)
++				{
++					fprintf(stderr, "Failed to malloc combined value\n");
++					exit(1);
++				}
++				sprintf(full_value, "%s%s%s", info->value, multi_delimiter, value);
+ 
+-    if ( server->type->id == Q_SERVER)
+-	q_player.data[0]= server->next_player_info;
+-    rc= send( server->fd, (const char *) server->type->player_packet,
+-	server->type->player_len, 0);
+-    if ( rc == SOCKET_ERROR)
+-	perror( "send");
++				// We should be able to free this
++				free(info->value);
++				info->value = full_value;
+ 
+-setup_retry:
+-    if ( server->retry2 == n_retries)  {
+-	gettimeofday( &server->packet_time2, NULL);
+-	server->n_requests++;
+-    }
+-    else
+-	server->n_retries++;
+-    server->retry2--;
+-    server->n_packets++;
+-}
+ 
+-void qserver_disconnect(struct qserver* server)
+-{
+-#ifdef _WIN32
+-    int i;
+-#endif
+-    if ( server->fd != -1)  {
+-	close( server->fd);
+-#ifndef _WIN32
+-	connmap[server->fd]= NULL;
+-#else
+-	for ( i= 0; i < max_connmap; i++)  {
+-	    if ( connmap[i] == server)  {
+-		connmap[i]= NULL;
+-		break;
+-	    }
++				return info;
++			}
++		}
+ 	}
+-#endif
+-	server->fd= -1;
+-	connected--;
+-    }
+-}
+ 
+-/* Functions for figuring timeouts and when to give up
+- * Returns 1 if the query is done (server may be freed) and 0 if not.
+- */
+-int
+-cleanup_qserver( struct qserver *server, int force )
+-{
+-	int close_it = force;
+-	if ( server->server_name == NULL)
++	info = (struct info*)malloc(sizeof(struct info));
++	if ( flags & NO_KEY_COPY )
++	{
++		info->name = key;
++	}
++	else
++	{
++		info->name = strdup(key);
++	}
++	if ( flags & NO_VALUE_COPY )
+ 	{
+-		close_it = 1;
+-		if ( server->type->id & MASTER_SERVER && server->master_pkt != NULL)
+-		{
+-			server->server_name = MASTER;
+-		}
+-		else
+-		{
+-			server->server_name = TIMEOUT;
+-			num_servers_timed_out++;
+-		}
++		info->value = value;
+ 	}
+-	else if ( server->type->flags & TF_SINGLE_QUERY )
++	else
+ 	{
+-		close_it = 1;
++		info->value = strdup(value);
+ 	}
+-	else if (
+-		server->next_rule == NO_SERVER_RULES &&
+-		server->next_player_info >= server->num_players
+-	)
++	info->next = NULL;
++
++	if (NULL == player->info)
+ 	{
+-		close_it = 1;
++		player->info = info;
++	}
++	else
++	{
++		*player->last_info = info;
+ 	}
++	player->last_info = &info->next;
++	player->n_info++;
++
++	return info;
++}
+ 
+-	debug( 3, "close_it %d", close_it );
+-	if ( close_it )
++struct rule *add_rule(struct qserver *server, char *key, char *value, int flags)
++{
++	struct rule *rule;
++	if ( flags & OVERWITE_DUPLICATES )
+ 	{
+-		if ( server->saved_data.data)
++		for (rule = server->rules; rule; rule = rule->next)
+ 		{
+-			SavedData *sdata= server->saved_data.next;
+-			free(server->saved_data.data);
+-			server->saved_data.data= NULL;
+-			while ( sdata != NULL)
++			if ( 0 == strcmp( rule->name, key) )
+ 			{
+-				SavedData *next;
+-				free(sdata->data);
+-				next= sdata->next;
+-				free(sdata);
+-				sdata= next;
++				// We should be able to free this
++				free(rule->value);
++				if ( flags & NO_VALUE_COPY )
++				{
++					rule->value = value;
++				}
++				else
++				{
++					rule->value = strdup(value);
++				}
++
++				return rule;
+ 			}
+-			server->saved_data.next= NULL;
+ 		}
++	}
+ 
+-		qserver_disconnect(server);
+-
+-		if ( server->server_name != TIMEOUT)
++	if ( flags & CHECK_DUPLICATE_RULES )
++	{
++		for (rule = server->rules; rule; rule = rule->next)
+ 		{
+-			num_servers_returned++;
+-			if ( server->server_name != DOWN)
++			if (0 == strcmp(rule->name, key))
+ 			{
+-				num_players_total+= server->num_players;
+-				max_players_total+= server->max_players;
++				return NULL;
+ 			}
+ 		}
+-		if ( server->server_name == TIMEOUT || server->server_name == DOWN)
+-		{
+-			server->ping_total= 999999;
+-		}
+-		if ( server->type->master)
++	}
++
++	if ( flags & COMBINE_VALUES )
++	{
++		for (rule = server->rules; rule; rule = rule->next)
+ 		{
+-			waiting_for_masters--;
+-			if ( waiting_for_masters == 0)
++			if (0 == strcmp(rule->name, key))
+ 			{
+-				add_servers_from_masters();
++				char *full_value = (char*)calloc(sizeof(char), strlen(rule->value) + strlen(value) + strlen(multi_delimiter) + 1);
++				if (NULL == full_value)
++				{
++					fprintf(stderr, "Failed to malloc combined value\n");
++					exit(1);
++				}
++				sprintf(full_value, "%s%s%s", rule->value, multi_delimiter, value);
++
++				// We should be able to free this
++				free(rule->value);
++				rule->value = full_value;
++
++				return rule;
+ 			}
+ 		}
+-		if ( ! server_sort)
+-		{
+-			display_server( server);
+-		}
+-		return 1;
+ 	}
+-	return 0;
+-}
+-
+-/** must be called only on connected servers
+- * @returns time in ms until server needs timeout handling. timeout handling is needed if <= zero
+- */
+-static int qserver_get_timeout(struct qserver* server, struct timeval* now)
+-{
+-	int diff, diff1, diff2, interval;
+ 
+-	if ( server->type->id & MASTER_SERVER)
+-		interval= master_retry_interval;
++	rule = (struct rule*)malloc(sizeof(struct rule));
++	if ( flags & NO_KEY_COPY )
++	{
++		rule->name = key;
++	}
+ 	else
+-		interval= retry_interval;
+-
+-	diff2= 0xffff;
+-
+-	diff1= interval*(n_retries-server->retry1+1) -
+-		time_delta( now, &server->packet_time1);
+-
+-	if (server->next_player_info < server->num_players)
+ 	{
+-		diff2= interval*(n_retries-server->retry2+1) -
+-			time_delta( now, &server->packet_time2);
++		rule->name = strdup(key);
+ 	}
+ 
+-	debug(2, "timeout for %p is diff1 %d diff2 %d", server, diff1, diff2);
+-
+-
+-	diff= (diff1<diff2)?diff1:diff2;
++	if ( flags &NO_VALUE_COPY )
++	{
++		rule->value = value;
++	}
++	else
++	{
++		rule->value = strdup(value);
++	}
++	rule->next = NULL;
++	*server->last_rule = rule;
++	server->last_rule = &rule->next;
++	server->n_rules++;
+ 
+-	return diff;
++	return rule;
+ }
+ 
+-void
+-get_next_timeout( struct timeval *timeout)
++void add_nrule(struct qserver *server, char *key, char *value, int len)
+ {
+-    struct qserver *server= servers;
+-    struct timeval now;
+-    int diff, smallest= retry_interval+master_retry_interval;
+-    int bind_count= 0;
+-
+-    if ( first_server_bind == NULL)
+-	first_server_bind= servers;
+-
+-    server= first_server_bind;
+-
+-    for ( ; server != NULL && server->fd == -1; server= server->next)
+-	;
+-
+-    /* if there are unconnected servers and slots left we retry in 10ms */
+-    if ( server == NULL || (num_servers > connected && connected < max_simultaneous))  {
+-	timeout->tv_sec= 0;
+-	timeout->tv_usec= 10 * 1000;
+-	return;
+-    }
+-    first_server_bind= server;
+-
+-    gettimeofday( &now, NULL);
+-    for ( ; server != NULL && bind_count < connected; server= server->next)  {
+-	if ( server->fd == -1)
+-	    continue;
+-
+-	diff = qserver_get_timeout(server, &now);
+-	if ( diff < smallest)
+-	    smallest= diff;
+-	bind_count++;
+-    }
+-
+-    if ( smallest < 10)
+-	smallest= 10;
++	struct rule *rule;
++	for (rule = server->rules; rule; rule = rule->next)
++	if (strcmp(rule->name, key) == 0)
++	{
++		return ;
++	}
+ 
+-    timeout->tv_sec= smallest / 1000;
+-    timeout->tv_usec= (smallest % 1000) * 1000;
++	rule = (struct rule*)malloc(sizeof(struct rule));
++	rule->name = strdup(key);
++	rule->value = strndup(value, len);
++	rule->next = NULL;
++	*server->last_rule = rule;
++	server->last_rule = &rule->next;
++	server->n_rules++;
+ }
+ 
+-
+-#ifdef USE_SELECT
+-static fd_set select_read_fds;
+-static int select_maxfd;
+-static int select_cursor;
+-
+-int
+-set_fds( fd_set *fds)
++struct player *add_player(struct qserver *server, int player_number)
+ {
+-	int maxfd= -1, i;
++	struct player *player;
+ 
+-	for ( i= 0; i < max_connmap; i++)
++	for (player = server->players; player; player = player->next)
+ 	{
+-		if ( connmap[i] != NULL)
++		if (player->number == player_number)
+ 		{
+-			FD_SET( connmap[i]->fd, fds);
+-			if ( connmap[i]->fd > maxfd)
+-			{
+-				maxfd= connmap[i]->fd;
+-			}
++			return NULL;
+ 		}
+ 	}
+ 
+-	return maxfd;
+-}
+-
+-void
+-set_file_descriptors()
+-{
+-    FD_ZERO( &select_read_fds);
+-    select_maxfd= set_fds( &select_read_fds);
++	player = (struct player*)calloc(1, sizeof(struct player));
++	player->number = player_number;
++	player->next = server->players;
++	player->n_info = 0;
++	player->ping = NA_INT;
++	player->team = NA_INT;
++	player->score = NA_INT;
++	player->deaths = NA_INT;
++	player->frags = NA_INT;
++	player->last_info = NULL;
++	server->players = player;
++	server->n_player_info++;
++	return player;
+ }
+ 
+-int
+-wait_for_file_descriptors( struct timeval *timeout)
++STATIC struct player *get_player_by_number(struct qserver *server, int player_number)
+ {
+-    select_cursor= 0;
+-    return select( select_maxfd+1, &select_read_fds, NULL, NULL, timeout);
++	struct player *player;
++	for (player = server->players; player; player = player->next)
++	if (player->number == player_number)
++	{
++		return player;
++	} return NULL;
+ }
+ 
+-struct qserver *
+-get_next_ready_server()
++// Updates a servers port information.
++// Sets the rules:
++// _queryport <queryport>
++// hostport <port>
++void change_server_port(struct qserver *server, unsigned short port, int force)
+ {
+-	while ( select_cursor < max_connmap &&
+-		( connmap[select_cursor] == NULL ||
+-		! FD_ISSET( connmap[select_cursor]->fd, &select_read_fds)) )
++	if (port > 0 && port != server->port)
+ 	{
+-		select_cursor++;
+-	}
+-	if ( select_cursor >= max_connmap)
+-		return NULL;
+-	return connmap[select_cursor++];
+-}
++		// valid port and changing
++		char arg[64];
++		unsigned int ipaddr = ntohl(server->ipaddr);
+ 
+-int
+-wait_for_timeout( unsigned int ms)
+-{
+-    struct timeval timeout;
+-    timeout.tv_sec= ms/1000;
+-    timeout.tv_usec= (ms%1000) * 1000;
+-    return select( 0, 0, NULL, NULL, &timeout);
+-}
++		// Update the servers hostname as required
++		sprintf(arg, "%d.%d.%d.%d:%hu", ipaddr >> 24, (ipaddr >> 16) &0xff, (ipaddr >> 8) &0xff, ipaddr &0xff, port);
+ 
+-#endif /* USE_SELECT */
++		if (show_game_port || force || server->flags &TF_SHOW_GAME_PORT)
++		{
++			// Update the server arg
++			free(server->arg);
++			server->arg = strdup(arg);
+ 
+-#ifdef USE_POLL
+-static struct pollfd *pollfds;
+-static int n_pollfds;
+-static int max_pollfds= 0;
+-static int poll_cursor;
++			// Add a rule noting the previous query port
++			sprintf(arg, "%hu", server->port);
++			add_rule(server, "_queryport", arg, NO_FLAGS);
+ 
+-void
+-set_file_descriptors()
+-{
+-    struct pollfd *p;
+-    int i;
++			// Update the servers port
++			server->port = port;
++		}
+ 
+-    if ( max_connmap > max_pollfds)  {
+-	max_pollfds= max_connmap;
+-	pollfds= (struct pollfd *) realloc( pollfds, max_pollfds *
+-		sizeof(struct pollfd));
+-    }
++		if ( 0 != strcmp(server->arg, server->host_name) )
++		{
++			// hostname isnt the query arg
++			char *colon = strchr(server->host_name, ':');
++			// dns hostname or hostname:port
++			char *hostname = malloc( strlen(server->host_name) + 7 );
++			if ( NULL == hostname )
++			{
++				fprintf(stderr, "Failed to malloc hostname memory\n");
++			}
++			else
++			{
++				if (colon)
++				{
++					*colon = '\0';
++				}
++				sprintf(hostname, "%s:%hu", server->host_name, port);
++				free(server->host_name);
++				server->host_name = hostname;
++			}
++		}
+ 
+-    p= pollfds;
+-    for ( i= 0; i < max_connmap; i++)
+-	if ( connmap[i] != NULL)  {
+-	    p->fd= connmap[i]->fd;
+-	    p->events= POLLIN;
+-	    p->revents= 0;
+-	    p++;
++		// Add a rule noting the servers hostport
++		sprintf(arg, "%hu", port);
++		add_rule(server, "hostport", arg, OVERWITE_DUPLICATES);
+ 	}
+-    n_pollfds= p - pollfds;
+ }
+ 
+-int
+-wait_for_file_descriptors( struct timeval *timeout)
++STATIC void players_set_teamname(struct qserver *server, int teamid, char *teamname)
+ {
+-    poll_cursor= 0;
+-    return poll( pollfds, n_pollfds, timeout->tv_sec*1000 + timeout->tv_usec/1000);
++	struct player *player;
++	for (player = server->players; player; player = player->next)
++	{
++		if (player->team == teamid)
++		{
++			player->team_name = strdup(teamname);
++		}
++	}
+ }
+ 
+-struct qserver *
+-get_next_ready_server()
++STATIC char *dup_nstring(const char *pkt, const char *end, char **next)
+ {
+-    for ( ; poll_cursor < n_pollfds; poll_cursor++)
++	char *pt = (char*)pkt;
++	int len = ((unsigned char*)pkt)[0];
++	pt++;
++	if (*pt == '\1')
+ 	{
+-		if ( pollfds[ poll_cursor].revents)
+-			break;
++		len++;
+ 	}
+-    if ( poll_cursor >= n_pollfds)
++	if (pt + len > end)
++	{
+ 		return NULL;
+-    return connmap[pollfds[poll_cursor++].fd];
+-}
+-
+-int
+-wait_for_timeout( unsigned int ms)
+-{
+-    return poll( 0, 0, ms);
+-}
+-
+-#endif /* USE_POLL */
+-
+-
+-void
+-free_server( struct qserver *server)
+-{
+-    struct player *player, *next_player;
+-    struct rule *rule, *next_rule;
+-
+-    /* remove from servers list */
+-    if ( server == servers)  {
+-	servers= server->next;
+-	if ( servers)
+-	    servers->prev= NULL;
+-    }
+-    if ( (void*) &server->next == (void*) last_server)  {
+-	if ( server->prev)
+-	    last_server= & server->prev->next;
+-	else
+-	    last_server= & servers;
+-    }
+-    if ( server == first_server_bind)
+-	first_server_bind= server->next;
+-    if ( server == last_server_bind)
+-	last_server_bind= server->next;
+-
+-    if ( server->prev)
+-	server->prev->next= server->next;
+-    if ( server->next)
+-	server->next->prev= server->prev;
+-
+-    /* remove from server hash table */
+-    remove_server_from_hash( server);
+-
+-    /* free all the data */
+-    for ( player= server->players; player; player= next_player)  {
+-	next_player= player->next;
+-	free_player( player);
+-    }
+-
+-    for ( rule= server->rules; rule; rule= next_rule)  {
+-	next_rule= rule->next;
+-	free_rule( rule);
+-    }
+-
+-    if ( server->arg) free( server->arg);
+-    if ( server->host_name) free( server->host_name);
+-    if ( server->error) free( server->error);
+-    if ( server->address) free( server->address);
+-    if ( server->map_name) free( server->map_name);
+-    if ( !(server->flags&FLAG_DO_NOT_FREE_GAME) && server->game)
+-	free(server->game);
+-    if ( server->master_pkt) free( server->master_pkt);
+-
+-    free(server->query_arg);
+-
+-    /* These fields are never malloc'd: outfilename
+-    */
+-
+-    if ( server->server_name != NULL &&
+-	    server->server_name != DOWN &&
+-	    server->server_name != HOSTNOTFOUND &&
+-	    server->server_name != SYSERROR &&
+-	    server->server_name != MASTER &&
+-	    server->server_name != SERVERERROR &&
+-	    server->server_name != TIMEOUT &&
+-	    server->server_name != GAMESPY_MASTER_NAME &&
+-	    server->server_name != BFRIS_SERVER_NAME)  {
+-	free( server->server_name);
+-    }
+-
+-/*
+-params ...
+-saved_data ...
+-*/
+-
+-    free( server);
+-    --num_servers;
+-}
+-
+-void
+-free_player( struct player *player)
+-{
+-    if ( player->name) free( player->name);
+-    if ( ! (player->flags&PLAYER_FLAG_DO_NOT_FREE_TEAM) && player->team_name)
+-	free( player->team_name);
+-    if ( player->address) free( player->address);
+-    if ( player->tribe_tag) free( player->tribe_tag);
+-    if ( player->skin) free( player->skin);
+-    if ( player->mesh) free( player->mesh);
+-    if ( player->face) free( player->face);
+-    free( player);
+-}
+-
+-void
+-free_rule( struct rule *rule)
+-{
+-    if ( rule->name) free( rule->name);
+-    if ( rule->value) free( rule->value);
+-    free( rule);
+-}
+-
+-/* Functions for handling response packets
+- */
+-
+-/* Packet from normal Quake server
+- */
+-void
+-deal_with_q_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+-    struct q_packet *pkt= (struct q_packet *)rawpkt;
+-    int rc;
+-
+-    if ( ntohs( pkt->length) != pktlen)  {
+-	fprintf( stderr, "%s Ignoring bogus packet; length %d != %d\n",
+-		server->arg, ntohs( pkt->length), pktlen);
+-	cleanup_qserver(server,FORCE);
+-	return;
+-    }
+-
+-    rawpkt[pktlen]= '\0';
+-
+-    switch ( pkt->op_code)  {
+-    case Q_CCREP_ACCEPT:
+-    case Q_CCREP_REJECT:
+-	return;
+-    case Q_CCREP_SERVER_INFO:
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-	rc= server_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+-	break;
+-    case Q_CCREP_PLAYER_INFO:
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time2);
+-	rc= player_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+-	break;
+-    case Q_CCREP_RULE_INFO:
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-	rc= rule_info_packet( server, pkt, pktlen-Q_HEADER_LEN);
+-	break;
+-    case Q_CCREQ_CONNECT:
+-    case Q_CCREQ_SERVER_INFO:
+-    case Q_CCREQ_PLAYER_INFO:
+-    case Q_CCREQ_RULE_INFO:
+-    default:
+-	return;
+-    }
+-
+-    if ( rc == -1)
+-	fprintf( stderr, "%s error on packet opcode %x\n", server->arg,
+-		(int)pkt->op_code);
++	}
+ 
+-    cleanup_qserver( server, (rc == -1) ? FORCE : 0);
++	*next = pt + len;
++	return strndup(pt, len);
+ }
+ 
+-/* Packet from QuakeWorld server
+- */
+-void
+-deal_with_qw_packet( struct qserver *server, char *rawpkt, int pktlen)
++STATIC char *dup_n1string(char *pkt, char *end, char **next)
+ {
+-    if ( server->server_name == NULL)
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-
+-    if ( ((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' ||
+-	    rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors)  {
+-	unsigned int ipaddr= ntohl(server->ipaddr);
+-	fprintf( stderr,
+-		"Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
+-		(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-		(ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+-	print_packet( server, rawpkt, pktlen);
+-    }
++	unsigned len;
+ 
+-    rawpkt[pktlen]= '\0';
+-
+-    if ( rawpkt[4] == 'n')  {
+-	if ( server->type->id != QW_SERVER)
+-	    server->type= find_server_type_id( QW_SERVER);
+-	deal_with_q1qw_packet( server, rawpkt, pktlen);
+-	return;
+-    }
+-    else if ( rawpkt[4] == '\377' && rawpkt[5] == 'n')  {
+-	if ( server->type->id != HW_SERVER)
+-	    server->type= find_server_type_id( HW_SERVER);
+-	deal_with_q1qw_packet( server, rawpkt, pktlen);
+-	return;
+-    }
+-    else if ( strncmp( &rawpkt[4], "print\n\\", 7) == 0)  {
+-	deal_with_q2_packet( server, rawpkt+10, pktlen-10, 0);
+-	return;
+-    }
+-    else if ( strncmp( &rawpkt[4], "print\n", 6) == 0)  {
+-	/* work-around for occasional bug in Quake II status packets
+-	*/
+-	char *c, *p;
+-	p= c= &rawpkt[10];
+-	while ( *p != '\\' && (c= strchr( p, '\n')))
+-	    p= c+1;
+-	if ( *p == '\\' && c != NULL)  {
+-	    deal_with_q2_packet( server, p, pktlen-(p-rawpkt), 0);
+-	    return;
++	if (!pkt || pkt >= end)
++	{
++		return NULL;
+ 	}
+-    }
+-    else if ( strncmp( &rawpkt[4], "infoResponse", 12) == 0 ||
+-		(rawpkt[4] == '\001' && strncmp( &rawpkt[5], "infoResponse", 12) == 0) )  {
+-	/* quake3 info response */
+-	if ( rawpkt[4] == '\001')  {
+-	    rawpkt++;
+-	    pktlen--;
+-	}
+-	rawpkt+= 12;
+-	pktlen-= 12;
+-	for ( ; pktlen && *rawpkt != '\\'; pktlen--, rawpkt++)
+-	    ;
+-	if ( !pktlen)
+-	    return;
+-	if ( rawpkt[pktlen-1] == '"')  {
+-	    rawpkt[pktlen-1]= '\0';
+-	    pktlen--;
+-	}
+-	if ( get_player_info || get_server_rules)
+-	    server->next_rule= "";
+-	deal_with_q2_packet( server, rawpkt, pktlen, 0);
+-	if ( (get_player_info || get_server_rules) &&
+-			( server->flags & FLAG_BROADCAST || server->fd != -1)) {
+-	    send_rule_request_packet( server);
+-	    server->retry1= n_retries-1;
++
++	len = (unsigned char)pkt[0] - 1;
++	pkt++;
++	if (pkt + len > end)
++	{
++		return NULL;
+ 	}
+-	return;
+-    }
+-    else if ( strncmp( &rawpkt[4], "statusResponse\n", 15) == 0 ||
+-		(rawpkt[4] == '\001' && strncmp( &rawpkt[5], "statusResponse\n", 15) == 0) )  {
+-	/* quake3 status response */
+-	server->next_rule= NO_SERVER_RULES;
+-	server->retry1 = 0;
+-	if ( rawpkt[4] == '\001')  {
+-	    rawpkt++;
+-	    pktlen--;
+-	}
+-    	deal_with_q2_packet( server, rawpkt + 19, pktlen - 19,
+-		CHECK_DUPLICATE_RULES);
+-	return;
+-    }
+-    else if ( strncmp( &rawpkt[4], "infostringresponse", 19) == 0)  {
+-	deal_with_q2_packet( server, rawpkt+23, pktlen-23, 0);
+-	return;
+-    }
+ 
+-    if ( show_errors)  {
+-	unsigned int ipaddr= ntohl(server->ipaddr);
+-	fprintf( stderr,
+-		"Odd packet from server %d.%d.%d.%d:%hu, ignoring ...\n",
+-		(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-		(ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+-	print_packet( server, rawpkt, pktlen);
+-	cleanup_qserver( server, 1);
+-    }
+-    else
+-	cleanup_qserver( server, 0);
++	*next = pkt + len;
++	return strndup(pkt, len);
+ }
+ 
+-void
+-deal_with_q1qw_packet( struct qserver *server, char *rawpkt, int pktlen)
++STATIC int pariah_basic_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+-    char *key, *value, *end;
+-    struct player *player= NULL, **last_player= &server->players;
+-    int len, rc, complete= 0;
+-    int number, frags, connect_time, ping;
+-    char *pkt= &rawpkt[5];
+-
+-    if ( server->type->id == HW_SERVER)
+-	pkt= &rawpkt[6];
+-
+-    while ( *pkt && pkt-rawpkt < pktlen)  {
+-	if ( *pkt == '\\')  {
+-	    pkt++;
+-	    end= strchr( pkt, '\\');
+-	    if ( end == NULL)
+-		break;
+-	    *end= '\0';
+-	    key= pkt;
+-	    pkt+= strlen(pkt)+1;
+-	    end= strchr( pkt, '\\');
+-	    if ( end == NULL)
+-		end= strchr( pkt, '\n');
+-	    value= (char*) malloc(end-pkt+1);
+-	    memcpy( value, pkt, end-pkt);
+-	    value[end-pkt]= '\0';
+-	    pkt= end;
+-	    if ( strcmp( key, "hostname") == 0)
+-		server->server_name= value;
+-	    else if  ( strcmp( key, "map") == 0)
+-		server->map_name= value;
+-	    else if  ( strcmp( key, "maxclients") == 0)  {
+-		server->max_players= atoi(value);
+-		free( value);
+-	    }
+-	    else if ( get_server_rules || strncmp( key, "*game", 5) == 0)  {
+-		add_rule( server, key, value, NO_VALUE_COPY);
+-		if ( strcmp( key, "*gamedir") == 0)  {
+-		    server->game= value;
+-		    server->flags |= FLAG_DO_NOT_FREE_GAME;
+-		}
+-	    }
+-	}
+-	else if ( *pkt == '\n')  {
+-	    pkt++;
+-	    if ( pkt-rawpkt>=pktlen || *pkt == '\0')
+-		break;
+-	    rc= sscanf( pkt, "%d %d %d %d %n", &number, &frags, &connect_time,
+-		&ping, &len);
+-	    if ( rc != 4)  {
+-		char *nl;	/* assume it's an error packet */
+-		server->error= (char*)malloc( pktlen+1);
+-	        nl= strchr( pkt, '\n');
+-		if ( nl != NULL)  {
+-		    strncpy( server->error, pkt, nl-pkt);
+-		    server->error[nl-pkt]= '\0';
+-		}
+-		else
+-		    strcpy( server->error, pkt);
+-		server->server_name= SERVERERROR;
+-		complete= 1;
+-		break;
+-	    }
+-	    if ( get_player_info)  {
+-		player= (struct player *) calloc( 1, sizeof( struct player));
+-		player->number= number;
+-		player->frags= frags;
+-		player->connect_time= connect_time * 60;
+-		player->ping= ping;
+-	    }
+-	    else
+-		player= NULL;
+-
+-	    pkt+= len;
+-
+-	    if ( *pkt != '"') break;
+-	    pkt++;
+-	    end= strchr( pkt, '"');
+-	    if ( end == NULL) break;
+-	    if ( player != NULL)  {
+-		player->name= (char*) malloc(end-pkt+1);
+-		memcpy( player->name, pkt, end-pkt);
+-		player->name[end-pkt]= '\0';
+-	    }
+-	    pkt= end+2;
+-
+-	    if ( *pkt != '"') break;
+-	    pkt++;
+-	    end= strchr( pkt, '"');
+-	    if ( end == NULL) break;
+-	    if ( player != NULL)  {
+-		player->skin= (char*) malloc(end-pkt+1);
+-		memcpy( player->skin, pkt, end-pkt);
+-		player->skin[end-pkt]= '\0';
+-	    }
+-	    pkt= end+2;
+-
+-	    if ( player != NULL)  {
+-		sscanf( pkt, "%d %d%n", &player->shirt_color,
+-			&player->pants_color, &len);
+-		*last_player= player;
+-		last_player= & player->next;
+-	    }
+-	    else
+-		sscanf( pkt, "%*d %*d%n", &len);
+-	    pkt+= len;
+-
+-	    server->num_players++;
+-	}
+-	else
+-	    pkt++;
+-	complete= 1;
+-    }
+-
+-    if ( !complete)  {
+-	if ( rawpkt[4] != 'n' || rawpkt[5] != '\0')  {
+-	    fprintf( stderr,
+-		"Odd packet from QW server %d.%d.%d.%d:%hu ...\n",
+-		(server->ipaddr>>24)&0xff, (server->ipaddr>>16)&0xff,
+-		(server->ipaddr>>8)&0xff, server->ipaddr&0xff,
+-		ntohs(server->port));
+-	    print_packet( server, rawpkt, pktlen);
++	char *next;
++	char *string;
++	change_server_port(server, swap_short_from_little(&rawpkt[14]), 0);
++	if (NULL == (string = ut2003_strdup(&rawpkt[18], end, &next)))
++	{
++		return -1;
+ 	}
+-    }
+-    else if ( server->server_name == NULL)
+-	server->server_name= strdup("");
+-
+-    cleanup_qserver( server, 0);
+-}
+ 
+-void
+-deal_with_q2_packet( struct qserver *server, char *rawpkt, int pktlen,
+-	int check_duplicate_rules)
+-{
+-    char *key, *value, *end;
+-    struct player *player= NULL;
+-    struct player **last_player= & server->players;
+-    int len, rc, complete= 0;
+-    int frags=0, ping=0, num_players= 0;
+-    char *pkt= rawpkt;
+-
+-    while ( *pkt && pkt-rawpkt < pktlen)  {
+-	if ( *pkt == '\\')  {
+-	    pkt++;
+-	    if ( *pkt == '\n' && server->type->id == SOF_SERVER)
+-		goto player_info;
+-	    end= strchr( pkt, '\\');
+-	    if ( end == NULL)
+-		break;
+-	    *end= '\0';
+-	    key= pkt;
+-	    pkt+= strlen(pkt)+1;
+-	    end= strpbrk( pkt, "\\\n");
+-	    if(!end) {
+-		    end= rawpkt+pktlen;
+-	    }
+-	    value= (char*) malloc(end-pkt+1);
+-	    memcpy( value, pkt, end-pkt);
+-	    value[end-pkt]= '\0';
+-	    pkt= end;
+-	    debug(3, "%s = %s", key, value);
+-	    if ( server->server_name == NULL &&
+-			(strcmp( key, "hostname") == 0 ||
+-			strcmp( key, "sv_hostname") == 0))
+-		server->server_name= value;
+-	    else if  ( strcmp( key, "mapname") == 0 ||
+-		    (strcmp( key, "map") == 0 && server->map_name == NULL))  {
+-		if ( server->map_name != NULL)
+-		    free( server->map_name);
+-		server->map_name= value;
+-	    }
+-	    else if  ( strcmp( key, "maxclients") == 0 ||
+-	    		strcmp( key, "sv_maxclients") == 0 ||
+-			strcmp( key, "max") == 0)  {
+-		server->max_players= atoi(value);
+-		/* MOHAA Q3 protocol max players is always 0 */
+-		if ( server->max_players == 0)
+-		    server->max_players= -1;
+-		free(value);
+-	    }
+-	    else if  ( strcmp( key, "clients") == 0 ||
+-			strcmp( key, "players") == 0)  {
+-		server->num_players= atoi(value);
+-		free(value);
+-	    }
+-	    else if ( server->server_name == NULL &&
+-			strcmp( key, "pure") == 0)  {
+-		add_rule( server, key, value, NO_VALUE_COPY);
+-	    }
+-	    else if ( get_server_rules || strncmp( key, "game", 4) == 0)
+-		{
+-			if ( add_rule( server, key, value, NO_VALUE_COPY|check_duplicate_rules) == NULL)
+-			{
+-				free(value);      /* duplicate, so free value */
+-			}
+-			if ( server->game == NULL && strcmp( key, server->type->game_rule) == 0)
+-			{
+-				server->game= value;
+-				server->flags |= FLAG_DO_NOT_FREE_GAME;
+-			}
+-	    }
+-	    else
+-		free(value);
+-	}
+-	else if ( *pkt == '\n')  {
+-player_info:
+-	    pkt++;
+-	    if ( *pkt == '\0')
+-		break;
+-	    if(!strncmp(pkt, "\\challenge\\", 11))
+-		{
+-			// qfusion
+-			// This doesnt support getstatus looking at warsow source:
+-			// server/sv_main.c: SV_ConnectionlessPacket
+-			server->next_rule = NO_SERVER_RULES;
+-		    break;
+-		}
+-	    rc= sscanf( pkt, "%d %n", &frags, &len);
+-	    if ( rc == 1 && pkt[len] != '"')  {
+-		pkt+= len;
+-		rc= sscanf( pkt, "%d %n", &ping, &len);
+-	    }
+-	    else if ( rc == 1)  {
+-		/* MOHAA Q3 protocol only provides player ping */
+-		ping= frags;
+-		frags= 0;
+-	    }
+-	    if ( rc != 1)  {
+-		char *nl;	/* assume it's an error packet */
+-		server->error= (char*)malloc( pktlen+1);
+-	        nl= strchr( pkt, '\n');
+-		if ( nl != NULL)
+-		    strncpy( server->error, pkt, nl-pkt);
+-		else
+-		    strcpy( server->error, pkt);
+-		server->server_name= SERVERERROR;
+-		complete= 1;
+-		break;
+-	    }
+-	    if ( get_player_info)  {
+-		player= (struct player *) calloc( 1, sizeof( struct player));
+-		player->number= 0;
+-		player->connect_time= -1;
+-		player->frags= frags;
+-		player->ping= ping;
+-	    }
+-	    else
+-		player= NULL;
+-
+-	    pkt+= len;
+-
+-	    if ( isdigit((unsigned char)*pkt))  {
+-		/* probably an SOF2 1.01 server, includes team # */
+-		int team;
+-		rc= sscanf( pkt, "%d %n", &team, &len);
+-		if ( rc == 1)  {
+-		    pkt+= len;
+-		    if ( player)  {
+-			player->team= team;
+-			server->flags|= FLAG_PLAYER_TEAMS;
+-		    }
+-		}
+-	    }
+-
+-	    if ( *pkt != '"') break;
+-
+-	    pkt++;
+-	    end= strchr( pkt, '"');
+-	    if ( end == NULL) break;
+-	    if ( player != NULL)  {
+-		player->name= (char*) malloc(end-pkt+1);
+-		memcpy( player->name, pkt, end-pkt);
+-		player->name[end-pkt]= '\0';
+-	    }
+-	    pkt= end+1;
+-
+-	    //WarSoW team number
+-	    if ( *pkt != '\n') {
+-		int team;
+-		rc= sscanf( pkt, "%d%n", &team, &len);
+-		if ( rc == 1)  {
+-		    pkt+= len;
+-		    if ( player)  {
+-			player->team= team;
+-			server->flags|= FLAG_PLAYER_TEAMS;
+-		    }
+-		}
+-	    }
+-
+-	    if ( player != NULL)  {
+-		player->skin= NULL;
+-		player->shirt_color= -1;
+-		player->pants_color= -1;
+-		*last_player= player;
+-		last_player= & player->next;
+-	    }
+-	    num_players++;
++	if (server->server_name == NULL)
++	{
++		server->server_name = string;
+ 	}
+ 	else
+-	    pkt++;
+-	complete= 1;
+-    }
+-
+-    if ( server->num_players == 0 || num_players > server->num_players)
+-	server->num_players= num_players;
+-
+-    if ( !complete)  {
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
+-    else if ( server->server_name == NULL)
+-	server->server_name= strdup("");
+-
+-    cleanup_qserver( server, 0);
+-}
+-
+-void
+-ack_descent3master_packet( struct qserver *server, char *curtok )
+-{
+-    int rc;
+-    char packet[0x1e];
+-    memcpy( packet, descent3_masterquery,0x1a);
+-    packet[1]= 0x1d;
+-    packet[0x16]= 1;
+-    memcpy( packet + 0x1a, curtok, 4);
+-    rc= send( server->fd, packet, sizeof(packet), 0);
+-    if ( rc == SOCKET_ERROR)
+-	perror( "send");
+-}
+-
+-/* Packet from Descent3 master server (PXO)
+- */
+-void
+-deal_with_descent3master_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+-    int i= 0, lastpacket= 0;
+-    char *names= rawpkt + 0x1f;
+-    char *ips= rawpkt + 0x29f;
+-    char *ports= rawpkt + 0x2ef;
+-    /* printf ("s=%p p=%p l=%i\n",server,rawpkt,pktlen); */
+-    while ( i<20) {
+-	if ( *names) {
+-	    char *c;
+-	    server->master_pkt_len += 6;
+-	    server->master_pkt= (char*)realloc( server->master_pkt,
+-		    server->master_pkt_len);
+-	    c= server->master_pkt + server->master_pkt_len - 6;
+-	    memcpy( c, ips, 4);
+-	    memcpy( c + 4, ports, 2);
+-	}else if (i>0)
+-	    lastpacket= 1;
+-	names+= 0x20;
+-	ips+= 4;
+-	ports+= 2;
+-	i++;
+-    }
+-
+-    ack_descent3master_packet( server, rawpkt+0x1a);
+-
+-    server->n_servers= server->master_pkt_len / 6;
+-
+-    server->next_player_info= -1;
+-    server->retry1= 0;
+-
+-    if (lastpacket) {
+-	cleanup_qserver( server, 0);
+-    }
+-}
+-
+-/* Packet from QuakeWorld master server
+- */
+-void
+-deal_with_qwmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-
+-	if ( rawpkt[0] == QW_NACK)
+ 	{
+-		server->error= strdup( &rawpkt[2]);
+-		server->server_name= SERVERERROR;
+-		cleanup_qserver( server, 1);
+-		return;
++		free(string);
+ 	}
+ 
+-	if ( *((unsigned int*)rawpkt) == 0xffffffff)
++	if (NULL == (string = ut2003_strdup(next, end, &next)))
+ 	{
+-		rawpkt+= 4;	/* QW 1.5 */
+-		pktlen-= 4;
++		return -1;
+ 	}
+ 
+-	if ( rawpkt[0] == QW_SERVERS && rawpkt[1] == QW_NEWLINE)
++	if (server->map_name == NULL)
+ 	{
+-		rawpkt+= 2;
+-		pktlen-= 2;
++		server->map_name = string;
+ 	}
+-	else if ( rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0d )
++	else
+ 	{
+-		// 2 byte id + 4 byte sequence
+-		memcpy( server->master_query_tag, rawpkt+2, 3);
+-		rawpkt+= 6;
+-		pktlen-= 6;
++		free(string);
+ 	}
+-	else if ( rawpkt[0] == HL_SERVERS && rawpkt[1] == 0x0a )
+-	{
+-		// no sequence id for steam
+-		// instead we use the ip:port of the last recieved server
+-		struct in_addr *sin_addr = (struct in_addr*)(rawpkt+pktlen-6);
+-		char *ip = inet_ntoa( *sin_addr );
+-		unsigned short port = htons( *((unsigned short*)(rawpkt+pktlen-2)) );
+-
+-		//fprintf( stderr, "NEXT IP=%s:%u\n", ip, port );
+-		sprintf( server->master_query_tag, "%s:%u", ip, port );
+ 
+-		// skip over the 2 byte id
+-		rawpkt+= 2;
+-		pktlen-= 2;
++	if (NULL == (string = ut2003_strdup(next, end, &next)))
++	{
++		return -1;
+ 	}
+-	else if ( strncmp( rawpkt, "servers", 7) == 0)
++
++	if (server->game == NULL)
+ 	{
+-		rawpkt+= 8;
+-		pktlen-= 8;
++		server->game = string;
++		add_rule(server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+ 	}
+-	else if ( strncmp( rawpkt, "getserversResponse", 18) == 0)
++	else
+ 	{
+-		static int q3m_debug= 0;
+-
+-		rawpkt+= 18;
+-		pktlen-= 18;
+-
+-		for ( ; *rawpkt != '\\' && pktlen; pktlen--, rawpkt++)
+-		{
+-		}
+-
+-		if ( !pktlen)
+-		{
+-			return;
+-		}
+-		rawpkt++;
+-		pktlen--;
++		free(string);
++	}
+ 
+-		if ( q3m_debug) printf( "q3m pktlen %d lastchar %x\n", pktlen, (unsigned int)rawpkt[pktlen-1]);
++	server->num_players = (unsigned char)next[0];
++	server->max_players = (unsigned char)next[1];
+ 
+-		server->master_pkt= (char*)realloc( server->master_pkt,
+-				server->master_pkt_len + pktlen+1);
++	return 0;
++}
+ 
+-		if ( server->type->id == STEF_MASTER)
+-		{
+-			decode_stefmaster_packet( server, rawpkt, pktlen);
+-		}
+-		else
+-		{
+-			decode_q3master_packet( server, rawpkt, pktlen);
+-		}
+-		if ( q3m_debug) printf( "q3m %d servers\n", server->n_servers);
++STATIC int ut2003_basic_packet(struct qserver *server, char *rawpkt, char *end)
++{
++	char *next;
++	char *string;
++	change_server_port(server, swap_short_from_little(&rawpkt[6]), 0);
+ 
+-		return;
+-	}
+-	else if ( show_errors)
++	if (NULL == (string = ut2003_strdup(&rawpkt[14], end, &next)))
+ 	{
+-		unsigned int ipaddr= ntohl(server->ipaddr);
+-		fprintf( stderr,
+-			"Odd packet from QW master %d.%d.%d.%d, processing ...\n",
+-			(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-			(ipaddr>>8)&0xff, ipaddr&0xff
+-		);
+-		print_packet( server, rawpkt, pktlen);
++		return -1;
+ 	}
+ 
+-	server->master_pkt= (char*)realloc( server->master_pkt,
+-	server->master_pkt_len+pktlen+1);
+-	rawpkt[pktlen]= '\0';
+-	memcpy( server->master_pkt+server->master_pkt_len, rawpkt, pktlen+1);
+-	server->master_pkt_len+= pktlen;
+-
+-	server->n_servers= server->master_pkt_len / 6;
+-
+-	if ( server->type->flags & TF_MASTER_MULTI_RESPONSE)
++	if (server->server_name == NULL)
+ 	{
+-		server->next_player_info= -1;
+-		server->retry1= 0;
++		server->server_name = string;
+ 	}
+-	else if ( server->type->id == HL_MASTER )
++	else
+ 	{
+-		if ( server->master_query_tag[0] == 0 &&
+-			server->master_query_tag[1] == 0 &&
+-			server->master_query_tag[2] == 0
+-		)
+-		{
+-			// all done
+-			server->server_name = MASTER;
+-			cleanup_qserver( server, 1);
+-			bind_sockets();
+-		}
+-		else
+-		{
+-			// more to come
+-			server->retry1++;
+-			send_qwmaster_request_packet( server);
+-		}
++		free(string);
+ 	}
+-	else if ( server->type->flags & TF_MASTER_STEAM )
++
++	if (NULL == (string = ut2003_strdup(next, end, &next)))
+ 	{
+-		// should the HL_MASTER be the same as this?
+-		int i;
+-		for ( i = pktlen - 6; i < pktlen && 0x00 == rawpkt[i] ; i++ )
+-		{
+-		}
++		return -1;
++	}
+ 
+-		if ( i == pktlen )
+-		{
+-			// last 6 bytes where 0x00 so we have reached the last packet
+-			server->n_servers--;
+-			server->master_pkt_len -= 6;
+-			server->server_name = MASTER;
+-			cleanup_qserver( server, 1);
+-			bind_sockets();
+-		}
+-		else
+-		{
+-			// more to come
+-			server->retry1++;
+-			send_qwmaster_request_packet( server );
+-		}
++	if (server->map_name == NULL)
++	{
++		server->map_name = string;
+ 	}
+ 	else
+ 	{
+-		server->server_name = MASTER;
+-		cleanup_qserver( server, 0);
+-		bind_sockets();
++		free(string);
+ 	}
+-}
+-
+-void
+-decode_q3master_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+-    char *p;
+-
+-    pkt[pktlen]= 0;
+-    p= pkt;
+-
+-    while ( *p && p < &pkt[pktlen-6]) {
+-	memcpy( server->master_pkt + server->master_pkt_len, &p[0], 4);
+-	memcpy( server->master_pkt + server->master_pkt_len + 4, &p[4], 2);
+-	server->master_pkt_len += 6;
+-	p+= 6;
+-	while ( *p && *p == '\\')
+-	    p++;
+-    }
+-    server->n_servers= server->master_pkt_len / 6;
+-//    server->next_player_info= -1; evil, causes busy loop!
+-    server->retry1 = 0; // received at least one packet so no need to retry
+-}
+-
+-void
+-decode_stefmaster_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+-    unsigned char *p, *m, *end;
+-    unsigned int i, b;
+-
+-    pkt[pktlen]= 0;
+-
+-    p= (unsigned char*) pkt;
+-    m= (unsigned char*) server->master_pkt + server->master_pkt_len;
+-    end= (unsigned char*) &pkt[pktlen-12];
+-    while ( *p && p < end)  {
+-	for ( i= 6; i; i--)  {
+-	    sscanf( (char*)p, "%2x", &b);
+-	    p+= 2;
+-	    *m++= b;
+-	}
+-	server->master_pkt_len += 6;
+-	while ( *p && *p == '\\')
+-	    p++;
+-    }
+-    server->n_servers= server->master_pkt_len / 6;
+-    server->next_player_info= -1;
+-    server->retry1= 0;
+-}
+-
+-/* Packet from Tribes master server
+- */
+-void
+-deal_with_tribesmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+-    unsigned char *upkt= (unsigned char*) rawpkt;
+-    int packet_number= upkt[2];
+-    int n_packets= upkt[3];
+-    unsigned char *p;
+-    char *mpkt;
+-    int len;
+-    unsigned int ipaddr;
+-
+-    if ( memcmp( rawpkt, tribes_master_response, sizeof(tribes_master_response)) != 0)  {
+-	fprintf( stderr, "Odd packet from Tribes master server\n");
+-	print_packet( server, rawpkt, pktlen);
+-    }
+ 
+-    /* 0x1006
+-       01		packet number
+-       08		# packets
+-       02
+-       0000
+-       66
+-       0d		length of following string
+-       "Tribes Master"
+-       3c		length of following string
+-       "Check out the Starsiege demo now!   www.starsiegeplayers.com"
+-       0035
+-       06 d143 4764 812c
+-       06 d1e2 8df3 616d
+-       06 1804 6d50 616d
+-       06 d81c 6dc0 616d
+-*/
+-/* 0x1006
+-   02
+-   08
+-   02 0000
+-   66
+-   00 3f
+-   06 cf88 344c 1227
+-*/
+-
+-/* printf( "packet_number %d n_packets %d\n", packet_number, n_packets);
+-*/
+-
+-    len= upkt[8];
+-    if ( len > 0)  {
+-	p= (unsigned char*) rawpkt+9;
+-/* printf( "%.*s\n", len, p);
+-*/
+-	p+= len;
+-	len= upkt[8+len+1];
+-/* printf( "%.*s\n", len, p+1);
+-*/
+-	p+= len+1;
+-	p+= 2;
+-    }
+-    else
+-	p= (unsigned char*) rawpkt+10;
+-
+-    if ( server->master_pkt == NULL)  {
+-	server->master_pkt= (char*)malloc( n_packets*64*6);
+-	mpkt= server->master_pkt;
+-    }
+-    else
+-	mpkt= server->master_pkt + server->n_servers*6;
++	if (NULL == (string = ut2003_strdup(next, end, &next)))
++	{
++		return -1;
++	}
+ 
+-    while ( (char*)p < rawpkt+pktlen)  {
+-	if ( *p != 0x6) printf( "*p %u\n", (unsigned)*p);
+-	memcpy( mpkt, p+1, sizeof(ipaddr));
+-	if ( 0)  {
+-	    mpkt[4]= p[5];
+-	    mpkt[5]= p[6];
+-	}
+-	else  {
+-	    mpkt[5]= p[5];
+-	    mpkt[4]= p[6];
++	if (server->game == NULL)
++	{
++		server->game = string;
++		add_rule(server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+ 	}
+-/*
+-    printf( "%08x:%hu %u.%u.%u.%u:%hu\n", ipaddr, port, ipaddr>>24,
+-	(ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port);
+-*/
+-	p+= 7;
+-	mpkt+= 6;
+-    }
+-/*
+-if (  (char*)p != rawpkt+pktlen)
+-printf( "%x %x\n", p, rawpkt+pktlen);
+-*/
+-    server->master_pkt_len= mpkt - server->master_pkt;
+-    server->n_servers= server->master_pkt_len / 6;
+-    server->server_name= MASTER;
+-    server->next_player_info= -1;
+-
+-    if ( packet_number >= n_packets)
+-	cleanup_qserver( server, 1);
+-    else
+-	cleanup_qserver( server, 0);
+-}
+-
+-char *
+-display_tribes2_string_list( unsigned char *pkt)
+-{
+-    char *delim="";
+-    unsigned int count, len;
+-    count= *pkt;
+-    pkt++;
+-    for ( ; count; count--)  {
+-	len= *pkt;
+-	pkt++;
+-	if ( len > 0)  {
+-	    if ( raw_display)  {
+-		fprintf( OF, "%s%.*s", delim, (int)len, pkt);
+-		delim= raw_delimiter;
+-	    }
+-	    else
+-		fprintf( OF, "%.*s\n", (int)len, pkt);
++	else
++	{
++		free(string);
+ 	}
+-	pkt+= len;
+-    }
+-    if ( raw_display)
+-	fputs( "\n", OF);
+-    return (char*)pkt;
+-}
+-
+-void
+-deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+-    unsigned int n_servers, index, total, server_limit;
+-    char *p, *mpkt;
+-
+-    if ( pkt[0] == TRIBES2_RESPONSE_GAME_TYPES)  {
+-	pkt+= 6;
+-	if ( raw_display)  {
+-	    fprintf( OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter,
+-		server->arg, raw_delimiter);
+-	}
+-	else  {
+-	    fprintf( OF, "Game Types\n");
+-	    fprintf( OF, "----------\n");
+-	}
+-	pkt= display_tribes2_string_list( (unsigned char *)pkt);
+-	if ( raw_display)  {
+-	    fprintf( OF, "%s%s%s%s", server->type->type_prefix, raw_delimiter,
+-		server->arg, raw_delimiter);
+-	}
+-	else  {
+-	    fprintf( OF, "\nMission Types\n");
+-	    fprintf( OF, "-------------\n");
+-	}
+-	display_tribes2_string_list( (unsigned char *)pkt);
+-
+-	server->master_pkt_len= 0;
+-	server->n_servers= 0;
+-	server->server_name= MASTER;
+-	server->next_player_info= -1;
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
+-
+-    if ( pkt[0] != TRIBES2_RESPONSE_MASTER)  {
+-	/* error */
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
+-
+-    server_limit= get_param_ui_value( server, "limit", ~0);
+-
+-    n_servers= little_endian ? *(unsigned short*)(pkt+8) :
+-	swap_short(pkt+8);
+-    index= *(unsigned char*)(pkt+6);
+-    total= *(unsigned char*)(pkt+7);
+-    if ( server->master_pkt == NULL)  {
+-	server->master_pkt= (char*)malloc( total * n_servers * 6);
+-	mpkt= server->master_pkt;
+-    }
+-    else
+-	mpkt= server->master_pkt + server->n_servers*6;
+-
+-    p= pkt+10;
+-    for ( ; n_servers && ((char*)mpkt - server->master_pkt)/6 < server_limit;
+-		n_servers--, p+= 6, mpkt+= 6)  {
+-	memcpy( mpkt, p, 4);
+-	mpkt[4]= p[5];
+-	mpkt[5]= p[4];
+-    }
+-    server->master_pkt_len= (char*)mpkt - server->master_pkt;
+-    server->n_servers= server->master_pkt_len / 6;
+-    server->server_name= MASTER;
+-    server->next_player_info= -1;
+ 
+-    if ( index >= total-1 || server->n_servers >= server_limit)
+-	cleanup_qserver( server, 1);
+-    else
+-	cleanup_qserver( server, 0);
++	server->num_players = swap_long_from_little(next);
++	next += 4;
++	server->max_players = swap_long_from_little(next);
++	return 0;
+ }
+ 
+-int
+-server_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
++STATIC int pariah_rule_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+-    int off= 0;
++	char *key, *value;
+ 
+-    /* ignore duplicate packets */
+-    if ( server->server_name != NULL)
+-	return 0;
++	unsigned char no_rules = (unsigned char)rawpkt[1];
++	unsigned char seen = 0;
+ 
+-    server->address= strdup((char*)&pkt->data[off]);
+-    off+= strlen(server->address) + 1;
+-    if ( off >= datalen)
+-	return -1;
++	// type + no_rules
++	rawpkt += 2;
+ 
+-    server->server_name= strdup((char*)&pkt->data[off]);
+-    off+= strlen(server->server_name) + 1;
+-    if ( off >= datalen)
+-	return -1;
++	// we get size encoded key = value pairs
++	while (rawpkt < end && no_rules > seen)
++	{
++		// first byte is the rule count
++		seen = (unsigned char)rawpkt[0];
++		rawpkt++;
++		if (NULL == (key = ut2003_strdup(rawpkt, end, &rawpkt)))
++		{
++			break;
++		}
+ 
+-    server->map_name= strdup((char*)&pkt->data[off]);
+-    off+= strlen(server->map_name) + 1;
+-    if ( off > datalen)
+-	return -1;
++		if ('\0' == rawpkt[0])
++		{
++			value = strdup("");
++			rawpkt++;
++		}
++		else if (NULL == (value = ut2003_strdup(rawpkt, end, &rawpkt)))
++		{
++			break;
++		}
+ 
+-    server->num_players= pkt->data[off++];
+-    server->max_players= pkt->data[off++];
+-    server->protocol_version= pkt->data[off++];
++		if (NULL == add_rule(server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES))
++		{
++			/* duplicate, so free key and value */
++			free(value);
++			free(key);
++		}
+ 
+-    server->retry1= n_retries;
++		seen++;
++	}
+ 
+-    if ( get_server_rules)
+-	send_rule_request_packet( server);
+-    if ( get_player_info)
+-	send_player_request_packet( server);
++	if (no_rules == seen)
++	{
++		// all done
++		server->next_rule = NULL;
++		return 1;
++	}
+ 
+-    return 0;
++	return 0;
+ }
+ 
+-int
+-player_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
++STATIC int ut2003_rule_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+-    char *name, *address;
+-    int off, colors, frags, connect_time, player_number;
+-    struct player *player, *last;
+-
+-    off= 0;
+-    player_number= pkt->data[off++];
+-    name= (char*) &pkt->data[off];
+-    off+= strlen(name)+1;
+-    if ( off >= datalen)
+-	return -1;
+-
+-    colors= pkt->data[off+3];
+-    colors= (colors<<8) + pkt->data[off+2];
+-    colors= (colors<<8) + pkt->data[off+1];
+-    colors= (colors<<8) + pkt->data[off];
+-    off+= sizeof(colors);
+-
+-    frags= pkt->data[off+3];
+-    frags= (frags<<8) + pkt->data[off+2];
+-    frags= (frags<<8) + pkt->data[off+1];
+-    frags= (frags<<8) + pkt->data[off];
+-    off+= sizeof(frags);
+-
+-    connect_time= pkt->data[off+3];
+-    connect_time= (connect_time<<8) + pkt->data[off+2];
+-    connect_time= (connect_time<<8) + pkt->data[off+1];
+-    connect_time= (connect_time<<8) + pkt->data[off];
+-    off+= sizeof(connect_time);
+-
+-    address= (char*) &pkt->data[off];
+-    off+= strlen(address)+1;
+-    if ( off > datalen)
+-	return -1;
+-
+-    last= server->players;
+-    while ( last != NULL && last->next != NULL)  {
+-	if ( last->number == player_number)
+-	     return 0;
+-	last= last->next;
+-    }
+-    if ( last != NULL && last->number == player_number)
+-	return 0;
+-
+-    player= (struct player *) calloc( 1, sizeof(struct player));
+-    player->number= player_number;
+-    player->name= strdup( name);
+-    player->address= strdup( address);
+-    player->connect_time= connect_time;
+-    player->frags= frags;
+-    player->shirt_color= colors>>4;
+-    player->pants_color= colors&0xf;
+-    player->next= NULL;
+-
+-    if ( last == NULL)
+-	server->players= player;
+-    else
+-	last->next= player;
+-
+-    server->next_player_info++;
+-    server->retry2= n_retries;
+-    if ( server->next_player_info < server->num_players)
+-	send_player_request_packet( server);
+-
+-    return 0;
+-}
+-
+-int
+-rule_info_packet( struct qserver *server, struct q_packet *pkt, int datalen)
+-{
+-    int off= 0;
+-    struct rule *rule, *last;
+-    char *name, *value;
+-
+-    /* Straggler packet after we've already given up fetching rules */
+-    if ( server->next_rule == NULL)
+-	return 0;
+-
+-    if ( ntohs(pkt->length) == Q_HEADER_LEN)  {
+-	server->next_rule= NULL;
+-	return 0;
+-    }
+-
+-    name= (char*)&pkt->data[off];
+-    off+= strlen( name)+1;
+-    if ( off >= datalen)
+-	return -1;
++	char *key, *value;
++	int result = 0;
+ 
+-    value= (char*)&pkt->data[off];
+-    off+= strlen( value)+1;
+-    if ( off > datalen)
+-	return -1;
++	// Packet Type
++	rawpkt++;
+ 
+-    last= server->rules;
+-    while ( last != NULL && last->next != NULL)  {
+-	if ( strcmp( last->name, name) == 0)
+-	     return 0;
+-	last= last->next;
+-    }
+-    if ( last != NULL && strcmp( last->name, name) == 0)
+-	return 0;
++	// we get size encoded key = value pairs
++	while (rawpkt < end)
++	{
++		if (NULL == (key = ut2003_strdup(rawpkt, end, &rawpkt)))
++		{
++			break;
++		}
+ 
+-    rule= (struct rule *) malloc( sizeof( struct rule));
+-    rule->name= strdup( name);
+-    rule->value= strdup( value);
+-    rule->next= NULL;
++		if (NULL == (value = ut2003_strdup(rawpkt, end, &rawpkt)))
++		{
++			break;
++		}
+ 
+-    if ( last == NULL)
+-	server->rules= rule;
+-    else
+-	last->next= rule;
++		if (strcmp(key, "minplayers") == 0)
++		{
++			result = atoi(value);
++		}
+ 
+-    server->n_rules++;
+-    server->next_rule= rule->name;
+-    server->retry1= n_retries;
+-    send_rule_request_packet( server);
++		if (NULL == add_rule(server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES))
++		{
++			/* duplicate, so free key and value */
++			free(value);
++			free(key);
++		}
++	}
+ 
+-    return 0;
++	return result;
+ }
+ 
+-struct info *
+-player_add_info( struct player *player, char *key, char *value, int flags)
++char *ut2003_strdup(const char *string, const char *end, char **next)
+ {
+-    struct info *info;
+-    if ( flags & CHECK_DUPLICATE_RULES )
++	unsigned char len = string[0];
++	char *result = NULL;
++
++	if (len < 128)
++	{
++		// type 1 string
++		//fprintf( stderr, "Type 1:" );
++		result = dup_nstring(string, end, next);
++	}
++	else
+ 	{
+-		for ( info = player->info; info; info = info->next )
++		// type 2 string
++		//fprintf( stderr, "Type 2:\n" );
++		const char *last;
++		char *resp, *pos;
++		// minus indicator
++		len -= 128;
++		// double byte chars so * 2
++		len = len * 2;
++		last = string + len;
++		if (last > end)
+ 		{
+-			if ( 0 == strcmp( info->name, key ) )
+-			{
+-				return NULL;
+-			}
++			*next = (char*)end;
++			fprintf(stderr, "Type 2 string format error ( too short )\n");
++			return NULL;
+ 		}
+-    }
+ 
+-	if ( flags & COMBINE_VALUES )
+-	{
+-		for ( info = player->info; info; info = info->next )
++		*next = (char*)last + 1;
++		if (NULL == (result = (char*)calloc(last - string, sizeof(char))))
+ 		{
+-			if ( 0 == strcmp( info->name, key ) )
++			fprintf(stderr, "Failed to malloc string memory\n");
++			return NULL;
++		}
++		resp = result;
++		pos = (char*)string + 1;
++		while (pos <= last)
++		{
++			// check for a color code
++			if (pos + 6 <= last && 0 == memcmp(pos, "^\0#\0", 4))
+ 			{
+-				char *full_value = (char*)calloc( sizeof(char), strlen( info->value ) + strlen( value ) + 2 );
+-				if ( NULL == full_value )
+-				{
+-					fprintf( stderr, "Failed to malloc combined value\n" );
+-					exit( 1 );
+-				}
+-				sprintf( full_value, "%s%s%s", info->value, multi_delimiter, value );
+-
+-				// We should be able to free this
+-				free( info->value );
+-				info->value = full_value;
+-
+-				return info;
++				// we have a color code
++				//fprintf( stderr, "color:%02hhx%02hhx\n", pos[4], pos[5] );
++				// indicator transformed to ^\1
++				*resp = *pos;
++				resp++;
++				pos++;
++				*resp = '\1';
++				resp++;
++				pos += 3;
++				// color byte
++				*resp = *pos;
++				resp++;
++				pos += 2;
++				//pos += 6;
+ 			}
+-		}
+-	}
+ 
+-    info = (struct info *) malloc( sizeof( struct info));
+-    if ( flags & NO_KEY_COPY)
+-	{
+-		info->name = key;
+-	}
+-    else
+-	{
+-		info->name = strdup(key);
+-	}
+-    if ( flags & NO_VALUE_COPY)
+-	{
+-		info->value = value;
+-	}
+-    else
+-	{
+-		info->value = strdup(value);
++			// standard char
++			//fprintf( stderr, "char: %02hhx\n", *pos );
++			*resp = *pos;
++			resp++;
++			pos += 2;
++		}
+ 	}
+-    info->next = NULL;
+ 
+-	if ( NULL == player->info )
+-	{
+-		player->info = info;
+-	}
+-	else
+-	{
+-		*player->last_info = info;
+-	}
+-    player->last_info = &info->next;
+-    player->n_info++;
++	//fprintf( stderr, "'%s'\n", result );
+ 
+-    return info;
++	return result;
+ }
+ 
+-struct rule *
+-add_rule( struct qserver *server, char *key, char *value, int flags)
++
++STATIC int pariah_player_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+-    struct rule *rule;
+-    if ( flags & CHECK_DUPLICATE_RULES )
+-	{
+-		for ( rule = server->rules; rule; rule = rule->next )
+-		{
+-			if ( 0 == strcmp( rule->name, key ) )
+-			{
+-				return NULL;
+-			}
+-		}
+-    }
++	unsigned char no_players = rawpkt[1];
++	unsigned char seen = 0; /* XXX: cannot work this way, it takes only
++	this packet into consideration. What if
++	player info is spread across multiple
++	packets? */
+ 
+-	if ( flags & COMBINE_VALUES )
++	// type + no_players + some unknown preamble
++	rawpkt += 3;
++	while (rawpkt < end && seen < no_players)
+ 	{
+-		for ( rule = server->rules; rule; rule = rule->next )
+-		{
+-			if ( 0 == strcmp( rule->name, key ) )
+-			{
+-				char *full_value = (char*)calloc( sizeof(char), strlen( rule->value ) + strlen( value ) + 2 );
+-				if ( NULL == full_value )
+-				{
+-					fprintf( stderr, "Failed to malloc combined value\n" );
+-					exit( 1 );
+-				}
+-				sprintf( full_value, "%s%s%s", rule->value, multi_delimiter, value );
++		struct player *player;
+ 
+-				// We should be able to free this
+-				free( rule->value );
+-				rule->value = full_value;
++		// Player Number
++		rawpkt += 4;
+ 
+-				return rule;
+-			}
++		// Create a player
++		if (NULL == (player = add_player(server, server->n_player_info)))
++		{
++			return 0;
+ 		}
+-	}
+ 
+-    rule = (struct rule *) malloc( sizeof( struct rule));
+-    if ( flags & NO_KEY_COPY)
+-	{
+-		rule->name = key;
+-	}
+-    else
+-	{
+-		rule->name = strdup(key);
+-	}
+-    if ( flags & NO_VALUE_COPY)
+-	{
+-		rule->value = value;
+-	}
+-    else
+-	{
+-		rule->value = strdup(value);
+-	}
+-    rule->next = NULL;
+-    *server->last_rule = rule;
+-    server->last_rule = & rule->next;
+-    server->n_rules++;
++		// Name ( min 3 bytes )
++		player->name = ut2003_strdup(rawpkt, end, &rawpkt);
+ 
+-    return rule;
+-}
++		// Ping
++		player->ping = swap_long_from_little(rawpkt);
++		rawpkt += 4;
+ 
+-void
+-add_nrule( struct qserver *server, char *key, char *value, int len)
+-{
+-    struct rule *rule;
+-    for ( rule= server->rules; rule; rule= rule->next)
+-	if ( strcmp( rule->name, key) == 0)
+-	    return;
++		// Frags
++		player->frags = (unsigned char)rawpkt[0];
++		rawpkt++;
+ 
+-    rule= (struct rule *) malloc( sizeof( struct rule));
+-    rule->name= strdup(key);
+-    rule->value= strndup(value,len);
+-    rule->next= NULL;
+-    *server->last_rule= rule;
+-    server->last_rule= & rule->next;
+-    server->n_rules++;
+-}
++		// unknown
++		rawpkt++;
+ 
+-struct player *
+-add_player( struct qserver *server, int player_number )
+-{
+-    struct player *player;
++		seen++;
++	}
+ 
+-    for ( player = server->players; player; player = player->next )
+-    {
+-	if ( player->number == player_number)
++	if (no_players == seen)
+ 	{
+-	    return NULL;
++		// all done
++		server->num_players = server->n_player_info;
++		return 1;
+ 	}
+-    }
+ 
+-    player = (struct player *) calloc( 1, sizeof( struct player));
+-    player->number = player_number;
+-    player->next = server->players;
+-    player->n_info = 0;
+-    player->score = NA_INT;
+-    player->deaths = NA_INT;
+-    player->frags = NA_INT;
+-    player->last_info = NULL;
+-    server->players = player;
+-    server->n_player_info++;
+-    return player;
+-}
+-
+-STATIC struct player *
+-get_player_by_number( struct qserver *server, int player_number)
+-{
+-    struct player *player;
+-    for ( player= server->players; player; player= player->next)
+-        if ( player->number == player_number)
+-            return player;
+-    return NULL;
++	// possibly more to come
++
++	return 0;
+ }
+ 
+-// Updates a servers port information.
+-// Sets the rules:
+-// _queryport <queryport>
+-// hostport <port>
+-void
+-change_server_port( struct qserver *server, unsigned short port, int force )
++STATIC int ut2003_player_packet(struct qserver *server, char *rawpkt, char *end)
+ {
+-	if ( port > 0 && port != server->port )
++	// skip type
++	rawpkt++;
++	switch (server->protocol_version)
+ 	{
+-		// valid port and changing
+-		char arg[64];
+-
+-		if ( show_game_port || force || server->flags & TF_SHOW_GAME_PORT )
+-		{
+-			unsigned int ipaddr = ntohl(server->ipaddr);
+-
+-			// Update the servers hostname as required
+-			sprintf( arg, "%d.%d.%d.%d:%hu", ipaddr>>24, (ipaddr>>16)&0xff, (ipaddr>>8)&0xff, ipaddr&0xff, port );
+-			if ( 0 != strcmp( server->arg, server->host_name ))
+-			{
+-				// hostname isnt the query arg
+-				char *colon = strchr( server->host_name, ':' );
+-				if ( colon )
+-				{
+-					// dns hostname or hostname:port
+-					char *hostname = malloc( strlen(server->host_name) + 6 );
+-					*colon= '\0';
+-					sprintf( hostname, "%s:%hu", server->host_name, port);
+-					free( server->host_name);
+-					server->host_name= hostname;
++		case 0x7e:
++			// XMP packet
++			//fprintf( stderr, "XMP packet\n" );
++			while (rawpkt < end)
++			{
++				struct player *player;
++				char *var, *val;
++				unsigned char no_props;
++				if (rawpkt + 24 > end)
++				{
++					malformed_packet(server, "player info too short");
++					rawpkt = end;
++					return 1;
+ 				}
+-			}
+-			// Update the server arg
+-			free( server->arg );
+-			server->arg = strdup( arg );
+ 
+-			// Add a rule noting the previous query port
+-			sprintf( arg, "%hu", server->port );
+-			add_rule( server, "_queryport", arg, NO_FLAGS);
++				// Player Number never set
++				rawpkt += 4;
+ 
+-			// Update the servers port
+-			server->port = port;
+-		}
++				// Player ID never set
++				rawpkt += 4;
+ 
+-		// Add a rule noting the servers hostport
+-		sprintf( arg, "%hu", port );
+-		add_rule( server, "hostport", arg, NO_FLAGS);
+-	}
+-}
++				if (NULL == (player = add_player(server, server->n_player_info)))
++				{
++					return 0;
++				}
+ 
+-STATIC void
+-players_set_teamname( struct qserver *server, int teamid, char *teamname )
+-{
+-	struct player *player;
+-	for ( player= server->players; player; player= player->next)
+-	{
+-		if ( player->team == teamid )
+-		{
+-			player->team_name = strdup( teamname );
+-		}
+-	}
+-}
++				// Name ( min 3 bytes )
++				player->name = ut2003_strdup(rawpkt, end, &rawpkt);
+ 
+-STATIC char *
+-dup_nstring( const char *pkt, const char *end, char **next)
+-{
+-	char *pt = (char*)pkt;
+-    int len= ((unsigned char*)pkt)[0];
+-    pt++;
+-    if ( *pt == '\1')
+-    {
+-		len++;
+-	}
+-    if ( pt + len > end)
+-    {
+-		return NULL;
+-	}
++				// Ping
++				player->ping = swap_long_from_little(rawpkt);
++				rawpkt += 4;
+ 
+-    *next = pt+len;
+-    return strndup( pt, len );
+-}
++				// Frags
++				player->frags = swap_long_from_little(rawpkt);
++				rawpkt += 4;
+ 
+-STATIC char *
+-dup_n1string( char *pkt, char *end, char **next)
+-{
+-    unsigned len;
++				// Stat ID never set
++				rawpkt += 4;
+ 
+-    if(!pkt || pkt >= end)
+-	return NULL;
++				// Player properties
++				no_props = rawpkt[0];
++				//fprintf( stderr, "noprops %d\n", no_props );
++				rawpkt++;
++				while (rawpkt < end && no_props > 0)
++				{
++					if (NULL == (var = ut2003_strdup(rawpkt, end, &rawpkt)))
++					{
++						break;
++					}
++					if (NULL == (val = ut2003_strdup(rawpkt, end, &rawpkt)))
++					{
++						break;
++					}
++					//fprintf( stderr, "attrib: %s = %s\n", var, val );
+ 
+-    len = (unsigned char)pkt[0]-1;
+-    pkt++;
+-    if ( pkt + len > end)
+-	return NULL;
++					// Things we can use
++					if (0 == strcmp(var, "team"))
++					{
++						player->team_name = val;
++					}
++					else if (0 == strcmp(var, "class"))
++					{
++						player->skin = val;
++					}
++					else
++					{
++						free(val);
++					}
+ 
+-    *next= pkt+len;
+-    return strndup( pkt, len);
+-}
++					free(var);
++					no_props--;
++				}
++			}
++			break;
++		default:
++			while (rawpkt < end)
++			{
++				struct player *player;
+ 
+-STATIC int
+-pariah_basic_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+-    char *next;
+-    char *string;
+-	change_server_port( server, swap_short_from_little( &rawpkt[14]), 0 );
+-    if ( NULL == ( string = ut2003_strdup( &rawpkt[18], end, &next) ) )
+-    {
+-		return -1;
+-	}
++				if (rawpkt + 4 > end)
++				{
++					malformed_packet(server, "player packet too short");
++					return 1;
++				}
+ 
+-    if ( server->server_name == NULL)
+-    {
+-		server->server_name = string;
+-	}
+-    else
+-    {
+-		free(string);
+-	}
++				if (NULL == (player = add_player(server, swap_long_from_little(rawpkt))))
++				{
++					return 0;
++				}
+ 
+-    if ( NULL == ( string = ut2003_strdup( next, end, &next) ) )
+-    {
+-    	return -1;
++				player->name = ut2003_strdup(rawpkt + 4, end, &rawpkt);
++				if (rawpkt + 8 > end)
++				{
++					malformed_packet(server, "player packet too short");
++					return 1;
++				}
++				player->ping = swap_long_from_little(rawpkt);
++				rawpkt += 4;
++				player->frags = swap_long_from_little(rawpkt);
++				rawpkt += 4;
++				{
++					unsigned team = swap_long_from_little(rawpkt);
++					rawpkt += 4;
++					player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++					if (team &1 << 29)
++					{
++						player->team_name = "red";
++					}
++					else if (team &1 << 30)
++					{
++						player->team_name = "blue";
++					}
++				}
++			}
+ 	}
+ 
+-    if ( server->map_name == NULL)
+-    {
+-		server->map_name = string;
+-	}
+-    else
+-    {
+-		free(string);
+-	}
++	return 0;
++}
+ 
+-    if ( NULL == ( string= ut2003_strdup( next, end, &next) ) )
+-    {
+-		return -1;
+-	}
+ 
+-    if ( server->game == NULL)
+-    {
+-		server->game = string;
+-		add_rule( server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+-    }
+-    else
+-    {
+-		free(string);
++char *get_rule(struct qserver *server, char *name)
++{
++	struct rule *rule;
++	rule = server->rules;
++	for (; rule != NULL; rule = rule->next)
++	{
++		if (strcmp(name, rule->name) == 0)
++		{
++			return rule->value;
++		}
+ 	}
+ 
+-	server->num_players = (unsigned char)next[0];
+-	server->max_players = (unsigned char)next[1];
+-
+-    return 0;
++	return NULL;
+ }
+ 
+-STATIC int
+-ut2003_basic_packet( struct qserver *server, char *rawpkt, char *end)
++query_status_t deal_with_ut2003_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *next;
+-    char *string;
+-	change_server_port( server, swap_short_from_little( &rawpkt[6]), 0 );
+ 
+-    if ( NULL == ( string = ut2003_strdup( &rawpkt[14], end, &next) ) )
+-    {
+-		return -1;
+-	}
++	// For protocol spec see:
++	// http://unreal.student.utwente.nl/UT2003-queryspec.html
+ 
+-    if ( server->server_name == NULL)
+-    {
+-		server->server_name = string;
+-	}
+-    else
+-    {
+-		free(string);
+-	}
++	char *end;
++	int error = 0, before;
++	unsigned int packet_header;
+ 
+-    if ( NULL == ( string = ut2003_strdup( next, end, &next) ) )
+-    {
+-    	return -1;
+-	}
++	debug( 2, "deal_with_ut2003_packet %p, %d", server, pktlen );
+ 
+-    if ( server->map_name == NULL)
+-    {
+-		server->map_name= string;
+-	}
+-    else
+-    {
+-		free(string);
+-	}
++	rawpkt[pktlen] = '\0';
++	end = &rawpkt[pktlen];
+ 
+-    if ( NULL == ( string= ut2003_strdup( next, end, &next) ) )
+-    {
+-		return -1;
+-	}
++	packet_header = swap_long_from_little(&rawpkt[0]);
++	rawpkt += 4;
+ 
+-    if ( server->game == NULL)
+-    {
+-		server->game= string;
+-		add_rule( server, "gametype", server->game, NO_FLAGS | CHECK_DUPLICATE_RULES);
+-    }
+-    else
+-    {
+-		free(string);
++	server->protocol_version = packet_header;
++	if (
++		packet_header != 0x77 // Pariah Demo?
++	 && packet_header != 0x78 // UT2003 Demo
++	 && packet_header != 0x79 // UT2003 Retail
++	 && packet_header != 0x7e // Unreal2 XMP
++	 && packet_header != 0x7f // UT2004 Demo
++	 && packet_header != 0x80 // UT2004 Retail
++	)
++	{
++		malformed_packet(server, "Unknown type 0x%x", packet_header);
+ 	}
+ 
+-    server->num_players= swap_long_from_little( next);
+-    next+= 4;
+-    server->max_players= swap_long_from_little( next);
+-    return 0;
+-}
++	switch (rawpkt[0])
++	{
++		case 0x00:
++			// Server info
++			if (server->server_name == NULL)
++			{
++				server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++			}
+ 
+-STATIC int
+-pariah_rule_packet( struct qserver *server, char *rawpkt, char *end )
+-{
+-    char *key, *value;
++			error = ut2003_basic_packet(server, rawpkt, end);
++			if (!error)
++			{
++				if (get_server_rules || get_player_info)
++				{
++					int requests = server->n_requests;
++					server->next_rule = "";
++					server->retry1 = n_retries;
++					server->retry2 = 0; // don't wait for player packet
++					send_rule_request_packet(server);
++					server->n_requests = requests; // would produce wrong ping
++				}
++			}
++			break;
+ 
+-    unsigned char no_rules = (unsigned char)rawpkt[1];
+-	unsigned char seen = 0;
++		case 0x01:
++			// Game info
++			ut2003_rule_packet(server, rawpkt, end);
++			server->next_rule = "";
++			server->retry1 = 0; /* we received at least one rule packet so
++			no need to retry. We'd get double
++			entries otherwise. */
++			break;
+ 
+-	// type + no_rules
+-	rawpkt+=2;
++		case 0x02:
++			// Player info
++			before = server->n_player_info;
++			error = ut2003_player_packet(server, rawpkt, end);
++			if (before == server->n_player_info)
++			{
++				error = 1;
++			}
++			break;
+ 
+-	// we get size encoded key = value pairs
+-    while ( rawpkt < end && no_rules > seen )
+-    {
+-		// first byte is the rule count
+-		seen = (unsigned char)rawpkt[0];
+-		rawpkt++;
+-		if ( NULL == ( key = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+-		{
++		case 0x10:
++			// Pariah Server info
++			if (server->server_name == NULL)
++			{
++				server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++			}
++
++			error = pariah_basic_packet(server, rawpkt, end);
++			if (!error)
++			{
++				// N.B. pariah always sends a rules and players packet
++				int requests = server->n_requests;
++				server->next_rule = "";
++				server->retry1 = n_retries;
++				server->retry2 = 0;
++				server->n_requests = requests; // would produce wrong ping
++			}
+ 			break;
+-		}
+ 
+-		if ( '\0' == rawpkt[0] )
+-		{
+-			value = strdup( "" );
+-			rawpkt++;
+-		}
+-		else if ( NULL == ( value = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+-		{
++		case 0x11:
++			// Game info
++			pariah_rule_packet(server, rawpkt, end);
++			server->retry1 = 0; /* we received at least one rule packet so
++			no need to retry. We'd get double
++			entries otherwise. */
+ 			break;
+-		}
+ 
+-		if ( NULL == add_rule( server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES ) )
+-		{
+-			/* duplicate, so free key and value */
+-			free(value);
+-			free(key);
+-		}
++		case 0x12:
++			// Player info
++			before = server->n_player_info;
++			pariah_player_packet(server, rawpkt, end);
++			if (before == server->n_player_info)
++			{
++				error = 1;
++			}
++			break;
+ 
+-		seen++;
+-    }
+ 
+-	if ( no_rules == seen )
++		default:
++			malformed_packet(server, "Unknown packet type 0x%x", (unsigned)rawpkt[0]);
++			break;
++	}
++
++	/* don't cleanup if we fetch server rules. We would lose
++	 * rule packets as we don't know how many we get
++	 * We do clean up if we don't fetch server rules so we don't
++	 * need to wait for timeout.
++	 */
++	if (
++		error ||
++		(!get_server_rules && !get_player_info) ||
++		(!get_server_rules && server->num_players == server->n_player_info) ||
++		(server->next_rule == NULL && server->num_players == server->n_player_info)
++	)
+ 	{
+-		// all done
+-		server->next_rule = NULL;
+-		return 1;
++		return DONE_FORCE;
+ 	}
+ 
+-    return 0;
++	return INPROGRESS;
+ }
+ 
+-STATIC int
+-ut2003_rule_packet( struct qserver *server, char *rawpkt, char *end )
++int deal_with_unrealmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *key, *value;
+-    int result= 0;
+-
+-    // Packet Type
+-    rawpkt++;
++	debug( 2, "deal_with_unrealmaster_packet %p, %d", server, pktlen );
+ 
+-	// we get size encoded key = value pairs
+-    while ( rawpkt < end )
+-    {
+-		if ( NULL == ( key = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+-		{
+-			break;
+-		}
+-
+-		if ( NULL == ( value = ut2003_strdup( rawpkt, end, &rawpkt) ) )
+-		{
+-			break;
+-		}
++	if (pktlen == 0)
++	{
++		return PKT_ERROR;
++	}
++	print_packet(server, rawpkt, pktlen);
++	puts("--");
++	return 0;
++}
+ 
+-		if ( strcmp( key, "minplayers") == 0)
+-		{
+-			result = atoi(value);
+-		}
++/* Returns 1 if the query is done (server may be freed) and 0 if not.
++ */
++query_status_t deal_with_halflife_packet(struct qserver *server, char *rawpkt, int pktlen)
++{
++	char *pkt;
++	char *end = &rawpkt[pktlen];
++	int pkt_index = 0, pkt_max = 0;
++	char number[16];
++	short pkt_id;
+ 
+-		if ( NULL == add_rule( server, key, value, NO_KEY_COPY | NO_VALUE_COPY | COMBINE_VALUES ) )
+-		{
+-			/* duplicate, so free key and value */
+-			free(value);
+-			free(key);
+-		}
+-    }
++	debug( 2, "deal_with_halflife_packet %p, %d", server, pktlen );
+ 
+-    return result;
+-}
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	}
+ 
+-char *
+-ut2003_strdup( const char *string, const char *end, char **next )
+-{
+-	unsigned char len = string[0];
+-	char *result = NULL;
++	if (pktlen < 5)
++	{
++		return PKT_ERROR;
++	}
+ 
+-	if ( len < 128 )
++	if (((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' || rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors)
+ 	{
+-		// type 1 string
+-		//fprintf( stderr, "Type 1:" );
+-		result = dup_nstring( string, end, next );
++		unsigned int ipaddr = ntohl(server->ipaddr);
++		fprintf(stderr, "Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
++			(ipaddr >> 24) &0xff,
++			(ipaddr >> 16) &0xff,
++			(ipaddr >> 8) &0xff,
++			ipaddr &0xff,
++			ntohs(server->port)
++		);
++		print_packet(server, rawpkt, pktlen);
+ 	}
+-	else
++
++	if (((unsigned char*)rawpkt)[0] == 0xfe)
+ 	{
+-		// type 2 string
+-		//fprintf( stderr, "Type 2:\n" );
+-		const char *last;
+-		char *resp, *pos;
+-		// minus indicator
+-		len -= 128;
+-		// double byte chars so * 2
+-		len = len * 2;
+-		last = string + len;
+-		if ( last > end )
+-		{
+-			*next = (char*)end;
+-			fprintf( stderr, "Type 2 string format error ( too short )\n" );
+-			return NULL;
+-		}
++		SavedData *sdata;
++		pkt_index = ((unsigned char*)rawpkt)[8] >> 4;
++		pkt_max = ((unsigned char*)rawpkt)[8] &0xf;
++		memcpy(&pkt_id, &rawpkt[4], 2);
+ 
+-		*next = (char*)last+1;
+-		if ( NULL == ( result = (char*)calloc( last - string, sizeof(char) ) ) )
++		if (server->saved_data.data == NULL)
+ 		{
+-			fprintf( stderr, "Failed to malloc string memory\n" );
+-			return NULL;
++			sdata = &server->saved_data;
+ 		}
+-		resp = result;
+-		pos = (char*)string + 1;
+-		while ( pos <= last )
++		else
+ 		{
+-			// check for a color code
+-			if ( pos + 6 <= last && 0 == memcmp( pos, "^\0#\0", 4 ) )
+-			{
+-				// we have a color code
+-				//fprintf( stderr, "color:%02hhx%02hhx\n", pos[4], pos[5] );
+-				// indicator transformed to ^\1
+-				*resp = *pos;
+-				resp++;
+-				pos++;
+-				*resp = '\1';
+-				resp++;
+-				pos+=3;
+-				// color byte
+-				*resp = *pos;
+-				resp++;
+-				pos+=2;
+-				//pos += 6;
+-			}
+-
+-			// standard char
+-			//fprintf( stderr, "char: %02hhx\n", *pos );
+-			*resp = *pos;
+-			resp++;
+-			pos += 2;
++			sdata = (SavedData*)calloc(1, sizeof(SavedData));
++			sdata->next = server->saved_data.next;
++			server->saved_data.next = sdata;
+ 		}
+-	}
+-
+-	//fprintf( stderr, "'%s'\n", result );
+ 
+-	return result;
+-}
++		sdata->pkt_index = pkt_index;
++		sdata->pkt_max = pkt_max;
++		sdata->pkt_id = pkt_id;
++		sdata->datalen = pktlen - 9;
++		sdata->data = (char*)malloc(pktlen - 9);
++		memcpy(sdata->data, &rawpkt[9], pktlen - 9);
+ 
++		/* combine_packets will call us recursively */
++		return combine_packets(server);
+ 
+-STATIC int
+-pariah_player_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+-	unsigned char no_players = rawpkt[1];
+-	unsigned char seen = 0; /* XXX: cannot work this way, it takes only
+-				   this packet into consideration. What if
+-				   player info is spread across multiple
+-				   packets? */
++		/*
++		fprintf( OF, "pkt_index %d pkt_max %d\n", pkt_index, pkt_max);
++		rawpkt+= 9;
++		pktlen-= 9;
++		 */
++	}
+ 
+-	// type + no_players + some unknown preamble
+-	rawpkt += 3;
+-	while ( rawpkt < end && seen < no_players )
++	/* 'info' response */
++	if (rawpkt[4] == 'C' || rawpkt[4] == 'm')
+ 	{
+-		struct player *player;
+-
+-		// Player Number
+-		rawpkt += 4;
+-
+-		// Create a player
+-		if ( NULL == ( player = add_player( server, server->n_player_info ) ) )
++		if (server->server_name != NULL)
+ 		{
+ 			return 0;
+ 		}
++		pkt = &rawpkt[5];
++		server->address = strdup(pkt);
++		pkt += strlen(pkt) + 1;
++		server->server_name = strdup(pkt);
++		pkt += strlen(pkt) + 1;
++		server->map_name = strdup(pkt);
++		pkt += strlen(pkt) + 1;
+ 
+-		// Name ( min 3 bytes )
+-		player->name = ut2003_strdup( rawpkt, end, &rawpkt );
+-
+-		// Ping
+-		player->ping = swap_long_from_little( rawpkt );
+-		rawpkt += 4;
+-
+-		// Frags
+-		player->frags = (unsigned char)rawpkt[0];
+-		rawpkt++;
+-
+-		// unknown
+-		rawpkt++;
+-
+-		seen++;
+-	}
+-
+-	if ( no_players == seen )
+-	{
+-		// all done
+-		server->num_players = server->n_player_info;
+-		return 1;
+-	}
+-
+-	// possibly more to come
++		if (*pkt)
++		{
++			add_rule(server, "gamedir", pkt, NO_FLAGS);
++		}
++		if (*pkt && strcmp(pkt, "valve") != 0)
++		{
++			server->game = add_rule(server, "game", pkt, NO_FLAGS)->value;
++			server->flags |= FLAG_DO_NOT_FREE_GAME;
++		}
++		pkt += strlen(pkt) + 1;
++		if (*pkt)
++		{
++			add_rule(server, "gamename", pkt, NO_FLAGS);
++		}
++		pkt += strlen(pkt) + 1;
+ 
+-	return 0;
+-}
++		server->num_players = (unsigned int)pkt[0];
++		server->max_players = (unsigned int)pkt[1];
++		pkt += 2;
++		if (pkt < end)
++		{
++			int protocol = *((unsigned char*)pkt);
++			sprintf(number, "%d", protocol);
++			add_rule(server, "protocol", number, NO_FLAGS);
++			pkt++;
++		}
+ 
+-STATIC int
+-ut2003_player_packet( struct qserver *server, char *rawpkt, char *end)
+-{
+-	// skip type
+-	rawpkt++;
+-	switch ( server->protocol_version )
+-	{
+-	case 0x7e:
+-		// XMP packet
+-		//fprintf( stderr, "XMP packet\n" );
+-		while ( rawpkt < end )
++		if (rawpkt[4] == 'm')
+ 		{
+-			struct player *player;
+-			char *var, *val;
+-			unsigned char no_props;
+-			if ( rawpkt + 24 > end )
++			if (*pkt == 'd')
+ 			{
+-				malformed_packet( server, "player info too short" );
+-				rawpkt = end;
+-				return 1;
++				add_rule(server, "sv_type", "dedicated", NO_FLAGS);
+ 			}
+-
+-			// Player Number never set
+-			rawpkt += 4;
+-
+-			// Player ID never set
+-			rawpkt += 4;
+-
+-			if ( NULL == ( player = add_player( server, server->n_player_info ) ) )
++			else if (*pkt == 'l')
+ 			{
+-				return 0;
++				add_rule(server, "sv_type", "listen", NO_FLAGS);
+ 			}
+-
+-			// Name ( min 3 bytes )
+-			player->name = ut2003_strdup( rawpkt, end, &rawpkt );
+-
+-			// Ping
+-			player->ping = swap_long_from_little( rawpkt );
+-			rawpkt += 4;
+-
+-			// Frags
+-			player->frags = swap_long_from_little( rawpkt );
+-			rawpkt += 4;
+-
+-			// Stat ID never set
+-			rawpkt += 4;
+-
+-			// Player properties
+-			no_props = rawpkt[0];
+-			//fprintf( stderr, "noprops %d\n", no_props );
+-			rawpkt++;
+-			while( rawpkt < end && no_props > 0 )
++			else
+ 			{
+-				if ( NULL == ( var = ut2003_strdup( rawpkt, end, &rawpkt ) ) )
+-				{
+-					break;
+-				}
+-				if ( NULL == ( val = ut2003_strdup( rawpkt, end, &rawpkt ) ) )
+-				{
+-					break;
+-				}
+-				//fprintf( stderr, "attrib: %s = %s\n", var, val );
+-
+-				// Things we can use
+-				if ( 0 == strcmp( var, "team" ) )
+-				{
+-					player->team_name = val;
+-				}
+-				else if ( 0 == strcmp( var, "class" ) )
+-				{
+-					player->skin = val;
+-				}
+-				else
++				add_rule(server, "sv_type", "?", NO_FLAGS);
++			}
++			pkt++;
++			if (*pkt == 'w')
++			{
++				add_rule(server, "sv_os", "windows", NO_FLAGS);
++			}
++			else if (*pkt == 'l')
++			{
++				add_rule(server, "sv_os", "linux", NO_FLAGS);
++			}
++			else
++			{
++				char str[2] = "\0";
++				str[0] = *pkt;
++				add_rule(server, "sv_os", str, NO_FLAGS);
++			}
++			pkt++;
++			add_rule(server, "sv_password", *pkt ? "1" : "0", NO_FLAGS);
++			pkt++;
++			add_rule(server, "mod", *pkt ? "1" : "0", NO_FLAGS);
++			if (*pkt)
++			{
++				int n;
++				/* pull out the mod infomation */
++				pkt++;
++				add_rule(server, "mod_info_url", pkt, NO_FLAGS);
++				pkt += strlen(pkt) + 1;
++				if (*pkt)
++				{
++					add_rule(server, "mod_download_url", pkt, NO_FLAGS);
++				}
++				pkt += strlen(pkt) + 1;
++				if (*pkt)
++				{
++					add_rule(server, "mod_detail", pkt, NO_FLAGS);
++				}
++				pkt += strlen(pkt) + 1;
++				n = swap_long_from_little(pkt);
++				sprintf(number, "%d", n);
++				add_rule(server, "modversion", number, NO_FLAGS);
++				pkt += 4;
++				n = swap_long_from_little(pkt);
++				sprintf(number, "%d", n);
++				add_rule(server, "modsize", number, NO_FLAGS);
++				pkt += 4;
++				add_rule(server, "svonly", *pkt ? "1" : "0", NO_FLAGS);
++				pkt++;
++				add_rule(server, "cldll", *pkt ? "1" : "0", NO_FLAGS);
++				pkt++;
++				if (pkt < end)
+ 				{
+-					free( val );
++					add_rule(server, "secure", *pkt ? "1" : "0", NO_FLAGS);
+ 				}
+-
+-				free( var );
+-				no_props--;
+ 			}
+ 		}
+-		break;
+-	default:
+-		while ( rawpkt < end )
+-		{
+-			struct player *player;
+ 
+-			if(rawpkt+4 > end)
++		if (get_player_info && server->num_players)
++		{
++			int requests = server->n_requests;
++			server->next_player_info = server->num_players - 1;
++			send_player_request_packet(server);
++			server->n_requests = requests; // prevent wrong ping
++		}
++		if (get_server_rules)
++		{
++			int requests = server->n_requests;
++			server->next_rule = "";
++			server->retry1 = n_retries;
++			send_rule_request_packet(server);
++			server->n_requests = requests; // prevent wrong ping
++		}
++	}
++	/* 'players' response */
++	else if (rawpkt[4] == 'D' && server->players == NULL)
++	{
++		unsigned int n = 0, temp;
++		struct player *player;
++		struct player **last_player = &server->players;
++		if ((unsigned int)rawpkt[5] > server->num_players)
++		{
++			server->num_players = (unsigned int)rawpkt[5];
++		} pkt = &rawpkt[6];
++		rawpkt[pktlen] = '\0';
++		while (1)
++		{
++			if (*pkt != n + 1)
+ 			{
+-				malformed_packet(server, "player packet too short");
+-				return 1;
++				break;
+ 			}
+-
+-			if ( NULL == ( player = add_player( server, swap_long_from_little(rawpkt) ) ) )
++			n++;
++			pkt++;
++			player = (struct player*)calloc(1, sizeof(struct player));
++			player->name = strdup(pkt);
++			pkt += strlen(pkt) + 1;
++			memcpy(&player->frags, pkt, 4);
++			pkt += 4;
++			memcpy(&temp, pkt, 4);
++			pkt += 4;
++			if (big_endian)
++			{
++				player->frags = swap_long(&player->frags);
++			}
++			player->connect_time = swap_float_from_little(&temp);
++			*last_player = player;
++			last_player = &player->next;
++		}
++		if (n > server->num_players)
++		{
++			server->num_players = n;
++		}
++		server->next_player_info = server->num_players;
++	}
++	/* 'rules' response */
++	else if (rawpkt[4] == 'E' && server->next_rule != NULL)
++	{
++		int n = 0;
++		n = ((unsigned char*)rawpkt)[5] + ((unsigned char*)rawpkt)[6] *256;
++		pkt = &rawpkt[7];
++		while (n)
++		{
++			char *key = pkt;
++			char *value;
++			pkt += strlen(pkt) + 1;
++			if (pkt > end)
+ 			{
+-				return 0;
++				break;
+ 			}
+-
+-			player->name = ut2003_strdup( rawpkt+4, end, &rawpkt );
+-			if(rawpkt+8 > end)
++			value = pkt;
++			pkt += strlen(pkt) + 1;
++			if (pkt > end)
+ 			{
+-				malformed_packet(server, "player packet too short");
+-				return 1;
++				break;
+ 			}
+-			player->ping = swap_long_from_little(rawpkt);
+-			rawpkt+= 4;
+-			player->frags = swap_long_from_little(rawpkt);
+-			rawpkt+= 4;
+-			{
+-				unsigned team = swap_long_from_little(rawpkt);
+-				rawpkt+= 4;
+-				player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-				if(team & 1<<29)
+-					player->team_name = "red";
+-				else if(team & 1<<30)
+-					player->team_name = "blue";
++			if (key[0] == 's' && strcmp(key, "sv_password") == 0)
++			{
++				add_rule(server, key, value, CHECK_DUPLICATE_RULES);
++			}
++			else
++			{
++				add_rule(server, key, value, NO_FLAGS);
+ 			}
++			n--;
+ 		}
++		server->next_rule = NULL;
++	}
++	else if (rawpkt[4] != 'E' && rawpkt[4] != 'D' && rawpkt[4] != 'm' && rawpkt[4] != 'C' && show_errors)
++	{
++		/*	if ( pkt_count) { rawpkt-= 9; pktlen+= 9; } */
++		fprintf(stderr, "Odd packet from HL server %s (packet len %d)\n", server->arg, pktlen);
++		print_packet(server, rawpkt, pktlen);
+ 	}
+ 
+-	return 0;
++	return DONE_AUTO;
+ }
+ 
+ 
+-char *
+-get_rule( struct qserver *server, char *name)
++query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    struct rule *rule;
+-    rule= server->rules;
+-    for ( ; rule != NULL; rule= rule->next)  {
+-	if ( strcmp( name, rule->name) == 0)
+-	    return rule->value;
+-    }
+-    return NULL;
+-}
++	unsigned char *pkt, *end;
++	int len, pnum, ping, packet_loss, n_teams, t;
++	struct player *player;
++	struct player **teams = NULL;
++	struct player **last_player = &server->players;
++	char buf[24];
+ 
+-void
+-deal_with_ut2003_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
++	debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen );
+ 
+-	// For protocol spec see:
+-	// http://unreal.student.utwente.nl/UT2003-queryspec.html
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	}
++	else
++	{
++		gettimeofday(&server->packet_time1, NULL);
++	}
+ 
+-	char *end;
+-	int error = 0, before;
+-	unsigned int packet_header;
++	if (pktlen < sizeof(tribes_info_reponse))
++	{
++		return PKT_ERROR;
++	}
+ 
+-	rawpkt[pktlen]= '\0';
+-	end = &rawpkt[pktlen];
++	if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0)
++	{
++		return PKT_ERROR;
++	}
+ 
+-	packet_header = swap_long_from_little( &rawpkt[0] );
+-	rawpkt += 4;
++	pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)];
+ 
+-	server->protocol_version = packet_header;
+-	if (
+-		packet_header != 0x77 // Pariah Demo?
+-		&& packet_header != 0x78 // UT2003 Demo
+-	    && packet_header != 0x79 // UT2003 Retail
+-	    && packet_header != 0x7e // Unreal2 XMP
+-	    && packet_header != 0x7f // UT2004 Demo
+-	    && packet_header != 0x80 // UT2004 Retail
+-	    )
++	len = *pkt; /* game name: "Tribes" */
++	add_nrule(server, "gamename", (char*)pkt + 1, len);
++	pkt += len + 1;
++	len = *pkt; /* version */
++	add_nrule(server, "version", (char*)pkt + 1, len);
++	pkt += len + 1;
++	len = *pkt; /* server name */
++	server->server_name = strndup((char*)pkt + 1, len);
++	pkt += len + 1;
++	add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS);
++	pkt++; /* flag: dedicated server */
++	add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS);
++	pkt++; /* flag: password on server */
++	server->num_players = *pkt++;
++	server->max_players = *pkt++;
++
++	sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256);
++	add_rule(server, "cpu", buf, NO_FLAGS);
++	pkt++; /* cpu speed, lsb */
++	pkt++; /* cpu speed, msb */
++
++	len = *pkt; /* Mod (game) */
++	add_nrule(server, "mods", (char*)pkt + 1, len);
++	pkt += len + 1;
++
++	len = *pkt; /* game (mission): "C&H" */
++	add_nrule(server, "game", (char*)pkt + 1, len);
++	pkt += len + 1;
++
++	len = *pkt; /* Mission (map) */
++	server->map_name = strndup((char*)pkt + 1, len);
++	pkt += len + 1;
++
++	len = *pkt; /* description (contains Admin: and Email: ) */
++	debug( 2, "%.*s\n", len, pkt + 1);
++	pkt += len + 1;
++
++	n_teams = *pkt++; /* number of teams */
++	if (n_teams == 255)
+ 	{
+-		malformed_packet(server, "Unknown type 0x%x", packet_header);
++		return PKT_ERROR;
+ 	}
++	sprintf(buf, "%d", n_teams);
++	add_rule(server, "numteams", buf, NO_FLAGS);
++
++	len = *pkt; /* first title */
++	debug( 2, "%.*s\n", len, pkt + 1);
++	pkt += len + 1;
++
++	len = *pkt; /* second title */
++	debug( 2, "%.*s\n", len, pkt + 1);
++	pkt += len + 1;
+ 
+-	switch( rawpkt[0] )
++	if (n_teams > 1)
+ 	{
+-	case 0x00:
+-		// Server info
+-		if ( server->server_name == NULL )
++		teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
++		for (t = 0; t < n_teams; t++)
+ 		{
+-			server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+-		}
++			teams[t] = (struct player*)calloc(1, sizeof(struct player));
++			teams[t]->number = TRIBES_TEAM;
++			teams[t]->team = t;
++			len = *pkt; /* team name */
++			teams[t]->name = strndup((char*)pkt + 1, len);
++			debug( 2, "team#0 <%.*s>\n", len, pkt + 1);
++			pkt += len + 1;
+ 
+-		error = ut2003_basic_packet( server, rawpkt, end );
+-		if ( ! error )
+-		{
+-			if(get_server_rules || get_player_info)
++			len = *pkt; /* team score */
++			if (len > 2)
+ 			{
+-				int requests = server->n_requests;
+-				server->next_rule = "";
+-				server->retry1 = n_retries;
+-				server->retry2 = 0; // don't wait for player packet
+-				send_rule_request_packet( server);
+-				server->n_requests = requests; // would produce wrong ping
++				strncpy(buf, (char*)pkt + 1+3, len - 3);
++				buf[len - 3] = '\0';
++			}
++			else
++			{
++				debug( 2, "%s score len %d\n", server->arg, len);
++				buf[0] = '\0';
+ 			}
++			teams[t]->frags = atoi(buf);
++			debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3);
++			pkt += len + 1;
+ 		}
+-		break;
+-
+-	case 0x01:
+-		// Game info
+-		ut2003_rule_packet( server, rawpkt, end );
+-		server->next_rule = "";
+-		server->retry1 = 0; /* we received at least one rule packet so
+-				       no need to retry. We'd get double
+-				       entries otherwise. */
+-		break;
++	}
++	else
++	{
++		len = *pkt; /* DM team? */
++		debug( 2, "%.*s\n", len, pkt + 1);
++		pkt += len + 1;
++		pkt++;
++		n_teams = 0;
++	}
+ 
+-	case 0x02:
+-		// Player info
+-		before = server->n_player_info;
+-		error = ut2003_player_packet( server, rawpkt, end);
+-		if (before == server->n_player_info )
++	pnum = 0;
++	while ((char*)pkt < (rawpkt + pktlen))
++	{
++		ping = (unsigned int) *pkt << 2;
++		pkt++;
++		packet_loss = *pkt;
++		pkt++;
++		debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt);
++		pkt++;
++		len = *pkt;
++		if ((char*)pkt + len > (rawpkt + pktlen))
+ 		{
+-			error = 1;
++			break;
+ 		}
+-		break;
+-
+-	case 0x10:
+-		// Pariah Server info
+-		if ( server->server_name == NULL )
++		player = (struct player*)calloc(1, sizeof(struct player));
++		player->team = pkt[ - 1];
++		if (n_teams && player->team < n_teams)
+ 		{
+-			server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
++			player->team_name = teams[player->team]->name;
+ 		}
+-
+-		error = pariah_basic_packet( server, rawpkt, end );
+-		if ( ! error )
++		else if (player->team == 255 && n_teams)
+ 		{
+-			// N.B. pariah always sends a rules and players packet
+-			int requests = server->n_requests;
+-			server->next_rule = "";
+-			server->retry1 = n_retries;
+-			server->retry2 = 0;
+-			server->n_requests = requests; // would produce wrong ping
++			player->team_name = "Unknown";
+ 		}
+-		break;
+-
+-	case 0x11:
+-		// Game info
+-		pariah_rule_packet( server, rawpkt, end );
+-		server->retry1 = 0; /* we received at least one rule packet so
+-				       no need to retry. We'd get double
+-				       entries otherwise. */
+-		break;
+-
+-	case 0x12:
+-		// Player info
+-		before = server->n_player_info;
+-		pariah_player_packet( server, rawpkt, end );
+-		if ( before == server->n_player_info )
++		player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++		player->ping = ping;
++		player->packet_loss = packet_loss;
++		player->name = strndup((char*)pkt + 1, len);
++		debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1);
++		pkt += len + 1;
++		len = *pkt;
++		debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1);
++		end = (unsigned char*)strchr((char*)pkt + 9, 0x9);
++		if (end)
+ 		{
+-			error = 1;
++			strncpy(buf, (char*)pkt + 9, end - (pkt + 9));
++			buf[end - (pkt + 9)] = '\0';
++			player->frags = atoi(buf);
++			debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9);
+ 		}
+-		break;
+ 
++		 *last_player = player;
++		last_player = &player->next;
+ 
+-	default:
+-		malformed_packet(server, "Unknown packet type 0x%x", (unsigned)rawpkt[0]);
+-		break;
++		pkt += len + 1;
++		pnum++;
+ 	}
+ 
+-	/* don't cleanup if we fetch server rules. We would lose
+-	 * rule packets as we don't know how many we get
+-	 * We do clean up if we don't fetch server rules so we don't
+-	 * need to wait for timeout.
+-	 */
+-	if (error
+-	|| (!get_server_rules && !get_player_info)
+-	|| (!get_server_rules && server->num_players == server->n_player_info)
+-	|| (server->next_rule == NULL && server->num_players == server->n_player_info))
++	for (t = n_teams; t;)
+ 	{
+-		cleanup_qserver( server, 1 );
++		t--;
++		teams[t]->next = server->players;
++		server->players = teams[t];
+ 	}
++	free(teams);
++
++	return DONE_AUTO;
+ }
+ 
+-int
+-deal_with_unrealmaster_packet( struct qserver *server, char *rawpkt, int pktlen)
++void get_tribes2_player_type(struct player *player)
+ {
+-    if ( pktlen == 0)  {
+-	cleanup_qserver( server, 1);
+-	return 0;
+-    }
+-    print_packet( server, rawpkt, pktlen);
+-	puts( "--");
+-    return 0;
++	char *name = player->name;
++	for (; *name; name++)
++	{
++		switch (*name)
++		{
++			case 0x8:
++				player->type_flag = PLAYER_TYPE_NORMAL;
++				continue;
++			case 0xc:
++				player->type_flag = PLAYER_TYPE_ALIAS;
++				continue;
++			case 0xe:
++				player->type_flag = PLAYER_TYPE_BOT;
++				continue;
++			case 0xb:
++				break;
++			default:
++				continue;
++		}
++		name++;
++		if (isprint(*name))
++		{
++			char *n = name;
++			for (; isprint(*n); n++)
++				;
++			player->tribe_tag = strndup(name, n - name);
++			name = n;
++		}
++		if (! *name)
++		{
++			break;
++		}
++	}
+ }
+ 
+-/* Returns 1 if the query is done (server may be freed) and 0 if not.
+- */
+-int
+-deal_with_halflife_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+-    char *pkt;
+-    char *end= &rawpkt[pktlen];
+-    int pkt_index= 0, pkt_max= 0;
+-    char number[16];
+-    short pkt_id;
+-
+-    if ( server->server_name == NULL)
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-
+-    if ( pktlen < 5)
+-	return cleanup_qserver( server, 1);
+-
+-    if ( ((rawpkt[0] != '\377' && rawpkt[0] != '\376') || rawpkt[1] != '\377' ||
+-	    rawpkt[2] != '\377' || rawpkt[3] != '\377') && show_errors)  {
+-	unsigned int ipaddr= ntohl(server->ipaddr);
+-	fprintf( stderr,
+-		"Odd packet from server %d.%d.%d.%d:%hu, processing ...\n",
+-		(ipaddr>>24)&0xff, (ipaddr>>16)&0xff,
+-		(ipaddr>>8)&0xff, ipaddr&0xff, ntohs(server->port));
+-	print_packet( server, rawpkt, pktlen);
+-    }
+-
+-    if ( ((unsigned char*)rawpkt)[0] == 0xfe)  {
+-	SavedData *sdata;
+-	pkt_index= ((unsigned char*)rawpkt)[8] >> 4;
+-	pkt_max= ((unsigned char*)rawpkt)[8] & 0xf;
+-	memcpy( &pkt_id, &rawpkt[4], 2);
+-
+-	if ( server->saved_data.data == NULL)
+-	    sdata= & server->saved_data;
+-	else  {
+-	    sdata= (SavedData*) calloc( 1, sizeof(SavedData));
+-	    sdata->next= server->saved_data.next;
+-	    server->saved_data.next= sdata;
+-	}
+-
+-	sdata->pkt_index= pkt_index;
+-	sdata->pkt_max= pkt_max;
+-	sdata->pkt_id= pkt_id;
+-	sdata->datalen= pktlen-9;
+-	sdata->data= (char*) malloc( pktlen-9);
+-	memcpy( sdata->data, &rawpkt[9], pktlen-9);
++	char str[256], *pktstart = pkt, *term, *start;
++	unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
++	unsigned int net_protocol;
++	unsigned short cpu_speed;
++	int n_teams = 0, n_players;
++	struct player **teams = NULL, *player;
++	struct player **last_player = &server->players;
++	int query_version;
+ 
+-	/* combine_packets will call us recursively */
+-	return combine_packets( server);
++	debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen );
+ 
+-/*
+-fprintf( OF, "pkt_index %d pkt_max %d\n", pkt_index, pkt_max);
+-	rawpkt+= 9;
+-	pktlen-= 9;
+-*/
+-    }
++	pkt[pktlen] = '\0';
+ 
+-    /* 'info' response */
+-    if ( rawpkt[4] == 'C' || rawpkt[4] == 'm')  {
+-	if ( server->server_name != NULL)
+-	    return 0;
+-	pkt= &rawpkt[5];
+-	server->address= strdup( pkt);
+-	pkt+= strlen(pkt)+1;
+-	server->server_name= strdup( pkt);
+-	pkt+= strlen(pkt)+1;
+-	server->map_name= strdup( pkt);
+-	pkt+= strlen(pkt)+1;
+-
+-	if ( *pkt)
+-	    add_rule( server, "gamedir", pkt, NO_FLAGS);
+-	if ( *pkt && strcmp( pkt, "valve") != 0)  {
+-	    server->game= add_rule( server, "game", pkt, NO_FLAGS)->value;
+-	    server->flags |= FLAG_DO_NOT_FREE_GAME;
+-	}
+-	pkt+= strlen(pkt)+1;
+-	if ( *pkt)
+-	    add_rule( server, "gamename", pkt, NO_FLAGS);
+-	pkt+= strlen(pkt)+1;
+-
+-	server->num_players= (unsigned int)pkt[0];
+-	server->max_players= (unsigned int)pkt[1];
+-	pkt+= 2;
+-	if ( pkt < end)  {
+-	    int protocol= *((unsigned char *)pkt);
+-	    sprintf( number, "%d", protocol);
+-	    add_rule( server, "protocol", number, NO_FLAGS);
+-	    pkt++;
+-	}
+-
+-	if ( rawpkt[4] == 'm')  {
+-	    if ( *pkt == 'd')
+-		add_rule( server, "sv_type", "dedicated", NO_FLAGS);
+-	    else if ( *pkt == 'l')
+-		add_rule( server, "sv_type", "listen", NO_FLAGS);
+-	    else
+-		add_rule( server, "sv_type", "?", NO_FLAGS);
+-	    pkt++;
+-	    if ( *pkt == 'w')
+-		add_rule( server, "sv_os", "windows", NO_FLAGS);
+-	    else if ( *pkt == 'l')
+-		add_rule( server, "sv_os", "linux", NO_FLAGS);
+-	    else  {
+-		char str[2]= "\0";
+-		str[0]= *pkt;
+-		add_rule( server, "sv_os", str, NO_FLAGS);
+-	    }
+-	    pkt++;
+-	    add_rule( server, "sv_password", *pkt ? "1" : "0", NO_FLAGS);
+-	    pkt++;
+-	    add_rule( server, "mod", *pkt ? "1" : "0", NO_FLAGS);
+-	    if ( *pkt)  {
+-		int n;
+-		/* pull out the mod infomation */
+-		pkt++;
+-		add_rule( server, "mod_info_url", pkt, NO_FLAGS);
+-		pkt+= strlen( pkt)+1;
+-		if ( *pkt)
+-		    add_rule( server, "mod_download_url", pkt, NO_FLAGS);
+-		pkt+= strlen( pkt)+1;
+-		if ( *pkt)
+-		    add_rule( server, "mod_detail", pkt, NO_FLAGS);
+-		pkt+= strlen( pkt)+1;
+-		n= swap_long_from_little( pkt);
+-		sprintf( number, "%d", n);
+-		add_rule( server, "modversion", number, NO_FLAGS);
+-		pkt+= 4;
+-		n= swap_long_from_little( pkt);
+-		sprintf( number, "%d", n);
+-		add_rule( server, "modsize", number, NO_FLAGS);
+-		pkt+= 4;
+-		add_rule( server, "svonly", *pkt ? "1" : "0", NO_FLAGS);
+-		pkt++;
+-		add_rule( server, "cldll", *pkt ? "1" : "0", NO_FLAGS);
+-		pkt++;
+-		if ( pkt < end)
+-		    add_rule( server, "secure", *pkt ? "1" : "0", NO_FLAGS);
+-	    }
+-	}
+-
+-	if ( get_player_info && server->num_players)  {
+-	    int requests = server->n_requests;
+-	    server->next_player_info= server->num_players-1;
+-	    send_player_request_packet( server);
+-	    server->n_requests = requests; // prevent wrong ping
+-	}
+-	if ( get_server_rules)  {
+-	    int requests = server->n_requests;
+-	    server->next_rule= "";
+-	    server->retry1= n_retries;
+-	    send_rule_request_packet( server);
+-	    server->n_requests = requests; // prevent wrong ping
+-	}
+-    }
+-    /* 'players' response */
+-    else if ( rawpkt[4] == 'D' && server->players == NULL)  {
+-	unsigned int n= 0, temp;
+-	struct player *player;
+-	struct player **last_player= & server->players;
+-	if ( (unsigned int)rawpkt[5] > server->num_players)
+-	    server->num_players= (unsigned int)rawpkt[5];
+-	pkt= &rawpkt[6];
+-	rawpkt[pktlen]= '\0';
+-	while (1)  {
+-	    if ( *pkt != n+1)
+-		break;
+-	    n++;
+-	    pkt++;
+-	    player= (struct player*) calloc( 1, sizeof(struct player));
+-	    player->name= strdup( pkt);
+-	    pkt+= strlen(pkt)+1;
+-	    memcpy( &player->frags, pkt, 4);
+-	    pkt+= 4;
+-	    memcpy( &temp, pkt, 4);
+-	    pkt+= 4;
+-	    if ( big_endian)  {
+-		player->frags= swap_long( &player->frags);
+-	    }
+-	    player->connect_time= swap_float_from_little(&temp);
+-	    *last_player= player;
+-	    last_player= & player->next;
+-	}
+-	if ( n > server->num_players)
+-	    server->num_players= n;
+-	server->next_player_info= server->num_players;
+-    }
+-    /* 'rules' response */
+-    else if ( rawpkt[4] == 'E' && server->next_rule != NULL)  {
+-	int n= 0;
+-	n= ((unsigned char*)rawpkt)[5] + ((unsigned char *)rawpkt)[6]*256;
+-	pkt= &rawpkt[7];
+-	while ( n)  {
+-	    char *key= pkt;
+-	    char *value;
+-	    pkt+= strlen(pkt)+1;
+-	    if ( pkt > end)
+-		break;
+-	    value= pkt;
+-	    pkt+= strlen(pkt)+1;
+-	    if ( pkt > end)
+-		break;
+-	    if ( key[0] == 's' && strcmp( key, "sv_password") == 0)
+-		add_rule( server, key, value, CHECK_DUPLICATE_RULES);
+-	    else
+-		add_rule( server, key, value, NO_FLAGS);
+-	    n--;
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 	}
+-	server->next_rule= NULL;
+-    }
+-    else if ( rawpkt[4] != 'E' && rawpkt[4] != 'D' && rawpkt[4] != 'm' &&
+-		rawpkt[4] != 'C' && show_errors)  {
+-/*	if ( pkt_count) { rawpkt-= 9; pktlen+= 9; } */
+-	fprintf( stderr, "Odd packet from HL server %s (packet len %d)\n",
+-		server->arg, pktlen);
+-	print_packet( server, rawpkt, pktlen);
+-    }
++	/*
++	else
++	gettimeofday( &server->packet_time1, NULL);
++	 */
+ 
+-    return cleanup_qserver( server, 0);
+-}
++	if (pkt[0] == TRIBES2_RESPONSE_PING)
++	{
++		if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0)
++		{
++			return PKT_ERROR;
++		}
+ 
++		strncpy(str, pkt + 10, pkt[6] - 3);
++		str[pkt[6] - 3] = '\0';
++		query_version = atoi(str);
++		add_nrule(server, "queryversion", pkt + 7, pkt[6]);
++		pkt += 7+pkt[6];
+ 
+-static int tribes_debug= 0;
++		server->protocol_version = query_version;
++		if (query_version != 3 && query_version != 5)
++		{
++			server->server_name = strdup("Unknown query version");
++			return PKT_ERROR;
++		}
+ 
+-void
+-deal_with_tribes_packet( struct qserver *server, char *rawpkt, int pktlen)
+-{
+-    unsigned char *pkt, *end;
+-    int len, pnum, ping, packet_loss, n_teams, t;
+-    struct player *player;
+-    struct player **teams= NULL;
+-    struct player **last_player= & server->players;
+-    char buf[24];
++		if (query_version == 5)
++		{
++			net_protocol = swap_long_from_little(pkt);
++			sprintf(str, "%u", net_protocol);
++			add_rule(server, "net_protocol", str, NO_FLAGS);
++			pkt += 4;
++		}
++		minimum_net_protocol = swap_long_from_little(pkt);
++		sprintf(str, "%u", minimum_net_protocol);
++		add_rule(server, "minimum_net_protocol", str, NO_FLAGS);
++		pkt += 4;
++		build_version = swap_long_from_little(pkt);
++		sprintf(str, "%u", build_version);
++		add_rule(server, "build_version", str, NO_FLAGS);
++		pkt += 4;
+ 
+-    if ( server->server_name == NULL)
+-	{
+-		server->ping_total+= time_delta( &packet_recv_time,
+-			&server->packet_time1);
++		server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt));
++
++		/* Always send the player request because the ping packet
++		 * contains very little information */
++		send_player_request_packet(server);
++		return 0;
+ 	}
+-    else
++	else if (pkt[0] != TRIBES2_RESPONSE_INFO)
+ 	{
+-		gettimeofday( &server->packet_time1, NULL);
++		return PKT_ERROR;
+ 	}
+ 
+-    if ( pktlen < sizeof( tribes_info_reponse))
++	pkt += 6;
++	for (i = 0; i < *(unsigned char*)pkt; i++)
++	if (!isprint(pkt[i + 1]))
+ 	{
+-		cleanup_qserver( server, 1);
+-		return;
+-    }
++		return PKT_ERROR;
++	}
++	add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt);
++	server->game = strndup(pkt + 1, *(unsigned char*)pkt);
++	pkt += *pkt + 1;
++	add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt);
++	pkt += *pkt + 1;
++	server->map_name = strndup(pkt + 1, *(unsigned char*)pkt);
++	pkt += *pkt + 1;
+ 
+-	if ( strncmp( rawpkt, tribes_players_reponse,
+-			sizeof( tribes_players_reponse)) != 0)
++	status = *(unsigned char*)pkt;
++	sprintf(str, "%u", status);
++	add_rule(server, "status", str, NO_FLAGS);
++	if (status &TRIBES2_STATUS_DEDICATED)
+ 	{
+-		cleanup_qserver( server, 1);
+-		return;
++		add_rule(server, "dedicated", "1", NO_FLAGS);
+ 	}
+-
+-    pkt= (unsigned char*) &rawpkt[sizeof( tribes_info_reponse)];
+-
+-    len= *pkt;		/* game name: "Tribes" */
+-    add_nrule( server, "gamename", (char*)pkt+1, len);
+-    pkt+= len+1;
+-    len= *pkt;		/* version */
+-    add_nrule( server, "version", (char*)pkt+1, len);
+-    pkt+= len+1;
+-    len= *pkt;		/* server name */
+-    server->server_name= strndup( (char*)pkt+1, len);
+-    pkt+= len+1;
+-    add_rule( server, "dedicated", *pkt?"1":"0", NO_FLAGS);
+-    pkt++;		/* flag: dedicated server */
+-    add_rule( server, "needpass", *pkt?"1":"0", NO_FLAGS);
+-    pkt++;		/* flag: password on server */
+-    server->num_players= *pkt++;
+-    server->max_players= *pkt++;
+-
+-    sprintf( buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1]*256);
+-    add_rule( server, "cpu", buf, NO_FLAGS);
+-    pkt++;		/* cpu speed, lsb */
+-    pkt++;		/* cpu speed, msb */
+-
+-    len= *pkt;		/* Mod (game) */
+-    add_nrule( server, "mods", (char*)pkt+1, len);
+-    pkt+= len+1;
+-
+-    len= *pkt;		/* game (mission): "C&H" */
+-    add_nrule( server, "game", (char*)pkt+1, len);
+-    pkt+= len+1;
+-
+-    len= *pkt;		/* Mission (map) */
+-    server->map_name= strndup( (char*)pkt+1, len);
+-    pkt+= len+1;
+-
+-    len= *pkt;		/* description (contains Admin: and Email: ) */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+-    pkt+= len+1;
+-
+-    n_teams= *pkt++;		/* number of teams */
+-    if ( n_teams == 255)  {
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
+-    sprintf( buf, "%d", n_teams);
+-    add_rule( server, "numteams", buf, NO_FLAGS);
+-
+-    len= *pkt;		/* first title */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+-    pkt+= len+1;
+-
+-    len= *pkt;		/* second title */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+-    pkt+= len+1;
+-
+-    if ( n_teams > 1)  {
+-	teams= (struct player**) calloc( 1, sizeof(struct player*) * n_teams);
+-	for ( t= 0; t < n_teams; t++)  {
+-	    teams[t]= (struct player*) calloc(1, sizeof(struct player));
+-	    teams[t]->number= TRIBES_TEAM;
+-	    teams[t]->team= t;
+-	    len= *pkt;		/* team name */
+-	    teams[t]->name= strndup( (char*)pkt+1, len);
+-if ( tribes_debug) printf( "team#0 <%.*s>\n", len, pkt+1);
+-	    pkt+= len+1;
+-
+-	    len= *pkt;		/* team score */
+-if ( len <= 2 && tribes_debug) printf( "%s score len %d\n", server->arg, len);
+-	    if ( len > 2)  {
+-		strncpy( buf, (char*)pkt+1+3, len-3);
+-		buf[len-3]= '\0';
+-	    }
+-	    else
+-		buf[0]= '\0';
+-	    teams[t]->frags= atoi( buf);
+-if ( tribes_debug) printf( "team#0 <%.*s>\n", len-3, pkt+1+3);
+-	    pkt+= len+1;
++	if (status &TRIBES2_STATUS_PASSWORD)
++	{
++		add_rule(server, "password", "1", NO_FLAGS);
++	}
++	if (status &TRIBES2_STATUS_LINUX)
++	{
++		add_rule(server, "linux", "1", NO_FLAGS);
++	}
++	if (status &TRIBES2_STATUS_TEAMDAMAGE)
++	{
++		add_rule(server, "teamdamage", "1", NO_FLAGS);
++	}
++	if (server->protocol_version == 3)
++	{
++		if (status &TRIBES2_STATUS_TOURNAMENT_VER3)
++		{
++			add_rule(server, "tournament", "1", NO_FLAGS);
++		}
++		if (status &TRIBES2_STATUS_NOALIAS_VER3)
++		{
++			add_rule(server, "no_aliases", "1", NO_FLAGS);
++		}
++	}
++	else
++	{
++		if (status &TRIBES2_STATUS_TOURNAMENT)
++		{
++			add_rule(server, "tournament", "1", NO_FLAGS);
++		}
++		if (status &TRIBES2_STATUS_NOALIAS)
++		{
++			add_rule(server, "no_aliases", "1", NO_FLAGS);
++		}
+ 	}
+-    }
+-    else  {
+-	len= *pkt;		/* DM team? */
+-if ( tribes_debug) printf( "%.*s\n", len, pkt+1);
+-	pkt+= len+1;
+ 	pkt++;
+-	n_teams= 0;
+-    }
+-
+-    pnum= 0;
+-    while ( (char*)pkt < (rawpkt+pktlen))  {
+-	ping= (unsigned int)*pkt << 2;
++	server->num_players = *(unsigned char*)pkt;
+ 	pkt++;
+-	packet_loss= *pkt;
++	server->max_players = *(unsigned char*)pkt;
+ 	pkt++;
+-if ( tribes_debug) printf( "player#%d, team #%d\n", pnum, (int)*pkt);
++	sprintf(str, "%u", *(unsigned char*)pkt);
++	add_rule(server, "bot_count", str, NO_FLAGS);
+ 	pkt++;
+-	len= *pkt;
+-	if ( (char*)pkt+len > (rawpkt+pktlen))
+-	    break;
+-	player= (struct player*) calloc( 1, sizeof(struct player));
+-	player->team= pkt[-1];
+-	if ( n_teams && player->team < n_teams)
+-	    player->team_name= teams[player->team]->name;
+-	else if ( player->team == 255 && n_teams)
+-	    player->team_name= "Unknown";
+-	player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-	player->ping= ping;
+-	player->packet_loss= packet_loss;
+-	player->name= strndup( (char*)pkt+1, len);
+-if ( tribes_debug) 	printf( "player#%d, name %.*s\n", pnum, len, pkt+1);
+-	pkt+= len+1;
+-	len= *pkt;
+-if ( tribes_debug) printf( "player#%d, info <%.*s>\n", pnum, len, pkt+1);
+-	end= (unsigned char*) strchr( (char*)pkt+9, 0x9);
+-	if ( end)  {
+-	    strncpy( buf, (char*)pkt+9, end-(pkt+9));
+-	    buf[end-(pkt+9)]= '\0';
+-	    player->frags= atoi( buf);
+-if ( tribes_debug) printf( "player#%d, score <%.*s>\n", pnum, (unsigned)(end-(pkt+9)), pkt+9);
+-	}
+-	*last_player= player;
+-	last_player= & player->next;
++	cpu_speed = swap_short_from_little(pkt);
++	sprintf(str, "%hu", cpu_speed);
++	add_rule(server, "cpu_speed", str, NO_FLAGS);
++	pkt += 2;
+ 
+-	pkt+= len+1;
+-	pnum++;
+-    }
++	if (strcmp(server->server_name, "VER3") == 0)
++	{
++		free(server->server_name);
++		server->server_name = strndup(pkt + 1, *(unsigned char*)pkt);
++	}
++	else
++	{
++		add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt);
++	}
+ 
+-    for ( t= n_teams; t;)  {
+-	t--;
+-	teams[t]->next= server->players;
+-	server->players= teams[t];
+-    }
+-    free( teams);
++	pkt += *(unsigned char*)pkt + 1;
++	len = swap_short_from_little(pkt);
++	pkt += 2;
++	start = pkt;
++	if (len + (pkt - pktstart) > pktlen)
++	{
++		len -= (len + (pkt - pktstart)) - pktlen;
++	}
+ 
+-    cleanup_qserver( server, 0);
+-}
++	if (len == 0 || pkt - pktstart >= pktlen)
++	{
++		goto info_done;
++	}
+ 
+-void
+-get_tribes2_player_type( struct player *player)
+-{
+-    char *name= player->name;
+-    for ( ; *name; name++)  {
+-	switch ( *name)  {
+-	case 0x8: player->type_flag= PLAYER_TYPE_NORMAL; continue;
+-	case 0xc: player->type_flag= PLAYER_TYPE_ALIAS; continue;
+-	case 0xe: player->type_flag= PLAYER_TYPE_BOT; continue;
+-	case 0xb: break;
+-	default: continue;
+-	}
+-	name++;
+-	if ( isprint( *name))  {
+-	    char *n= name;
+-	    for ( ; isprint(*n); n++)
+-		;
+-	    player->tribe_tag= strndup( name, n-name);
+-	    name= n;
++	term = strchr(pkt, 0xa);
++	if (!term)
++	{
++		goto info_done;
+ 	}
+-	if ( !*name)
+-	    break;
+-    }
+-}
++	*term = '\0';
++	n_teams = atoi(pkt);
++	sprintf(str, "%d", n_teams);
++	add_rule(server, "numteams", str, NO_FLAGS);
++	pkt = term + 1;
+ 
+-void
+-deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen)
+-{
+-    char str[256], *pktstart= pkt, *term, *start;
+-    unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
+-    unsigned int net_protocol;
+-    unsigned short cpu_speed;
+-    int n_teams= 0, n_players;
+-    struct player **teams= NULL, *player;
+-    struct player **last_player= & server->players;
+-    int query_version;
+-
+-    pkt[pktlen]= '\0';
+-
+-    if ( server->server_name == NULL)
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
+-/*
+-    else
+-	gettimeofday( &server->packet_time1, NULL);
+-*/
++	if (pkt - pktstart >= pktlen)
++	{
++		goto info_done;
++	}
+ 
+-    if ( pkt[0] == TRIBES2_RESPONSE_PING)  {
+-	if ( pkt[6] < 4 || pkt[6] > 12 || strncmp( pkt+7, "VER", 3) != 0)  {
+-	    cleanup_qserver( server, 1);
+-	    return;
+-	}
+-	strncpy( str, pkt+10, pkt[6]-3);
+-	str[ pkt[6]-3]= '\0';
+-	query_version= atoi( str);
+-	add_nrule(server,"queryversion", pkt+7, pkt[6]);
+-	pkt+= 7 + pkt[6];
+-
+-	server->protocol_version= query_version;
+-	if ( query_version != 3 && query_version != 5) {
+-	    server->server_name= strdup( "Unknown query version");
+-	    cleanup_qserver( server, 1);
+-	    return;
+-	}
+-
+-	if ( query_version == 5)  {
+-	    net_protocol= swap_long_from_little( pkt);
+-	    sprintf( str, "%u", net_protocol);
+-	    add_rule(server,"net_protocol",str, NO_FLAGS);
+-	    pkt+= 4;
+-	}
+-	minimum_net_protocol= swap_long_from_little( pkt);
+-	sprintf( str, "%u", minimum_net_protocol);
+-	add_rule(server,"minimum_net_protocol",str, NO_FLAGS);
+-	pkt+= 4;
+-	build_version= swap_long_from_little( pkt);
+-	sprintf( str, "%u", build_version);
+-	add_rule(server,"build_version",str, NO_FLAGS);
+-	pkt+= 4;
+-
+-	server->server_name= strndup( pkt+1, *(unsigned char*)(pkt));
+-
+-	/* Always send the player request because the ping packet
+-	 * contains very little information */
+-	send_player_request_packet(server);
+-	return;
+-    }
+-    else if ( pkt[0] != TRIBES2_RESPONSE_INFO)  {
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
++	teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
++	for (t = 0; t < n_teams; t++)
++	{
++		teams[t] = (struct player*)calloc(1, sizeof(struct player));
++		teams[t]->number = TRIBES_TEAM;
++		teams[t]->team = t;
++		/* team name */
++		term = strchr(pkt, 0x9);
++		if (!term)
++		{
++			n_teams = t;
++			goto info_done;
++		} teams[t]->name = strndup(pkt, term - pkt);
++		pkt = term + 1;
++		term = strchr(pkt, 0xa);
++		if (!term)
++		{
++			n_teams = t;
++			goto info_done;
++		}
++		*term = '\0';
++		teams[t]->frags = atoi(pkt);
++		pkt = term + 1;
++		if (pkt - pktstart >= pktlen)
++		{
++			goto info_done;
++		}
++	}
+ 
+-    pkt+= 6;
+-    for ( i= 0; i < *(unsigned char *)pkt; i++)
+-	if ( !isprint(pkt[i+1]))  {
+-	    cleanup_qserver( server, 1);
+-	    return;
+-	}
+-    add_nrule( server, server->type->game_rule, pkt+1, *(unsigned char *)pkt);
+-    server->game= strndup( pkt+1, *(unsigned char *)pkt);
+-    pkt+= *pkt + 1;
+-    add_nrule( server, "mission", pkt+1, *(unsigned char *)pkt);
+-    pkt+= *pkt + 1;
+-    server->map_name= strndup( pkt+1, *(unsigned char *)pkt);
+-    pkt+= *pkt + 1;
+-
+-    status= *(unsigned char *)pkt;
+-    sprintf( str, "%u", status);
+-    add_rule( server, "status", str, NO_FLAGS);
+-    if ( status & TRIBES2_STATUS_DEDICATED)
+-	add_rule( server, "dedicated", "1", NO_FLAGS);
+-    if ( status & TRIBES2_STATUS_PASSWORD)
+-	add_rule( server, "password", "1", NO_FLAGS);
+-    if ( status & TRIBES2_STATUS_LINUX)
+-	add_rule( server, "linux", "1", NO_FLAGS);
+-    if ( status & TRIBES2_STATUS_TEAMDAMAGE)
+-	add_rule( server, "teamdamage", "1", NO_FLAGS);
+-    if ( server->protocol_version == 3)  {
+-	if ( status & TRIBES2_STATUS_TOURNAMENT_VER3)
+-	    add_rule( server, "tournament", "1", NO_FLAGS);
+-	if ( status & TRIBES2_STATUS_NOALIAS_VER3)
+-	    add_rule( server, "no_aliases", "1", NO_FLAGS);
+-    }
+-    else  {
+-	if ( status & TRIBES2_STATUS_TOURNAMENT)
+-	    add_rule( server, "tournament", "1", NO_FLAGS);
+-	if ( status & TRIBES2_STATUS_NOALIAS)
+-	    add_rule( server, "no_aliases", "1", NO_FLAGS);
+-    }
+-    pkt++;
+-    server->num_players= *(unsigned char *)pkt;
+-    pkt++;
+-    server->max_players= *(unsigned char *)pkt;
+-    pkt++;
+-    sprintf( str, "%u", *(unsigned char *)pkt);
+-    add_rule( server, "bot_count", str, NO_FLAGS);
+-    pkt++;
+-    cpu_speed= swap_short_from_little( pkt);
+-    sprintf( str, "%hu", cpu_speed);
+-    add_rule( server, "cpu_speed", str, NO_FLAGS);
+-    pkt+= 2;
+-
+-    if ( strcmp( server->server_name, "VER3") == 0)  {
+-	free( server->server_name);
+-	server->server_name= strndup( pkt+1, *(unsigned char*)pkt);
+-    }
+-    else
+-	add_nrule( server, "info", pkt+1, *(unsigned char*)pkt);
++	term = strchr(pkt, 0xa);
++	if (!term || term - start >= len)
++	{
++		goto info_done;
++	}
++	*term = '\0';
++	n_players = atoi(pkt);
++	pkt = term + 1;
+ 
+-    pkt+= *(unsigned char*)pkt + 1;
+-    len= swap_short_from_little( pkt);
+-    pkt+= 2;
+-    start= pkt;
+-    if ( len+(pkt-pktstart) > pktlen)
+-	len-= (len+(pkt-pktstart)) - pktlen;
+-
+-    if ( len == 0 || pkt-pktstart >= pktlen) goto info_done;
+-
+-    term= strchr( pkt, 0xa);
+-    if ( !term) goto info_done;
+-    *term= '\0';
+-    n_teams= atoi( pkt);
+-    sprintf( str, "%d", n_teams);
+-    add_rule( server, "numteams", str, NO_FLAGS);
+-    pkt= term + 1;
+-
+-    if ( pkt-pktstart >= pktlen) goto info_done;
+-
+-    teams= (struct player**) calloc( 1, sizeof(struct player*) * n_teams);
+-    for ( t= 0; t < n_teams; t++)  {
+-	teams[t]= (struct player*) calloc(1, sizeof(struct player));
+-	teams[t]->number= TRIBES_TEAM;
+-	teams[t]->team= t;
+-	/* team name */
+-	term= strchr( pkt, 0x9);
+-	if ( !term) { n_teams= t; goto info_done; }
+-	teams[t]->name= strndup( pkt, term-pkt);
+-	pkt= term+1;
+-	term= strchr( pkt, 0xa);
+-	if ( !term) { n_teams= t; goto info_done; }
+-	*term='\0';
+-	teams[t]->frags= atoi(pkt);
+-	pkt= term+1;
+-	if ( pkt-pktstart >= pktlen) goto info_done;
+-    }
++	for (i = 0; i < n_players && pkt - start < len; i++)
++	{
++		pkt++; /* skip first byte (0x10) */
++		if (pkt - start >= len)
++		{
++			break;
++		}
++		player = (struct player*)calloc(1, sizeof(struct player));
++		term = strchr(pkt, 0x11);
++		if (!term || term - start >= len)
++		{
++			free(player);
++			break;
++		} player->name = strndup(pkt, term - pkt);
++		get_tribes2_player_type(player);
++		pkt = term + 1;
++		pkt++; /* skip 0x9 */
++		if (pkt - start >= len)
++		{
++			break;
++		}
++		term = strchr(pkt, 0x9);
++		if (!term || term - start >= len)
++		{
++			free(player->name);
++			free(player);
++			break;
++		}
++		for (t = 0; t < n_teams; t++)
++		{
++			if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0)
++			{
++				break;
++			}
++		}
++		if (t == n_teams)
++		{
++			player->team = - 1;
++			player->team_name = "Unassigned";
++		}
++		else
++		{
++			player->team = t;
++			player->team_name = teams[t]->name;
++		}
++		player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++		pkt = term + 1;
++		for (s = 0; *pkt != 0xa && pkt - start < len; pkt++)
++		{
++			str[s++] = *pkt;
++		}
++		str[s] = '\0';
++		player->frags = atoi(str);
++		if (*pkt == 0xa)
++		{
++			pkt++;
++		}
+ 
+-    term= strchr( pkt, 0xa);
+-    if ( !term || term-start >= len) goto info_done;
+-    *term= '\0';
+-    n_players= atoi( pkt);
+-    pkt= term + 1;
+-
+-    for ( i= 0; i < n_players && pkt-start < len; i++)  {
+-	pkt++;		/* skip first byte (0x10) */
+-	if ( pkt-start >= len) break;
+-	player= (struct player*) calloc( 1, sizeof(struct player));
+-	term= strchr( pkt, 0x11);
+-	if ( !term || term-start >= len) {
+-	    free( player);
+-	    break;
+-	}
+-	player->name= strndup( pkt, term-pkt);
+-	get_tribes2_player_type( player);
+-	pkt= term+1;
+-	pkt++;		/* skip 0x9 */
+-	if ( pkt-start >= len) break;
+-	term= strchr( pkt, 0x9);
+-	if ( !term || term-start >= len) {
+-	    free( player->name);
+-	    free( player);
+-	    break;
+-	}
+-	for ( t= 0; t < n_teams; t++)  {
+-	    if ( term-pkt == strlen(teams[t]->name) &&
+-			strncmp( pkt, teams[t]->name, term-pkt) == 0)
+-		break;
++		*last_player = player;
++		last_player = &player->next;
+ 	}
+-	if ( t == n_teams)  {
+-	    player->team= -1;
+-	    player->team_name= "Unassigned";
+-	}
+-	else  {
+-	    player->team= t;
+-	    player->team_name= teams[t]->name;
+-	}
+-	player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-	pkt= term+1;
+-	for ( s= 0; *pkt != 0xa && pkt-start < len; pkt++)
+-	    str[s++]= *pkt;
+-	str[s]= '\0';
+-	player->frags= atoi(str);
+-	if ( *pkt == 0xa)
+-	    pkt++;
+-
+-	*last_player= player;
+-	last_player= & player->next;
+-    }
+ 
+ info_done:
+-    for ( t= n_teams; t;)  {
+-	t--;
+-	teams[t]->next= server->players;
+-	server->players= teams[t];
+-    }
+-    if ( teams) free( teams);
++	for (t = n_teams; t;)
++	{
++		t--;
++		teams[t]->next = server->players;
++		server->players = teams[t];
++	}
++	if (teams)
++	{
++		free(teams);
++	}
+ 
+-    cleanup_qserver( server, 1);
+-    return;
++	return DONE_FORCE;
+ }
+ 
+-static const char GrPacketHead[]={'\xc0','\xde','\xf1','\x11'};
+-static const char PacketStart='\x42';
+-static char Dat2Reply1_2_10[]={'\xf4','\x03','\x14','\x02','\x0a','\x41','\x02','\x0a','\x41','\x00','\x00','\x78','\x30','\x63'};
+-static char Dat2Reply1_3[]   ={'\xf4','\x03','\x14','\x03','\x05','\x41','\x03','\x05','\x41','\x00','\x00','\x78','\x30','\x63'};
+-static char Dat2Reply1_4[]   ={'\xf4','\x03','\x14','\x04','\x00','\x41','\x04','\x00','\x41','\x00','\x00','\x78','\x30','\x63'};
++static const char GrPacketHead[] =
++{
++	'\xc0', '\xde', '\xf1', '\x11'
++};
++static const char PacketStart = '\x42';
++static char Dat2Reply1_2_10[] =
++{
++	'\xf4', '\x03', '\x14', '\x02', '\x0a', '\x41', '\x02', '\x0a', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
++static char Dat2Reply1_3[] =
++{
++	'\xf4', '\x03', '\x14', '\x03', '\x05', '\x41', '\x03', '\x05', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
++static char Dat2Reply1_4[] =
++{
++	'\xf4', '\x03', '\x14', '\x04', '\x00', '\x41', '\x04', '\x00', '\x41', '\x00', '\x00', '\x78', '\x30', '\x63'
++};
+ //static char HDat2[]={'\xea','\x03','\x02','\x00','\x14'};
+ 
+ #define SHORT_GR_LEN	75
+@@ -8343,349 +9752,394 @@
+ #define VERSION_1_3	2
+ #define VERSION_1_4	3
+ 
+-void
+-deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen)
++query_status_t deal_with_ghostrecon_packet(struct qserver *server, char *pkt, int pktlen)
+ {
+-    char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer;
+-    char *lpszMission;
+-    unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer;
+-    unsigned short GrPayloadLen;
+-    int i;
+-    struct player *player;
+-    int iLen, iTemp;
++	char str[256], *start, *end, StartFlag, *lpszIgnoreServerPlayer;
++	char *lpszMission;
++	unsigned int iIgnoreServerPlayer, iDedicatedServer, iUseStartTimer;
++	unsigned short GrPayloadLen;
++	int i;
++	struct player *player;
++	int iLen, iTemp;
+ 	short sLen;
+ 	int iSecsPlayed;
+ 	long iSpawnType;
+-	int ServerVersion=UNKNOWN_VERSION;
++	int ServerVersion = UNKNOWN_VERSION;
+ 	float flStartTimerSetPoint;
+ 
++	debug( 2, "deal_with_ghostrecon_packet %p, %d", server, pktlen );
++
+ 	start = pkt;
+-	end=&pkt[pktlen];
+-    pkt[pktlen]= '\0';
++	end = &pkt[pktlen];
++	pkt[pktlen] = '\0';
+ 
+-/*
+-	This function walks a packet that is recieved from a ghost recon server - default from port 2348.  It does quite a few
+-	sanity checks along the way as the structure is not documented.  The packet is mostly binary in nature with many string
+-	fields being variable in length,  ie the length is listed foloowed by that many bytes.  There are two structure arrays
+-	that have an array size followed by structure size * number of elements (player name and player data).  This routine
++	/*
++	This function walks a packet that is recieved from a ghost recon server - default from port 2348. It does quite a few
++	sanity checks along the way as the structure is not documented. The packet is mostly binary in nature with many string
++	fields being variable in length, ie the length is listed foloowed by that many bytes. There are two structure arrays
++	that have an array size followed by structure size * number of elements (player name and player data). This routine
+ 	walks this packet and increments a pointer "pkt" to extract the info.
+-*/
++	 */
+ 
+ 
+-    if ( server->server_name == NULL)
+-	server->ping_total+= time_delta( &packet_recv_time,
+-		&server->packet_time1);
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	}
+ 
+ 	/* sanity check against packet */
+-	if (memcmp(pkt,GrPacketHead,sizeof(GrPacketHead))!=0)
++	if (memcmp(pkt, GrPacketHead, sizeof(GrPacketHead)) != 0)
+ 	{
+-		server->server_name= strdup( "Unknown Packet Header");
+-		cleanup_qserver( server, 1);
+-		return;
+-	};
++		server->server_name = strdup("Unknown Packet Header");
++		return PKT_ERROR;
++	}
+ 
+ 	pkt += sizeof(GrPacketHead);
+-	StartFlag=pkt[0];
++	StartFlag = pkt[0];
+ 	pkt += 1;
+ 	if (StartFlag != 0x42)
+ 	{
+-		server->server_name= strdup( "Unknown Start Flag");
+-		cleanup_qserver( server, 1);
+-		return;
+-	};
++		server->server_name = strdup("Unknown Start Flag");
++		return PKT_ERROR;
++	}
+ 
+ 	/* compare packet length recieved to included size - header info */
+ 	sLen = swap_short_from_little(pkt);
+ 	pkt += 2;
+-	GrPayloadLen = pktlen - sizeof(GrPacketHead) -3; // 3 = size slen + size start flag
++	GrPayloadLen = pktlen - sizeof(GrPacketHead) - 3;
++	// 3 = size slen + size start flag
+ 
+ 	if (sLen != GrPayloadLen)
+ 	{
+-		server->server_name= strdup( "Packet Size Mismatch");
+-		cleanup_qserver( server, 1);
+-		return;
+-	};
++		server->server_name = strdup("Packet Size Mismatch");
++		return PKT_ERROR;
++	}
+ 
+-/*
+-Will likely need to verify and add to this "if" construct with every patch / add-on.
+-*/
+-	if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0) ServerVersion=VERSION_1_2_10;
+-	else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0) ServerVersion=VERSION_1_3;
+-	else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0) ServerVersion=VERSION_1_4;
++	/*
++	Will likely need to verify and add to this "if" construct with every patch / add-on.
++	 */
++	if (memcmp(pkt, Dat2Reply1_2_10, sizeof(Dat2Reply1_2_10)) == 0)
++	{
++		ServerVersion = VERSION_1_2_10;
++	}
++	else if (memcmp(pkt, Dat2Reply1_3, sizeof(Dat2Reply1_3)) == 0)
++	{
++		ServerVersion = VERSION_1_3;
++	}
++	else if (memcmp(pkt, Dat2Reply1_4, sizeof(Dat2Reply1_4)) == 0)
++	{
++		ServerVersion = VERSION_1_4;
++	}
+ 
+ 	if (ServerVersion == UNKNOWN_VERSION)
+ 	{
+-		server->server_name= strdup( "Unknown GR Version");
+-		cleanup_qserver( server, 1);
+-		return;
+-    };
++		server->server_name = strdup("Unknown GR Version");
++		return PKT_ERROR;
++	}
+ 
+ 	switch (ServerVersion)
+ 	{
+ 	case VERSION_1_2_10:
+-		{
+-			strcpy(str,"1.2.10");
+-			pkt+=sizeof(Dat2Reply1_2_10);
+-			break;
+-		};
++		strcpy(str, "1.2.10");
++		pkt += sizeof(Dat2Reply1_2_10);
++		break;
++
+ 	case VERSION_1_3:
+-		{
+-			strcpy(str,"1.3");
+-			pkt+=sizeof(Dat2Reply1_3);
+-			break;
+-		};
++		strcpy(str, "1.3");
++		pkt += sizeof(Dat2Reply1_3);
++		break;
++
+ 	case VERSION_1_4:
+-		{
+-			strcpy(str,"1.4");
+-			pkt+=sizeof(Dat2Reply1_4);
+-			break;
+-		};
++		strcpy(str, "1.4");
++		pkt += sizeof(Dat2Reply1_4);
++		break;
+ 
+-	};
++	}
+ 	add_rule(server, "patch", str, NO_FLAGS);
+ 
+ 	/* have player packet */
+ 
+-	// Ghost recon has one of the player slots filled up with the server program itself.  By default we will
+-	// drop the first player listed.  This causes a bit of a mess here and below but makes for the best display
++	// Ghost recon has one of the player slots filled up with the server program itself. By default we will
++	// drop the first player listed. This causes a bit of a mess here and below but makes for the best display
+ 	// a user can specify -grs,ignoreserverplayer=no to override this behaviour.
+ 
+-	lpszIgnoreServerPlayer = get_param_value( server, "ignoreserverplayer", "yes");
+-	for (i=0; i<4; i++) str[i]=tolower(lpszIgnoreServerPlayer[i]);
+-	if (strcmp(str,"yes")==0)iIgnoreServerPlayer=1;
+-	else iIgnoreServerPlayer=0;
++	lpszIgnoreServerPlayer = get_param_value(server, "ignoreserverplayer", "yes");
++	for (i = 0; i < 4; i++)
++	{
++		str[i] = tolower(lpszIgnoreServerPlayer[i]);
++	}
++	if (strcmp(str, "yes") == 0)
++	{
++		iIgnoreServerPlayer = 1;
++	}
++	else
++	{
++		iIgnoreServerPlayer = 0;
++	}
+ 
+-	pkt+=4; /* unknown */
++	pkt += 4; /* unknown */
+ 
+ 
+-	// this is the first of many variable strings.  get the length,
++	// this is the first of many variable strings. get the length,
+ 	// increment pointer over length, check for sanity,
+ 	// get the string, increment the pointer over string (using length)
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN))
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+-		server->server_name= strdup( "Server Name too Long");
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	server->server_name = strndup( pkt, iLen);
++		server->server_name = strdup("Server Name too Long");
++		return PKT_ERROR;
++	}
++	server->server_name = strndup(pkt, iLen);
+ 	pkt += iLen;
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN))
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+ 		add_rule(server, "error", "Map Name too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	server->map_name = strndup( pkt, iLen);
++		return PKT_ERROR;
++	}
++	server->map_name = strndup(pkt, iLen);
+ 	pkt += iLen;
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN))
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "Mission Name too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	/* mission does not make sense unless a coop game type.  Since
+-	   we dont know that now, we will save the mission and set
+-	   the rule and free memory below when we know game type */
+-	lpszMission = strndup( pkt, iLen);
++		add_rule(server, "error", "Mission Name too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	/* mission does not make sense unless a coop game type. Since
++	we dont know that now, we will save the mission and set
++	the rule and free memory below when we know game type */
++	lpszMission = strndup(pkt, iLen);
+ 	pkt += iLen;
+ 
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN))
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "Mission Type too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	add_nrule( server, "missiontype", pkt, iLen);
++		add_rule(server, "error", "Mission Type too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	add_nrule(server, "missiontype", pkt, iLen);
+ 	pkt += iLen;
+ 
+-	if ( pkt[1])
++	if (pkt[1])
+ 	{
+-		add_rule( server, "password", "Yes", NO_FLAGS);
+-	} else
++		add_rule(server, "password", "Yes", NO_FLAGS);
++	}
++	else
+ 	{
+-		add_rule( server, "password", "No", NO_FLAGS);
+-	};
++		add_rule(server, "password", "No", NO_FLAGS);
++	}
+ 	pkt += 2;
+ 
+-	server->max_players= swap_long_from_little(pkt);
++	server->max_players = swap_long_from_little(pkt);
+ 	pkt += 4;
+ 	if (server->max_players > 36)
+ 	{
+-		add_rule(server, "error",  "Max players more then 36", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
++		add_rule(server, "error", "Max players more then 36", NO_FLAGS);
++		return PKT_ERROR;
++	}
+ 
+-	server->num_players= swap_long_from_little(pkt);
++	server->num_players = swap_long_from_little(pkt);
+ 	pkt += 4;
+ 	if (server->num_players > server->max_players)
+ 	{
+ 		add_rule(server, "error", "More then MAX Players", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
++		return PKT_ERROR;
++	}
+ 
+-	if (iIgnoreServerPlayer) // skip past first player
++	if (iIgnoreServerPlayer)
++	// skip past first player
+ 	{
+ 		server->num_players--;
+ 		server->max_players--;
+ 		iLen = swap_long_from_little(pkt);
+ 		pkt += 4;
+ 		pkt += iLen;
+-	};
++	}
+ 
+-	for (i=0;i<server->num_players;i++) // read each player name
++	for (i = 0; i < server->num_players; i++)
++	// read each player name
+ 	{
+ 		iLen = swap_long_from_little(pkt);
+ 		pkt += 4;
+ 
+-		player= (struct player*) calloc( 1, sizeof(struct player));
++		player = (struct player*)calloc(1, sizeof(struct player));
+ 
+-		if ((iLen<1) || (iLen >SHORT_GR_LEN))
++		if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 		{
+ 			add_rule(server, "error", "Player Name too Long", NO_FLAGS);
+-			cleanup_qserver(server, 1);
+-			return;
+-		};
+-		player->name= strndup( pkt, iLen);
+-		pkt += iLen;  /* player name */
+-		player->team= i; // tag so we can find this record when we have player dat.
+-		player->team_name= "Unassigned";
++			return PKT_ERROR;
++		}
++		player->name = strndup(pkt, iLen);
++		pkt += iLen; /* player name */
++		player->team = i; // tag so we can find this record when we have player dat.
++		player->team_name = "Unassigned";
+ 		player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-		player->frags=0;
++		player->frags = 0;
+ 
+-		player->next= server->players;
+-		server->players= player;
+-	};
++		player->next = server->players;
++		server->players = player;
++	}
+ 
+ 	pkt += 17;
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN))
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "Version too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	strncpy(str,pkt,iLen);
+-	add_rule( server, "version", str, NO_FLAGS);
+-	pkt += iLen;  /* version */
++		add_rule(server, "error", "Version too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	strncpy(str, pkt, iLen);
++	add_rule(server, "version", str, NO_FLAGS);
++	pkt += iLen; /* version */
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >LONG_GR_LEN))
++	if ((iLen < 1) || (iLen > LONG_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "Mods too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	server->game= strndup( pkt, iLen);
++		add_rule(server, "error", "Mods too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	server->game = strndup(pkt, iLen);
+ 
+-	for (i=0;i<(int)strlen(server->game)-5;i++)  // clean the "/mods/" part from every entry
++	for (i = 0; i < (int)strlen(server->game) - 5; i++)
++	// clean the "/mods/" part from every entry
+ 	{
+-		if (memcmp(&server->game[i],"\\mods\\",6)==0)
++		if (memcmp(&server->game[i], "\\mods\\", 6) == 0)
+ 		{
+-			server->game[i]=' ';
+-			strcpy(&server->game[i+1],&server->game[i+6]);
+-		};
+-	};
+-	add_rule(server, "game",  server->game, NO_FLAGS);
++			server->game[i] = ' ';
++			strcpy(&server->game[i + 1], &server->game[i + 6]);
++		}
++	}
++	add_rule(server, "game", server->game, NO_FLAGS);
+ 
+-	pkt += iLen;  /* mods */
++	pkt += iLen; /* mods */
+ 
+-	iDedicatedServer=pkt[0];
+-	if ( iDedicatedServer)
+-		add_rule( server, "dedicated", "Yes", NO_FLAGS);
++	iDedicatedServer = pkt[0];
++	if (iDedicatedServer)
++	{
++		add_rule(server, "dedicated", "Yes", NO_FLAGS);
++	}
+ 	else
+-		add_rule( server, "dedicated", "No", NO_FLAGS);
++	{
++		add_rule(server, "dedicated", "No", NO_FLAGS);
++	}
+ 
+-	pkt += 1;  /* unknown */
++	pkt += 1; /* unknown */
+ 
+ 	iSecsPlayed = swap_float_from_little(pkt);
+ 
+-	add_rule(server, "timeplayed", play_time(iSecsPlayed,2), NO_FLAGS);
++	add_rule(server, "timeplayed", play_time(iSecsPlayed, 2), NO_FLAGS);
+ 
+-	pkt += 4;  /* time played */
++	pkt += 4; /* time played */
+ 
+-	switch (pkt[0]) {
+-	case 3 : strcpy(str,"Joining"); break;
+-	case 4 : strcpy(str,"Playing"); break;
+-	case 5 : strcpy(str,"Debrief"); break;
+-	default : strcpy(str,"Undefined");
+-	};
+-	add_rule( server, "status", str, NO_FLAGS);
++	switch (pkt[0])
++	{
++	case 3:
++		strcpy(str, "Joining");
++		break;
++	case 4:
++		strcpy(str, "Playing");
++		break;
++	case 5:
++		strcpy(str, "Debrief");
++		break;
++	default:
++		strcpy(str, "Undefined");
++	}
++	add_rule(server, "status", str, NO_FLAGS);
+ 
+ 	pkt += 1;
+ 
+-	pkt += 3;  /* unknown */
++	pkt += 3; /* unknown */
+ 
+ 
+-	switch (pkt[0]) {
+-	case 2: strcpy(str,"COOP"); break;
+-	case 3: strcpy(str,"SOLO"); break;
+-	case 4: strcpy(str,"TEAM"); break;
+-	default: sprintf(str,"UNKOWN %u",pkt[0]); break;
+-	};
++	switch (pkt[0])
++	{
++		case 2:
++			strcpy(str, "COOP");
++			break;
++		case 3:
++			strcpy(str, "SOLO");
++			break;
++		case 4:
++			strcpy(str, "TEAM");
++			break;
++		default:
++			sprintf(str, "UNKOWN %u", pkt[0]);
++			break;
++	}
+ 
+-	add_rule( server, "gamemode", str, NO_FLAGS);
++	add_rule(server, "gamemode", str, NO_FLAGS);
+ 
+-	if (pkt[0]==2)
+-		add_rule( server, "mission", lpszMission, NO_FLAGS);
++	if (pkt[0] == 2)
++	{
++		add_rule(server, "mission", lpszMission, NO_FLAGS);
++	}
+ 	else
+-		add_rule( server, "mission", "No Mission", NO_FLAGS);
++	{
++		add_rule(server, "mission", "No Mission", NO_FLAGS);
++	}
+ 
+ 	free(lpszMission);
+ 
+-	pkt += 1;  /* Game Mode */
++	pkt += 1; /* Game Mode */
+ 
+-	pkt += 3;  /* unknown */
++	pkt += 3; /* unknown */
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >LONG_GR_LEN))
++	if ((iLen < 1) || (iLen > LONG_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "MOTD too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	strncpy(str,pkt,sizeof(str));
+-	str[sizeof(str)-1]=0;
++		add_rule(server, "error", "MOTD too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	strncpy(str, pkt, sizeof(str));
++	str[sizeof(str) - 1] = 0;
+ 	add_rule(server, "motd", str, NO_FLAGS);
+-	pkt += iLen;  /* MOTD */
++	pkt += iLen; /* MOTD */
+ 
+ 	iSpawnType = swap_long_from_little(pkt);
+ 
+-	switch (iSpawnType) {
+-	case 0: strcpy(str,"None"); break;
+-	case 1: strcpy(str,"Individual"); break;
+-	case 2: strcpy(str,"Team"); break;
+-	case 3: strcpy(str,"Infinite"); break;
+-	default:strcpy(str,"Unknown");
+-	};
++	switch (iSpawnType)
++	{
++	case 0:
++		strcpy(str, "None");
++		break;
++	case 1:
++		strcpy(str, "Individual");
++		break;
++	case 2:
++		strcpy(str, "Team");
++		break;
++	case 3:
++		strcpy(str, "Infinite");
++		break;
++	default:
++		strcpy(str, "Unknown");
++	}
+ 
+-	add_rule( server, "spawntype", str, NO_FLAGS);
+-	pkt += 4;  /* spawn type */
++	add_rule(server, "spawntype", str, NO_FLAGS);
++	pkt += 4; /* spawn type */
+ 
+ 	iTemp = swap_float_from_little(pkt);
+-	add_rule(server, "gametime", play_time(iTemp,2), NO_FLAGS);
++	add_rule(server, "gametime", play_time(iTemp, 2), NO_FLAGS);
+ 
+-	iTemp = iTemp-iSecsPlayed;
++	iTemp = iTemp - iSecsPlayed;
+ 
+-	if (iTemp <= 0) iTemp=0;
+-	add_rule(server, "remainingtime", play_time(iTemp,2), NO_FLAGS);
++	if (iTemp <= 0)
++	{
++		iTemp = 0;
++	}
++	add_rule(server, "remainingtime", play_time(iTemp, 2), NO_FLAGS);
+ 	pkt += 4; /* Game time */
+ 
+ 
+@@ -8693,238 +10147,281 @@
+ 	if (iIgnoreServerPlayer)
+ 	{
+ 		iTemp--;
+-	};
++	}
+ 	if (iTemp != server->num_players)
+ 	{
+-		add_rule(server, "error",  "Number of Players Mismatch", NO_FLAGS);
+-	};
++		add_rule(server, "error", "Number of Players Mismatch", NO_FLAGS);
++	}
+ 
+ 
+ 	pkt += 4; /* player count 2 */
+ 
+ 	if (iIgnoreServerPlayer)
+ 	{
+-		pkt+=5;  // skip first player data
+-	};
++		pkt += 5; // skip first player data
++	}
+ 
+-	for (i=0;i<server->num_players;i++) // for each player get binary data
++	for (i = 0; i < server->num_players; i++)
++	// for each player get binary data
+ 	{
+-		player=server->players;			// first we must find the player - lets look for the tag
+-		while (player && (player->team != i)) player=player->next; /* get to player - linked list is in reverse order */
++		player = server->players;
++		// first we must find the player - lets look for the tag
++		while (player && (player->team != i))
++		{
++			player = player->next;
++		}
++		/* get to player - linked list is in reverse order */
+ 
+ 		if (player)
+ 		{
+-			player->team= pkt[2];
++			player->team = pkt[2];
+ 			switch (player->team)
+ 			{
+-			case 1: player->team_name= "Red"; break;
+-			case 2: player->team_name= "Blue"; break;
+-			case 3: player->team_name= "Yellow"; break;
+-			case 4: player->team_name= "Green"; break;
+-			case 5: player->team_name= "Unassigned"; break;
+-			default: player->team_name= "Not Known"; break;
+-			};
++			case 1:
++				player->team_name = "Red";
++				break;
++			case 2:
++				player->team_name = "Blue";
++				break;
++			case 3:
++				player->team_name = "Yellow";
++				break;
++			case 4:
++				player->team_name = "Green";
++				break;
++			case 5:
++				player->team_name = "Unassigned";
++				break;
++			default:
++				player->team_name = "Not Known";
++				break;
++			}
+ 			player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-			player->deaths=pkt[1];
+-		};
++			player->deaths = pkt[1];
++		}
+ 		pkt += 5; /* player data*/
+-	};
++	}
+ 
+-	for (i=0;i<5;i++)
++	for (i = 0; i < 5; i++)
+ 	{
+ 		pkt += 8; /* team data who knows what they have in here */
+-	};
++	}
+ 
+-	pkt +=1;
+-	iUseStartTimer=pkt[0]; // UseStartTimer
++	pkt += 1;
++	iUseStartTimer = pkt[0]; // UseStartTimer
+ 
+-	pkt +=1;
++	pkt += 1;
+ 
+-	iTemp = flStartTimerSetPoint = swap_float_from_little(pkt);// Start Timer Set Point
++	iTemp = flStartTimerSetPoint = swap_float_from_little(pkt);
++	// Start Timer Set Point
+ 	pkt += 4;
+ 
+ 	if (iUseStartTimer)
+ 	{
+-		add_rule( server, "usestarttime", "Yes", NO_FLAGS);
+-		add_rule( server, "starttimeset", play_time(iTemp,2), NO_FLAGS);
+-	} else
++		add_rule(server, "usestarttime", "Yes", NO_FLAGS);
++		add_rule(server, "starttimeset", play_time(iTemp, 2), NO_FLAGS);
++	}
++	else
+ 	{
+-		add_rule( server, "usestarttime", "No", NO_FLAGS);
+-		add_rule( server, "starttimeset", play_time(0,2), NO_FLAGS);
+-	};
++		add_rule(server, "usestarttime", "No", NO_FLAGS);
++		add_rule(server, "starttimeset", play_time(0, 2), NO_FLAGS);
++	}
+ 
+-	if ((ServerVersion == VERSION_1_3) ||  // stuff added in patch 1.3
+-		(ServerVersion == VERSION_1_4))
++	if ((ServerVersion == VERSION_1_3) || // stuff added in patch 1.3
++	(ServerVersion == VERSION_1_4))
+ 	{
+-		iTemp = swap_float_from_little(pkt);// Debrief Time
+-		add_rule( server, "debrieftime", play_time(iTemp,2), NO_FLAGS);
++		iTemp = swap_float_from_little(pkt); // Debrief Time
++		add_rule(server, "debrieftime", play_time(iTemp, 2), NO_FLAGS);
+ 		pkt += 4;
+ 
+-		iTemp = swap_float_from_little(pkt);// Respawn Min
+-		add_rule( server, "respawnmin", play_time(iTemp,2), NO_FLAGS);
++		iTemp = swap_float_from_little(pkt); // Respawn Min
++		add_rule(server, "respawnmin", play_time(iTemp, 2), NO_FLAGS);
+ 		pkt += 4;
+ 
+-		iTemp = swap_float_from_little(pkt);// Respawn Max
+-		add_rule( server, "respawnmax", play_time(iTemp,2), NO_FLAGS);
++		iTemp = swap_float_from_little(pkt); // Respawn Max
++		add_rule(server, "respawnmax", play_time(iTemp, 2), NO_FLAGS);
+ 		pkt += 4;
+ 
+-		iTemp = swap_float_from_little(pkt);// Respawn Invulnerable
+-		add_rule( server, "respawnsafe", play_time(iTemp,2), NO_FLAGS);
++		iTemp = swap_float_from_little(pkt); // Respawn Invulnerable
++		add_rule(server, "respawnsafe", play_time(iTemp, 2), NO_FLAGS);
+ 		pkt += 4;
+-	} else
++	}
++	else
+ 	{
+-		add_rule( server, "debrieftime", "Undefined", NO_FLAGS);
+-		add_rule( server, "respawnmin", "Undefined", NO_FLAGS);
+-		add_rule( server, "respawnmax", "Undefined", NO_FLAGS);
+-		add_rule( server, "respawnsafe", "Undefined", NO_FLAGS);
++		add_rule(server, "debrieftime", "Undefined", NO_FLAGS);
++		add_rule(server, "respawnmin", "Undefined", NO_FLAGS);
++		add_rule(server, "respawnmax", "Undefined", NO_FLAGS);
++		add_rule(server, "respawnsafe", "Undefined", NO_FLAGS);
+ 
+-	};
++	}
+ 
+ 
+-	pkt+=4; // 4
+-	iTemp=pkt[0]; // Spawn Count
++	pkt += 4; // 4
++	iTemp = pkt[0]; // Spawn Count
+ 
+-	if ((iSpawnType == 1) || (iSpawnType == 2))   /* Individual or team */
+-		sprintf( str, "%u", iTemp);
+-	else /* else not used */
+-		sprintf( str, "%u", 0);
+-	add_rule( server, "spawncount", str, NO_FLAGS);
++	if ((iSpawnType == 1) || (iSpawnType == 2))
++	/* Individual or team */
++	{
++		sprintf(str, "%u", iTemp);
++	}
++	else
++	/* else not used */
++	{
++		sprintf(str, "%u", 0);
++	}
++	add_rule(server, "spawncount", str, NO_FLAGS);
+ 	pkt += 1; // 5
+ 
+-	pkt +=4; // 9
++	pkt += 4; // 9
+ 
+-	iTemp=pkt[0];  // Allow Observers
++	iTemp = pkt[0]; // Allow Observers
+ 	if (iTemp)
+-		strcpy( str, "Yes");
+-	else /* else not used */
+-		strcpy( str, "No");
+-	add_rule( server, "allowobservers", str, NO_FLAGS);
+-	pkt +=1; // 10
++	{
++		strcpy(str, "Yes");
++	}
++	else
++	/* else not used */
++	{
++		strcpy(str, "No");
++	}
++	add_rule(server, "allowobservers", str, NO_FLAGS);
++	pkt += 1; // 10
+ 
+-	pkt +=3; // 13
++	pkt += 3; // 13
+ 
+ 	//	pkt += 13;
+ 
+ 	if (iUseStartTimer)
+ 	{
+-		iTemp=swap_float_from_little(pkt);  // Start Timer Count
+-		add_rule( server, "startwait", play_time(iTemp,2), NO_FLAGS);
++		iTemp = swap_float_from_little(pkt); // Start Timer Count
++		add_rule(server, "startwait", play_time(iTemp, 2), NO_FLAGS);
+ 	}
+ 	else
+ 	{
+-		add_rule( server, "startwait", play_time(0,2), NO_FLAGS);
+-	};
+-	pkt += 4;  //17
++		add_rule(server, "startwait", play_time(0, 2), NO_FLAGS);
++	}
++	pkt += 4; //17
+ 
+-	iTemp = pkt[0]; //  IFF
++	iTemp = pkt[0]; // IFF
+ 	switch (iTemp)
+ 	{
+-	case 0 : strcpy(str,"None"); break;
+-	case 1 : strcpy(str,"Reticule"); break;
+-	case 2 : strcpy(str,"Names"); break;
+-	default : strcpy(str,"Unknown"); break;
+-	};
+-	add_rule( server, "iff", str, NO_FLAGS);
++		case 0:
++			strcpy(str, "None");
++			break;
++		case 1:
++			strcpy(str, "Reticule");
++			break;
++		case 2:
++			strcpy(str, "Names");
++			break;
++		default:
++			strcpy(str, "Unknown");
++			break;
++	}
++	add_rule(server, "iff", str, NO_FLAGS);
+ 	pkt += 1; // 18
+ 
+-	iTemp = pkt [0]; // Threat Indicator
+-	if (iTemp) add_rule(server, "ti", "ON ", NO_FLAGS);
+-	else add_rule(server, "ti", "OFF", NO_FLAGS);
++	iTemp = pkt[0]; // Threat Indicator
++	if (iTemp)
++	{
++		add_rule(server, "ti", "ON ", NO_FLAGS);
++	}
++	else
++	{
++		add_rule(server, "ti", "OFF", NO_FLAGS);
++	}
+ 
+-	pkt += 1;  // 19
++	pkt += 1; // 19
+ 
+-	pkt += 5;  // 24
++	pkt += 5; // 24
+ 
+ 
+ 	iLen = swap_long_from_little(pkt);
+ 	pkt += 4;
+-	if ((iLen<1) || (iLen >SHORT_GR_LEN) )
++	if ((iLen < 1) || (iLen > SHORT_GR_LEN))
+ 	{
+-		add_rule(server, "error",  "Restrictions too Long", NO_FLAGS);
+-		cleanup_qserver(server, 1);
+-		return;
+-	};
+-	add_rule( server, "restrict", pkt, NO_FLAGS);
+-	pkt += iLen;  /* restrictions */
++		add_rule(server, "error", "Restrictions too Long", NO_FLAGS);
++		return PKT_ERROR;
++	}
++	add_rule(server, "restrict", pkt, NO_FLAGS);
++	pkt += iLen; /* restrictions */
+ 
+ 	pkt += 23;
+ 
+ 	/*
+ 	if ( ghostrecon_debug) print_packet( pkt, GrPayloadLen);
+-	*/
++	 */
+ 
+-	cleanup_qserver( server, 1);
+-	return;
++	return DONE_FORCE;
+ }
+ 
+-char*
+-find_ravenshield_game( char *gameno )
++char *find_ravenshield_game(char *gameno)
+ {
+-	switch( atoi( gameno ) )
++	switch (atoi(gameno))
+ 	{
+-	case 8:
+-		return strdup( "Team Deathmatch" );
+-		break;
+-	case 13:
+-		return strdup( "Deathmatch" );
+-		break;
+-	case 14:
+-		return strdup( "Team Deathmatch" );
+-		break;
+-	case 15:
+-		return strdup( "Bomb" );
+-		break;
+-	case 16:
+-		return strdup( "Escort Pilot" );
+-		break;
+-	default:
+-		// 1.50 and above actually uses a string so
+-		// return that
+-		return strdup( gameno );
+-		break;
++		case 8:
++			return strdup("Team Deathmatch");
++			break;
++		case 13:
++			return strdup("Deathmatch");
++			break;
++		case 14:
++			return strdup("Team Deathmatch");
++			break;
++		case 15:
++			return strdup("Bomb");
++			break;
++		case 16:
++			return strdup("Escort Pilot");
++			break;
++		default:
++			// 1.50 and above actually uses a string so
++			// return that
++			return strdup(gameno);
++			break;
+ 	}
+ }
+ 
+-char*
+-find_savage_game( char *gametype )
++char *find_savage_game(char *gametype)
+ {
+-	if ( 0 == strcmp( "RTSS", gametype ) )
++	if (0 == strcmp("RTSS", gametype))
+ 	{
+-		return strdup( "RTSS" );
++		return strdup("RTSS");
+ 	}
+ 	else
+ 	{
+-		return strdup( "Unknown" );
++		return strdup("Unknown");
+ 	}
+ }
+ 
+-void
+-deal_with_ravenshield_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *s, *key, *value;
++	char *s, *key, *value;
++
++	debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen );
+ 
+ 	server->n_servers++;
+-    if ( NULL == server->server_name )
++	if (NULL == server->server_name)
+ 	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 	}
+-    else
++	else
+ 	{
+-		gettimeofday( &server->packet_time1, NULL );
++		gettimeofday(&server->packet_time1, NULL);
+ 	}
+ 
+-    rawpkt[pktlen]= '\0';
++	rawpkt[pktlen] = '\0';
+ 
+-    s = rawpkt;
+-    while ( *s )
++	s = rawpkt;
++	while (*s)
+ 	{
+ 		// Find the seperator
+-		while ( *s && *s != '\xB6' )
++		while (*s && *s != '\xB6')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( !*s )
++		if (! *s)
+ 		{
+ 			// Hit the end no more
+ 			break;
+@@ -8932,11 +10429,11 @@
+ 
+ 		// key start
+ 		key = ++s;
+-		while ( *s && *s != ' ' )
++		while (*s && *s != ' ')
+ 		{
+ 			s++;
+ 		}
+-		if ( *s != ' ')
++		if (*s != ' ')
+ 		{
+ 			// malformed
+ 			break;
+@@ -8946,189 +10443,189 @@
+ 		// value start
+ 		value = s;
+ 
+-		while ( *s && *s != '\xB6' )
++		while (*s && *s != '\xB6')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( *s == '\xB6' )
++		if (*s == '\xB6')
+ 		{
+-			*(s-1) = '\0';
++			*(s - 1) = '\0';
+ 		}
+ 
+ 		// Decode current key par
+-		if ( 0 == strcmp( "A1", key ) )
++		if (0 == strcmp("A1", key))
+ 		{
+ 			// Max players
+-			server->max_players = atoi( value );
++			server->max_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "A2", key ) )
++		else if (0 == strcmp("A2", key))
+ 		{
+ 			// TeamKillerPenalty
+-			add_rule( server, "TeamKillerPenalty", value, NO_FLAGS );
++			add_rule(server, "TeamKillerPenalty", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "B1", key ) )
++		else if (0 == strcmp("B1", key))
+ 		{
+ 			// Current players
+-			server->num_players = atoi( value );
++			server->num_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "B2", key ) )
++		else if (0 == strcmp("B2", key))
+ 		{
+ 			// AllowRadar
+-			add_rule( server, "AllowRadar", value, NO_FLAGS );
++			add_rule(server, "AllowRadar", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "D2", key ) )
++		else if (0 == strcmp("D2", key))
+ 		{
+ 			// Version info
+-			add_rule( server, "Version", value, NO_FLAGS );
++			add_rule(server, "Version", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "E1", key ) )
++		else if (0 == strcmp("E1", key))
+ 		{
+ 			// Current map
+-			server->map_name = strdup( value );
++			server->map_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "E2", key ) )
++		else if (0 == strcmp("E2", key))
+ 		{
+ 			// Unknown
+ 		}
+-		else if ( 0 == strcmp( "F1", key ) )
++		else if (0 == strcmp("F1", key))
+ 		{
+ 			// Game type
+-			server->game = find_ravenshield_game( value );
+-			add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++			server->game = find_ravenshield_game(value);
++			add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "F2", key ) )
++		else if (0 == strcmp("F2", key))
+ 		{
+ 			// Unknown
+ 		}
+-		else if ( 0 == strcmp( "G1", key ) )
++		else if (0 == strcmp("G1", key))
+ 		{
+ 			// Password
+-			add_rule( server, "Password", value, NO_FLAGS );
++			add_rule(server, "Password", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "G2", key ) )
++		else if (0 == strcmp("G2", key))
+ 		{
+ 			// Query port
+ 		}
+-		else if ( 0 == strcmp( "H1", key ) )
++		else if (0 == strcmp("H1", key))
+ 		{
+ 			// Unknown
+ 		}
+-		else if ( 0 == strcmp( "H2", key ) )
++		else if (0 == strcmp("H2", key))
+ 		{
+ 			// Number of Terrorists
+-			add_rule( server, "nbTerro", value, NO_FLAGS );
++			add_rule(server, "nbTerro", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "I1", key ) )
++		else if (0 == strcmp("I1", key))
+ 		{
+ 			// Server name
+-			server->server_name = strdup( value );
++			server->server_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "I2", key ) )
++		else if (0 == strcmp("I2", key))
+ 		{
+ 			// Unknown
+ 		}
+-		else if ( 0 == strcmp( "J1", key ) )
++		else if (0 == strcmp("J1", key))
+ 		{
+ 			// Game Type Order
+ 			// Not pretty ignore for now
+ 			//add_rule( server, "Game Type Order", value, NO_FLAGS );
+ 		}
+-		else if ( 0 == strcmp( "J2", key ) )
++		else if (0 == strcmp("J2", key))
+ 		{
+ 			// RotateMap
+-			add_rule( server, "RotateMap", value, NO_FLAGS);
++			add_rule(server, "RotateMap", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "K1", key ) )
++		else if (0 == strcmp("K1", key))
+ 		{
+ 			// Map Cycle
+ 			// Not pretty ignore for now
+ 			//add_rule( server, "Map Cycle", value, NO_FLAGS );
+ 		}
+-		else if ( 0 == strcmp( "K2", key ) )
++		else if (0 == strcmp("K2", key))
+ 		{
+ 			// Force First Person Weapon
+-			add_rule( server, "ForceFPersonWeapon", value, NO_FLAGS);
++			add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "L1", key ) )
++		else if (0 == strcmp("L1", key))
+ 		{
+ 			// Players names
+ 			int player_number = 0;
+ 			char *n = value;
+ 
+-			if ( *n == '/' )
++			if (*n == '/')
+ 			{
+ 				// atleast 1 player
+ 				n++;
+-				while( *n && *n != '\xB6' )
++				while (*n && *n != '\xB6')
+ 				{
+ 					char *player_name = n;
+-					while ( *n && *n != '/' && *n != '\xB6' )
++					while (*n && *n != '/' && *n != '\xB6')
+ 					{
+ 						n++;
+ 					}
+ 
+-					if ( *n == '/' )
++					if (*n == '/')
+ 					{
+ 						*n++ = '\0';
+ 					}
+-					else if ( *n == '\xB6' )
++					else if (*n == '\xB6')
+ 					{
+-						*(n-1) = '\0';
++						*(n - 1) = '\0';
+ 					}
+ 
+-					if ( 0 != strlen( player_name ) )
++					if (0 != strlen(player_name))
+ 					{
+-						struct player *player = add_player( server, player_number );
+-						if ( NULL != player )
++						struct player *player = add_player(server, player_number);
++						if (NULL != player)
+ 						{
+-							player->name = strdup( player_name );
++							player->name = strdup(player_name);
+ 						}
+ 						player_number++;
+ 					}
+ 				}
+ 			}
+ 		}
+-		else if ( 0 == strcmp( "L3", key ) )
++		else if (0 == strcmp("L3", key))
+ 		{
+ 			// PunkBuster state
+-			add_rule( server, "PunkBuster", value, NO_FLAGS );
++			add_rule(server, "PunkBuster", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "M1", key ) )
++		else if (0 == strcmp("M1", key))
+ 		{
+ 			// Players times
+ 			int player_number = 0;
+ 			char *n = value;
+-			if ( *n == '/' )
++			if (*n == '/')
+ 			{
+ 				// atleast 1 player
+ 				n++;
+-				while( *n && *n != '\xB6' )
++				while (*n && *n != '\xB6')
+ 				{
+ 					char *time = n;
+-					while ( *n && *n != '/' && *n != '\xB6' )
++					while (*n && *n != '/' && *n != '\xB6')
+ 					{
+ 						n++;
+ 					}
+ 
+-					if ( *n == '/' )
++					if (*n == '/')
+ 					{
+ 						*n++ = '\0';
+ 					}
+-					else if ( *n == '\xB6' )
++					else if (*n == '\xB6')
+ 					{
+-						*(n-1) = '\0';
++						*(n - 1) = '\0';
+ 					}
+ 
+-					if ( 0 != strlen( time ) )
++					if (0 != strlen(time))
+ 					{
+ 						int mins, seconds;
+-						if ( 2 == sscanf( time, "%d:%d", &mins, &seconds ) )
++						if (2 == sscanf(time, "%d:%d", &mins, &seconds))
+ 						{
+-							struct player *player = get_player_by_number( server, player_number );
+-							if ( NULL != player )
++							struct player *player = get_player_by_number(server, player_number);
++							if (NULL != player)
+ 							{
+-								player->connect_time = mins * 60 + seconds;
++								player->connect_time = mins * 60+seconds;
+ 							}
+ 						}
+ 						player_number++;
+@@ -9136,164 +10633,162 @@
+ 				}
+ 			}
+ 		}
+-		else if ( 0 == strcmp( "N1", key ) )
++		else if (0 == strcmp("N1", key))
+ 		{
+ 			// Players ping
+ 			int player_number = 0;
+ 			char *n = value;
+-			if ( *n == '/' )
++			if (*n == '/')
+ 			{
+ 				// atleast 1 player
+ 				n++;
+-				while( *n && *n != '\xB6' )
++				while (*n && *n != '\xB6')
+ 				{
+ 					char *ping = n;
+-					while ( *n && *n != '/' && *n != '\xB6' )
++					while (*n && *n != '/' && *n != '\xB6')
+ 					{
+ 						n++;
+ 					}
+ 
+-					if ( *n == '/' )
++					if (*n == '/')
+ 					{
+ 						*n++ = '\0';
+ 					}
+-					else if ( *n == '\xB6' )
++					else if (*n == '\xB6')
+ 					{
+-						*(n-1) = '\0';
++						*(n - 1) = '\0';
+ 					}
+ 
+-					if ( 0 != strlen( ping ) )
++					if (0 != strlen(ping))
+ 					{
+-						struct player *player = get_player_by_number( server, player_number );
+-						if ( NULL != player )
++						struct player *player = get_player_by_number(server, player_number);
++						if (NULL != player)
+ 						{
+-							player->ping = atoi( ping );
+-						}
+-						player_number++;
++							player->ping = atoi(ping);
++						} player_number++;
+ 					}
+ 				}
+ 			}
+ 		}
+-		else if ( 0 == strcmp( "O1", key ) )
++		else if (0 == strcmp("O1", key))
+ 		{
+ 			// Players fags
+ 			int player_number = 0;
+ 			char *n = value;
+-			if ( *n == '/' )
++			if (*n == '/')
+ 			{
+ 				// atleast 1 player
+ 				n++;
+-				while( *n && *n != '\xB6' )
++				while (*n && *n != '\xB6')
+ 				{
+ 					char *frags = n;
+-					while ( *n && *n != '/' && *n != '\xB6' )
++					while (*n && *n != '/' && *n != '\xB6')
+ 					{
+ 						n++;
+ 					}
+ 
+-					if ( *n == '/' )
++					if (*n == '/')
+ 					{
+ 						*n++ = '\0';
+ 					}
+-					else if ( *n == '\xB6' )
++					else if (*n == '\xB6')
+ 					{
+-						*(n-1) = '\0';
++						*(n - 1) = '\0';
+ 					}
+ 
+-					if ( 0 != strlen( frags ) )
++					if (0 != strlen(frags))
+ 					{
+-						struct player *player = get_player_by_number( server, player_number );
+-						if ( NULL != player )
++						struct player *player = get_player_by_number(server, player_number);
++						if (NULL != player)
+ 						{
+-							player->frags = atoi( frags );
+-						}
+-						player_number++;
++							player->frags = atoi(frags);
++						} player_number++;
+ 					}
+ 				}
+ 			}
+ 		}
+-		else if ( 0 == strcmp( "P1", key ) )
++		else if (0 == strcmp("P1", key))
+ 		{
+ 			// Game port
+ 			// Not pretty ignore for now
+ 			/*
+ 			change_server_port( server, atoi( value ), 0 );
+-			*/
++			 */
+ 		}
+-		else if ( 0 == strcmp( "Q1", key ) )
++		else if (0 == strcmp("Q1", key))
+ 		{
+ 			// RoundsPerMatch
+-			add_rule( server, "RoundsPerMatch", value, NO_FLAGS );
++			add_rule(server, "RoundsPerMatch", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "R1", key ) )
++		else if (0 == strcmp("R1", key))
+ 		{
+ 			// RoundTime
+-			add_rule( server, "RoundTime", value, NO_FLAGS );
++			add_rule(server, "RoundTime", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "S1", key ) )
++		else if (0 == strcmp("S1", key))
+ 		{
+ 			// BetweenRoundTime
+-			add_rule( server, "BetweenRoundTime", value, NO_FLAGS );
++			add_rule(server, "BetweenRoundTime", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "T1", key ) )
++		else if (0 == strcmp("T1", key))
+ 		{
+ 			// BombTime
+-			add_rule( server, "BombTime", value, NO_FLAGS );
++			add_rule(server, "BombTime", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "W1", key ) )
++		else if (0 == strcmp("W1", key))
+ 		{
+ 			// ShowNames
+-			add_rule( server, "ShowNames", value, NO_FLAGS );
++			add_rule(server, "ShowNames", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "X1", key ) )
++		else if (0 == strcmp("X1", key))
+ 		{
+ 			// InternetServer
+-			add_rule( server, "InternetServer", value, NO_FLAGS );
++			add_rule(server, "InternetServer", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "Y1", key ) )
++		else if (0 == strcmp("Y1", key))
+ 		{
+ 			// FriendlyFire
+-			add_rule( server, "FriendlyFire", value, NO_FLAGS );
++			add_rule(server, "FriendlyFire", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "Z1", key ) )
++		else if (0 == strcmp("Z1", key))
+ 		{
+ 			// Autobalance
+-			add_rule( server, "Autobalance", value, NO_FLAGS );
++			add_rule(server, "Autobalance", value, NO_FLAGS);
+ 		}
+ 	}
+ 
+-	cleanup_qserver( server, 1 );
+-	return;
++	return DONE_FORCE;
+ }
+ 
+-void
+-deal_with_savage_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *s, *key, *value, *end;
++	char *s, *key, *value, *end;
++
++	debug( 2, "deal_with_savage_packet %p, %d", server, pktlen );
+ 
+ 	server->n_servers++;
+-    if ( NULL == server->server_name )
++	if (NULL == server->server_name)
+ 	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 	}
+-    else
++	else
+ 	{
+-		gettimeofday( &server->packet_time1, NULL );
++		gettimeofday(&server->packet_time1, NULL);
+ 	}
+ 
+-    rawpkt[pktlen]= '\0';
++	rawpkt[pktlen] = '\0';
+ 
+-    end = s = rawpkt;
++	end = s = rawpkt;
+ 	end += pktlen;
+-    while ( *s )
++	while (*s)
+ 	{
+ 		// Find the seperator
+-		while ( s <= end && *s != '\xFF' )
++		while (s <= end && *s != '\xFF')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( s >= end )
++		if (s >= end)
+ 		{
+ 			// Hit the end no more
+ 			break;
+@@ -9301,11 +10796,11 @@
+ 
+ 		// key start
+ 		key = ++s;
+-		while ( s < end && *s != '\xFE' )
++		while (s < end && *s != '\xFE')
+ 		{
+ 			s++;
+ 		}
+-		if ( *s != '\xFE')
++		if (*s != '\xFE')
+ 		{
+ 			// malformed
+ 			break;
+@@ -9315,75 +10810,75 @@
+ 		// value start
+ 		value = s;
+ 
+-		while ( s < end && *s != '\xFF' )
++		while (s < end && *s != '\xFF')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( *s == '\xFF' )
++		if (*s == '\xFF')
+ 		{
+ 			*s = '\0';
+ 		}
+ 		//fprintf( stderr, "'%s' = '%s'\n", key, value );
+ 
+ 		// Decode current key par
+-		if ( 0 == strcmp( "cmax", key ) )
++		if (0 == strcmp("cmax", key))
+ 		{
+ 			// Max players
+-			server->max_players = atoi( value );
++			server->max_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "cnum", key ) )
++		else if (0 == strcmp("cnum", key))
+ 		{
+ 			// Current players
+-			server->num_players = atoi( value );
++			server->num_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "bal", key ) )
++		else if (0 == strcmp("bal", key))
+ 		{
+ 			// Balance
+-			add_rule( server, "Balance", value, NO_FLAGS );
++			add_rule(server, "Balance", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "world", key ) )
++		else if (0 == strcmp("world", key))
+ 		{
+ 			// Current map
+-			server->map_name = strdup( value );
++			server->map_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "gametype", key ) )
++		else if (0 == strcmp("gametype", key))
+ 		{
+ 			// Game type
+-			server->game = find_savage_game( value );
+-			add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++			server->game = find_savage_game(value);
++			add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "pure", key ) )
++		else if (0 == strcmp("pure", key))
+ 		{
+ 			// Pure
+-			add_rule( server, "Pure", value, NO_FLAGS );
++			add_rule(server, "Pure", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "time", key ) )
++		else if (0 == strcmp("time", key))
+ 		{
+ 			// Current game time
+-			add_rule( server, "Time", value, NO_FLAGS );
++			add_rule(server, "Time", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "notes", key ) )
++		else if (0 == strcmp("notes", key))
+ 		{
+ 			// Notes
+-			add_rule( server, "Notes", value, NO_FLAGS );
++			add_rule(server, "Notes", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "needcmdr", key ) )
++		else if (0 == strcmp("needcmdr", key))
+ 		{
+ 			// Need Commander
+-			add_rule( server, "Need Commander", value, NO_FLAGS );
++			add_rule(server, "Need Commander", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "name", key ) )
++		else if (0 == strcmp("name", key))
+ 		{
+ 			// Server name
+-			server->server_name = strdup( value );
++			server->server_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "fw", key ) )
++		else if (0 == strcmp("fw", key))
+ 		{
+ 			// Firewalled
+-			add_rule( server, "Firewalled", value, NO_FLAGS );
++			add_rule(server, "Firewalled", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "players", key ) )
++		else if (0 == strcmp("players", key))
+ 		{
+ 
+ 			// Players names
+@@ -9394,12 +10889,12 @@
+ 
+ 			// team name
+ 			n++;
+-			while( *n && *n != '\x0a' )
++			while (*n && *n != '\x0a')
+ 			{
+ 				n++;
+ 			}
+ 
+-			if ( *n != '\x0a' )
++			if (*n != '\x0a')
+ 			{
+ 				// Broken data
+ 				break;
+@@ -9407,14 +10902,14 @@
+ 			*n = '\0';
+ 
+ 			player_name = ++n;
+-			while( *n )
++			while (*n)
+ 			{
+-				while( *n && *n != '\x0a' )
++				while (*n && *n != '\x0a')
+ 				{
+ 					n++;
+ 				}
+ 
+-				if ( *n != '\x0a' )
++				if (*n != '\x0a')
+ 				{
+ 					// Broken data
+ 					break;
+@@ -9422,23 +10917,22 @@
+ 				*n = '\0';
+ 				n++;
+ 
+-				if ( 0 == strncmp( "Team ", player_name, 5 ) )
++				if (0 == strncmp("Team ", player_name, 5))
+ 				{
+ 					team_name = player_name;
+ 					team_number++;
+ 				}
+ 				else
+ 				{
+-					if ( 0 != strlen( player_name ) )
++					if (0 != strlen(player_name))
+ 					{
+-						struct player *player = add_player( server, player_number );
+-						if ( NULL != player )
++						struct player *player = add_player(server, player_number);
++						if (NULL != player)
+ 						{
+-							player->name = strdup( player_name );
++							player->name = strdup(player_name);
+ 							player->team = team_number;
+-							player->team_name = strdup( team_name );
+-						}
+-						player_number++;
++							player->team_name = strdup(team_name);
++						} player_number++;
+ 					}
+ 				}
+ 				player_name = n;
+@@ -9448,38 +10942,38 @@
+ 		*s = '\xFF';
+ 	}
+ 
+-	cleanup_qserver( server, 1 );
+-	return;
++	return DONE_FORCE;
+ }
+ 
+-void
+-deal_with_farcry_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *s, *key, *value, *end;
++	char *s, *key, *value, *end;
++
++	debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen );
+ 
+ 	server->n_servers++;
+-    if ( NULL == server->server_name )
++	if (NULL == server->server_name)
+ 	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 	}
+-    else
++	else
+ 	{
+-		gettimeofday( &server->packet_time1, NULL );
++		gettimeofday(&server->packet_time1, NULL);
+ 	}
+ 
+-    rawpkt[pktlen]= '\0';
++	rawpkt[pktlen] = '\0';
+ 
+-    end = s = rawpkt;
++	end = s = rawpkt;
+ 	end += pktlen;
+-    while ( *s )
++	while (*s)
+ 	{
+ 		// Find the seperator
+-		while ( s <= end && *s != '\xFF' )
++		while (s <= end && *s != '\xFF')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( s >= end )
++		if (s >= end)
+ 		{
+ 			// Hit the end no more
+ 			break;
+@@ -9487,11 +10981,11 @@
+ 
+ 		// key start
+ 		key = ++s;
+-		while ( s < end && *s != '\xFE' )
++		while (s < end && *s != '\xFE')
+ 		{
+ 			s++;
+ 		}
+-		if ( *s != '\xFE')
++		if (*s != '\xFE')
+ 		{
+ 			// malformed
+ 			break;
+@@ -9501,75 +10995,75 @@
+ 		// value start
+ 		value = s;
+ 
+-		while ( s < end && *s != '\xFF' )
++		while (s < end && *s != '\xFF')
+ 		{
+ 			s++;
+ 		}
+ 
+-		if ( *s == '\xFF' )
++		if (*s == '\xFF')
+ 		{
+ 			*s = '\0';
+ 		}
+ 		//fprintf( stderr, "'%s' = '%s'\n", key, value );
+ 
+ 		// Decode current key par
+-		if ( 0 == strcmp( "cmax", key ) )
++		if (0 == strcmp("cmax", key))
+ 		{
+ 			// Max players
+-			server->max_players = atoi( value );
++			server->max_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "cnum", key ) )
++		else if (0 == strcmp("cnum", key))
+ 		{
+ 			// Current players
+-			server->num_players = atoi( value );
++			server->num_players = atoi(value);
+ 		}
+-		else if ( 0 == strcmp( "bal", key ) )
++		else if (0 == strcmp("bal", key))
+ 		{
+ 			// Balance
+-			add_rule( server, "Balance", value, NO_FLAGS );
++			add_rule(server, "Balance", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "world", key ) )
++		else if (0 == strcmp("world", key))
+ 		{
+ 			// Current map
+-			server->map_name = strdup( value );
++			server->map_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "gametype", key ) )
++		else if (0 == strcmp("gametype", key))
+ 		{
+ 			// Game type
+-			server->game = find_savage_game( value );
+-			add_rule( server, server->type->game_rule, server->game, NO_FLAGS );
++			server->game = find_savage_game(value);
++			add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "pure", key ) )
++		else if (0 == strcmp("pure", key))
+ 		{
+ 			// Pure
+-			add_rule( server, "Pure", value, NO_FLAGS );
++			add_rule(server, "Pure", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "time", key ) )
++		else if (0 == strcmp("time", key))
+ 		{
+ 			// Current game time
+-			add_rule( server, "Time", value, NO_FLAGS );
++			add_rule(server, "Time", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "notes", key ) )
++		else if (0 == strcmp("notes", key))
+ 		{
+ 			// Notes
+-			add_rule( server, "Notes", value, NO_FLAGS );
++			add_rule(server, "Notes", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "needcmdr", key ) )
++		else if (0 == strcmp("needcmdr", key))
+ 		{
+ 			// Need Commander
+-			add_rule( server, "Need Commander", value, NO_FLAGS );
++			add_rule(server, "Need Commander", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "name", key ) )
++		else if (0 == strcmp("name", key))
+ 		{
+ 			// Server name
+-			server->server_name = strdup( value );
++			server->server_name = strdup(value);
+ 		}
+-		else if ( 0 == strcmp( "fw", key ) )
++		else if (0 == strcmp("fw", key))
+ 		{
+ 			// Firewalled
+-			add_rule( server, "Firewalled", value, NO_FLAGS );
++			add_rule(server, "Firewalled", value, NO_FLAGS);
+ 		}
+-		else if ( 0 == strcmp( "players", key ) )
++		else if (0 == strcmp("players", key))
+ 		{
+ 
+ 			// Players names
+@@ -9580,12 +11074,12 @@
+ 
+ 			// team name
+ 			n++;
+-			while( *n && *n != '\x0a' )
++			while (*n && *n != '\x0a')
+ 			{
+ 				n++;
+ 			}
+ 
+-			if ( *n != '\x0a' )
++			if (*n != '\x0a')
+ 			{
+ 				// Broken data
+ 				break;
+@@ -9593,14 +11087,14 @@
+ 			*n = '\0';
+ 
+ 			player_name = ++n;
+-			while( *n )
++			while (*n)
+ 			{
+-				while( *n && *n != '\x0a' )
++				while (*n && *n != '\x0a')
+ 				{
+ 					n++;
+ 				}
+ 
+-				if ( *n != '\x0a' )
++				if (*n != '\x0a')
+ 				{
+ 					// Broken data
+ 					break;
+@@ -9608,23 +11102,22 @@
+ 				*n = '\0';
+ 				n++;
+ 
+-				if ( 0 == strncmp( "Team ", player_name, 5 ) )
++				if (0 == strncmp("Team ", player_name, 5))
+ 				{
+ 					team_name = player_name;
+ 					team_number++;
+ 				}
+ 				else
+ 				{
+-					if ( 0 != strlen( player_name ) )
++					if (0 != strlen(player_name))
+ 					{
+-						struct player *player = add_player( server, player_number );
+-						if ( NULL != player )
++						struct player *player = add_player(server, player_number);
++						if (NULL != player)
+ 						{
+-							player->name = strdup( player_name );
++							player->name = strdup(player_name);
+ 							player->team = team_number;
+-							player->team_name = strdup( team_name );
+-						}
+-						player_number++;
++							player->team_name = strdup(team_name);
++						} player_number++;
+ 					}
+ 				}
+ 				player_name = n;
+@@ -9634,574 +11127,624 @@
+ 		*s = '\xFF';
+ 	}
+ 
+-	cleanup_qserver( server, 1 );
+-	return;
++	return DONE_FORCE;
+ }
+ 
+-
+-
+ /* postions of map name, player name (in player substring), zero-based */
+ #define BFRIS_MAP_POS 18
+ #define BFRIS_PNAME_POS 11
+-void
+-deal_with_bfris_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-  int i, player_data_pos, nplayers;
+-  SavedData *sdata;
+-  unsigned char *saved_data;
+-  int saved_data_size;
+-
+-  server->ping_total += time_delta( &packet_recv_time,
+-				    &server->packet_time1);
+-
+-  /* add to the data previously saved */
+-  sdata= & server->saved_data;
+-  if (! sdata->data)
+-    sdata->data = (char*)malloc(pktlen);
+-  else
+-    sdata->data = (char*)realloc(sdata->data,sdata->datalen + pktlen);
+-
+-  memcpy(sdata->data + sdata->datalen, rawpkt, pktlen);
+-  sdata->datalen += pktlen;
+-
+-  saved_data= (unsigned char*) sdata->data;
+-  saved_data_size= sdata->datalen;
+-
+-  /* after we get the server portion of the data, server->game != NULL */
+-  if (!server->game) {
+-
+-    /* server data goes up to map name */
+-    if (sdata->datalen <= BFRIS_MAP_POS)
+-      return;
+-
+-    /* see if map name is complete */
+-    player_data_pos=0;
+-    for (i=BFRIS_MAP_POS; i<saved_data_size; i++) {
+-      if (saved_data[i] == '\0') {
+-	player_data_pos = i+1;
+-	/* data must extend beyond map name */
+-	if (saved_data_size <= player_data_pos)
+-	  return;
+-	break;
+-      }
+-    }
++	int i, player_data_pos, nplayers;
++	SavedData *sdata;
++	unsigned char *saved_data;
++	int saved_data_size;
+ 
+-    /* did we find beginning of player data? */
+-    if (!player_data_pos)
+-      return;
+-
+-    /* now we can go ahead and fill in server data */
+-    server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS);
+-    server->max_players = saved_data[12];
+-    server->protocol_version = saved_data[11];
+-
+-    /* save game type */
+-    switch (saved_data[13] & 15) {
+-    case 0: server->game = "FFA"; break;
+-    case 5: server->game = "Rover"; break;
+-    case 6: server->game = "Occupation"; break;
+-    case 7: server->game = "SPAAL"; break;
+-    case 8: server->game = "CTF"; break;
+-    default:
+-      server->game = "unknown"; break;
+-    }
+-    server->flags |= FLAG_DO_NOT_FREE_GAME;
+-    add_rule(server,server->type->game_rule,server->game, NO_FLAGS);
++	debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen );
++
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++	/* add to the data previously saved */
++	sdata = &server->saved_data;
++	if (!sdata->data)
++	{
++		sdata->data = (char*)malloc(pktlen);
++	}
++	else
++	{
++		sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen);
++	}
+ 
+-    if (get_server_rules) {
+-      char buf[24];
++	memcpy(sdata->data + sdata->datalen, rawpkt, pktlen);
++	sdata->datalen += pktlen;
+ 
+-      /* server revision */
+-      sprintf(buf,"%d",(unsigned int)saved_data[11]);
+-      add_rule(server,"Revision",buf, NO_FLAGS);
+-
+-      /* latency */
+-      sprintf(buf,"%d",(unsigned int)saved_data[10]);
+-      add_rule(server,"Latency",buf, NO_FLAGS);
++	saved_data = (unsigned char*)sdata->data;
++	saved_data_size = sdata->datalen;
+ 
+-      /* player allocation */
+-      add_rule(server,"Allocation",saved_data[13] & 16 ? "Automatic" : "Manual", NO_FLAGS);
++	/* after we get the server portion of the data, server->game != NULL */
++	if (!server->game)
++	{
+ 
+-    }
++		/* server data goes up to map name */
++		if (sdata->datalen <= BFRIS_MAP_POS)
++		{
++			return INPROGRESS;
++		}
++
++		/* see if map name is complete */
++		player_data_pos = 0;
++		for (i = BFRIS_MAP_POS; i < saved_data_size; i++)
++		{
++			if (saved_data[i] == '\0')
++			{
++				player_data_pos = i + 1;
++				/* data must extend beyond map name */
++				if (saved_data_size <= player_data_pos)
++				{
++					return INPROGRESS;
++				}
++				break;
++			}
++		}
++
++		/* did we find beginning of player data? */
++		if (!player_data_pos)
++		{
++			return INPROGRESS;
++		}
+ 
+-  }
++		/* now we can go ahead and fill in server data */
++		server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS);
++		server->max_players = saved_data[12];
++		server->protocol_version = saved_data[11];
+ 
+-  /* If we got this far, we know the data saved goes at least to the start of
+-     the player information, and that the server data is taken care of.
+-  */
++		/* save game type */
++		switch (saved_data[13] &15)
++		{
++			case 0:
++				server->game = "FFA";
++				break;
++			case 5:
++				server->game = "Rover";
++				break;
++			case 6:
++				server->game = "Occupation";
++				break;
++			case 7:
++				server->game = "SPAAL";
++				break;
++			case 8:
++				server->game = "CTF";
++				break;
++			default:
++				server->game = "unknown";
++				break;
++		}
++		server->flags |= FLAG_DO_NOT_FREE_GAME;
++		add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
+ 
+-  /* start of player data */
+-  player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data+BFRIS_MAP_POS) + 1;
++		if (get_server_rules)
++		{
++			char buf[24];
+ 
+-  /* ensure all player data have arrived */
+-  nplayers = 0;
+-  while (saved_data[player_data_pos] != '\0') {
++			/* server revision */
++			sprintf(buf, "%d", (unsigned int)saved_data[11]);
++			add_rule(server, "Revision", buf, NO_FLAGS);
+ 
+-    player_data_pos += BFRIS_PNAME_POS;
++			/* latency */
++			sprintf(buf, "%d", (unsigned int)saved_data[10]);
++			add_rule(server, "Latency", buf, NO_FLAGS);
+ 
+-    /* does player data extend to player name? */
+-    if (saved_data_size <= player_data_pos + 1)
+-      return;
++			/* player allocation */
++			add_rule(server, "Allocation", saved_data[13] &16 ? "Automatic" : "Manual", NO_FLAGS);
+ 
+-    /* does player data extend to end of player name? */
+-    for (i=0; player_data_pos + i < saved_data_size; i++) {
++		}
+ 
+-      if (saved_data_size == player_data_pos + i + 1)
+-	return;
++	}
+ 
+-      if (saved_data[player_data_pos + i] == '\0') {
+-	player_data_pos += i + 1;
+-	nplayers++;
+-	break;
+-      }
+-    }
+-  }
+-  /* all player data are complete */
++	/* If we got this far, we know the data saved goes at least to the start of
++	the player information, and that the server data is taken care of.
++	 */
+ 
+-  server->num_players = nplayers;
++	/* start of player data */
++	player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
+ 
+-  if (get_player_info) {
++	/* ensure all player data have arrived */
++	nplayers = 0;
++	while (saved_data[player_data_pos] != '\0')
++	{
+ 
+-    /* start of player data */
+-    player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data+BFRIS_MAP_POS) + 1;
+-
+-    for (i=0; i<nplayers; i++) {
+-      struct player *player;
+-      player= add_player( server, saved_data[player_data_pos]);
+-
+-      player->ship = saved_data[player_data_pos + 1];
+-      player->ping = saved_data[player_data_pos + 2];
+-      player->frags = saved_data[player_data_pos + 3];
+-      player->team = saved_data[player_data_pos + 4];
+-      switch (player->team) {
+-      case 0: player->team_name = "silver"; break;
+-      case 1: player->team_name = "red"; break;
+-      case 2: player->team_name = "blue"; break;
+-      case 3: player->team_name = "green"; break;
+-      case 4: player->team_name = "purple"; break;
+-      case 5: player->team_name = "yellow"; break;
+-      case 6: player->team_name = "cyan"; break;
+-      default:
+-	player->team_name = "unknown"; break;
+-      }
+-      player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
+-      player->room = saved_data[player_data_pos + 5];
+-
+-      /* score is little-endian integer */
+-      player->score =
+-	saved_data[player_data_pos+7] +
+-	(saved_data[player_data_pos+8] << 8) +
+-	(saved_data[player_data_pos+9] << 16) +
+-	(saved_data[player_data_pos+10] << 24);
+-
+-	  /* for archs with > 4-byte int */
+-      if (player->score & 0x80000000)
+-	player->score = -(~(player->score)) - 1;
++		player_data_pos += BFRIS_PNAME_POS;
+ 
++		/* does player data extend to player name? */
++		if (saved_data_size <= player_data_pos + 1)
++		{
++			return INPROGRESS;
++		}
+ 
+-      player_data_pos += BFRIS_PNAME_POS;
+-      player->name = strdup((char*)saved_data + player_data_pos);
++		/* does player data extend to end of player name? */
++		for (i = 0; player_data_pos + i < saved_data_size; i++)
++		{
+ 
+-      player_data_pos += strlen(player->name) + 1;
+-    }
++			if (saved_data_size == player_data_pos + i + 1)
++			{
++				return INPROGRESS;
++			}
+ 
+-  }
++			if (saved_data[player_data_pos + i] == '\0')
++			{
++				player_data_pos += i + 1;
++				nplayers++;
++				break;
++			}
++		}
++	}
++	/* all player data are complete */
++
++	server->num_players = nplayers;
++
++	if (get_player_info)
++	{
++
++		/* start of player data */
++		player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
++
++		for (i = 0; i < nplayers; i++)
++		{
++			struct player *player;
++			player = add_player(server, saved_data[player_data_pos]);
++
++			player->ship = saved_data[player_data_pos + 1];
++			player->ping = saved_data[player_data_pos + 2];
++			player->frags = saved_data[player_data_pos + 3];
++			player->team = saved_data[player_data_pos + 4];
++			switch (player->team)
++			{
++				case 0:
++					player->team_name = "silver";
++					break;
++				case 1:
++					player->team_name = "red";
++					break;
++				case 2:
++					player->team_name = "blue";
++					break;
++				case 3:
++					player->team_name = "green";
++					break;
++				case 4:
++					player->team_name = "purple";
++					break;
++				case 5:
++					player->team_name = "yellow";
++					break;
++				case 6:
++					player->team_name = "cyan";
++					break;
++				default:
++					player->team_name = "unknown";
++					break;
++			}
++			player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++			player->room = saved_data[player_data_pos + 5];
++
++			/* score is little-endian integer */
++			player->score = saved_data[player_data_pos + 7] +
++				(saved_data[player_data_pos + 8] << 8) +
++				(saved_data[player_data_pos + 9] << 16) +
++				(saved_data[player_data_pos + 10] << 24);
++
++			/* for archs with > 4-byte int */
++			if (player->score &0x80000000)
++			{
++				player->score = - (~(player->score)) - 1;
++			}
+ 
+-  server->server_name = BFRIS_SERVER_NAME;
+-  cleanup_qserver(server, 1);
+-  return;
++
++			player_data_pos += BFRIS_PNAME_POS;
++			player->name = strdup((char*)saved_data + player_data_pos);
++
++			player_data_pos += strlen(player->name) + 1;
++		}
++
++	}
++
++	server->server_name = BFRIS_SERVER_NAME;
++
++	return DONE_FORCE;
+ }
+ 
+-struct rule *
+-add_uchar_rule( struct qserver *server, char *key, unsigned char value)
++struct rule *add_uchar_rule(struct qserver *server, char *key, unsigned char value)
+ {
+-    char buf[24];
+-    sprintf( buf, "%u", (unsigned)value);
+-    return add_rule( server, key, buf, NO_FLAGS);
++	char buf[24];
++	sprintf(buf, "%u", (unsigned)value);
++	return add_rule(server, key, buf, NO_FLAGS);
+ }
+ 
+-void
+-deal_with_descent3_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *pkt;
+-    char buf[24];
++	char *pkt;
++	char buf[24];
+ 
+-    if ( server->server_name == NULL)
+-	server->ping_total += time_delta( &packet_recv_time,
+-				    &server->packet_time1);
++	debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen );
+ 
+-    if ( pktlen < 4)  {
+-	fprintf( stderr, "short descent3 packet\n");
+-	print_packet( server, rawpkt, pktlen);
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
++	if (server->server_name == NULL)
++	{
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++	}
+ 
+-    /* 'info' response */
+-    if ( rawpkt[1] == 0x1f)  {
+-	if ( server->server_name != NULL)
+-	{
+-	    cleanup_qserver( server, 1);
+-	    return;
++	if (pktlen < 4)
++	{
++		fprintf(stderr, "short descent3 packet\n");
++		print_packet(server, rawpkt, pktlen);
++		return PKT_ERROR;
+ 	}
+ 
++	/* 'info' response */
++	if (rawpkt[1] == 0x1f)
++	{
++		if (server->server_name != NULL)
++		{
++			return PKT_ERROR;
++		}
++
++
++		pkt = &rawpkt[0x15];
++		server->server_name = strdup(pkt);
++		pkt += strlen(pkt) + 2;
++		server->map_name = strdup(pkt); /* mission name (blah.mn3) */
++		pkt += strlen(pkt) + 2;
++		add_rule(server, "level_name", pkt, NO_FLAGS);
++		pkt += strlen(pkt) + 2;
++		add_rule(server, "gametype", pkt, NO_FLAGS);
++		pkt += strlen(pkt) + 1;
++
++		sprintf(buf, "%hu", swap_short_from_little(pkt));
++		add_rule(server, "level_num", buf, NO_FLAGS);
++		pkt += 2;
++		server->num_players = swap_short_from_little(pkt);
++		pkt += 2;
++		server->max_players = swap_short_from_little(pkt);
++		pkt += 2;
++
++		/* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */
++		add_uchar_rule(server, "u0", pkt[0]);
++		add_uchar_rule(server, "u1", pkt[1]);
++		add_uchar_rule(server, "u2", pkt[2]);
++		add_uchar_rule(server, "u3", pkt[3]);
++		add_uchar_rule(server, "u4", pkt[4]);
++		add_uchar_rule(server, "u5", pkt[5]);
++		add_uchar_rule(server, "u6", pkt[6]);
++		add_uchar_rule(server, "u7", pkt[7]);
++		add_uchar_rule(server, "u8", pkt[8]);
++
++		add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /*
++		randomize powerup spawn */
++		add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0));
++		/* accurate collision detection */
++		add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0));
++		/* bright player ships */
++		add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /*
++		mouselook enabled */
++		sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : "");
++		add_rule(server, "servertype", buf, NO_FLAGS);
++
++		sprintf(buf, "%hhu", pkt[9]);
++		add_rule(server, "difficulty", buf, NO_FLAGS);
++
++		/* unknown/undecoded fields after known flags removed */
++		add_uchar_rule(server, "x4", (unsigned char)(pkt[4] &~(1+16)));
++		add_uchar_rule(server, "x5", (unsigned char)(pkt[5] &~(4+16)));
++		add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1));
++
++		if (get_player_info && server->num_players)
++		{
++			server->next_player_info = 0;
++			send_player_request_packet(server);
++			return INPROGRESS;
++		}
+ 
+-	pkt= &rawpkt[0x15];
+-	server->server_name= strdup( pkt);
+-	pkt+= strlen(pkt)+2;
+-	server->map_name= strdup( pkt); /* mission name (blah.mn3) */
+-	pkt+= strlen(pkt)+2;
+-	add_rule( server, "level_name", pkt, NO_FLAGS);
+-	pkt+= strlen(pkt)+2;
+-	add_rule( server, "gametype", pkt, NO_FLAGS);
+-	pkt+= strlen(pkt)+1;
+-
+-	sprintf( buf, "%hu", swap_short_from_little(pkt));
+-	add_rule( server, "level_num", buf, NO_FLAGS);
+-	pkt+=2;
+-	server->num_players= swap_short_from_little(pkt);
+-	pkt+=2;
+-	server->max_players= swap_short_from_little(pkt);
+-	pkt+=2;
+-
+-	/* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */
+-	add_uchar_rule( server, "u0", pkt[0]);
+-	add_uchar_rule( server, "u1", pkt[1]);
+-	add_uchar_rule( server, "u2", pkt[2]);
+-	add_uchar_rule( server, "u3", pkt[3]);
+-	add_uchar_rule( server, "u4", pkt[4]);
+-	add_uchar_rule( server, "u5", pkt[5]);
+-	add_uchar_rule( server, "u6", pkt[6]);
+-	add_uchar_rule( server, "u7", pkt[7]);
+-	add_uchar_rule( server, "u8", pkt[8]);
+-
+-	add_uchar_rule( server, "randpowerup", !(pkt[4]&1)); /* randomize powerup spawn */
+-	add_uchar_rule( server, "acccollisions", (pkt[5]&4) > 0); /* accurate collision detection */
+-	add_uchar_rule( server, "brightships", (pkt[5]&16) > 0); /* bright player ships */
+-	add_uchar_rule( server, "mouselook", (pkt[6]&1) > 0); /* mouselook enabled */
+-	sprintf( buf, "%s%s", (pkt[4]&16)?"PP":"CS", (pkt[6]&1)?"-ML":"");
+-	add_rule( server, "servertype", buf, NO_FLAGS);
+-
+-	sprintf( buf, "%hhu", pkt[9]);
+-	add_rule( server, "difficulty", buf, NO_FLAGS);
+-
+-	/* unknown/undecoded fields after known flags removed */
+-	add_uchar_rule( server, "x4", pkt[4] & ~(1+16));
+-	add_uchar_rule( server, "x5", pkt[5] & ~(4+16));
+-	add_uchar_rule( server, "x6", pkt[6] & ~1);
+-
+-	if ( get_player_info && server->num_players)  {
+-	    server->next_player_info= 0;
+-	    send_player_request_packet( server);
+-	    cleanup_qserver( server, 0);
+-	    return;
+ 	}
++	/* MP_PLAYERLIST_DATA */
++	else if (rawpkt[1] == 0x73)
++	{
++		struct player *player;
++		struct player **last_player = &server->players;
+ 
+-    }
+-    /* MP_PLAYERLIST_DATA */
+-    else if ( rawpkt[1] == 0x73)  {
+-	struct player *player;
+-	struct player **last_player= & server->players;
++		if (server->players != NULL)
++		{
++			return PKT_ERROR;
++		}
+ 
+-	if ( server->players != NULL)
++		pkt = &rawpkt[0x4];
++		while (*pkt)
++		{
++			player = (struct player*)calloc(1, sizeof(struct player));
++			player->name = strdup(pkt);
++			pkt += strlen(pkt) + 1;
++			*last_player = player;
++			last_player = &player->next;
++		}
++		server->next_player_info = NO_PLAYER_INFO;
++	}
++	else
+ 	{
+-	    cleanup_qserver( server, 1);
+-	    return;
++		fprintf(stderr, "unknown d3 packet\n");
++		print_packet(server, rawpkt, pktlen);
+ 	}
+ 
+-	pkt= &rawpkt[0x4];
+-	while (*pkt)  {
+-	    player= (struct player*) calloc( 1, sizeof(struct player));
+-	    player->name= strdup( pkt);
+-	    pkt+= strlen(pkt) + 1;
+-	    *last_player= player;
+-	    last_player= & player->next;
+-	}
+-	server->next_player_info= NO_PLAYER_INFO;
+-    }
+-    else {
+-	fprintf( stderr, "unknown d3 packet\n");
+-	print_packet( server, rawpkt, pktlen);
+-    }
+-    cleanup_qserver( server, 1);
+-    return;
++	return DONE_FORCE;
+ }
+ 
+ 
+-#define EYE_NAME_MASK   1
+-#define EYE_TEAM_MASK   2
+-#define EYE_SKIN_MASK   4
+-#define EYE_SCORE_MASK  8
+-#define EYE_PING_MASK   16
+-#define EYE_TIME_MASK   32
++#define EYE_NAME_MASK  1
++#define EYE_TEAM_MASK  2
++#define EYE_SKIN_MASK  4
++#define EYE_SCORE_MASK 8
++#define EYE_PING_MASK  16
++#define EYE_TIME_MASK  32
+ 
+-void
+-deal_with_eye_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_eye_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    char *next, *end, *value, *key;
+-    struct player **last_player;
+-    unsigned char pkt_index, pkt_max;
+-    unsigned int pkt_id;
++	char *next, *end, *value, *key;
++	struct player **last_player;
++	unsigned char pkt_index, pkt_max;
++	unsigned int pkt_id;
++
++	debug( 2, "deal_with_eye_packet %p, %d", server, pktlen );
+ 
+-    if ( pktlen < 4)
++	if (pktlen < 4)
+ 	{
+-		cleanup_qserver( server, 1);
+-		return;
+-    }
++		return PKT_ERROR;
++	}
+ 
+-    if ( rawpkt[0] != 'E' || rawpkt[1] != 'Y' || rawpkt[2] != 'E')
++	if (rawpkt[0] != 'E' || rawpkt[1] != 'Y' || rawpkt[2] != 'E')
+ 	{
+-		cleanup_qserver( server, 1);
+-		return;
+-    }
++		return PKT_ERROR;
++	}
+ 
+-    server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 
+-    end= rawpkt + pktlen;
+-    pkt_index= rawpkt[3] - '0';
++	end = rawpkt + pktlen;
++	pkt_index = rawpkt[3] - '0';
+ 
+-    if ( pktlen == 1364 || pkt_index != 1)
++	if (pktlen == 1364 || pkt_index != 1)
+ 	{
+ 		/* fragmented packet */
+ 		SavedData *sdata;
+-		/* EYE doesn't tell us how many packets to expect.  Two packets
++		/* EYE doesn't tell us how many packets to expect. Two packets
+ 		 * is enough for 100+ players on a BF1942 server with standard
+ 		 * server rules.
+ 		 */
+-		pkt_max= 2;
+-		memcpy( &pkt_id, &rawpkt[pktlen-4], 4);
++		pkt_max = 2;
++		memcpy(&pkt_id, &rawpkt[pktlen - 4], 4);
+ 
+-		if ( server->saved_data.data == NULL)
++		if (server->saved_data.data == NULL)
+ 		{
+-			sdata= & server->saved_data;
++			sdata = &server->saved_data;
+ 		}
+ 		else
+ 		{
+-			sdata= (SavedData*) calloc( 1, sizeof(SavedData));
+-			sdata->next= server->saved_data.next;
+-			server->saved_data.next= sdata;
++			sdata = (SavedData*)calloc(1, sizeof(SavedData));
++			sdata->next = server->saved_data.next;
++			server->saved_data.next = sdata;
+ 		}
+ 
+-		sdata->pkt_index= pkt_index-1;
+-		sdata->pkt_max= pkt_max;
+-		sdata->pkt_id= pkt_id;
+-		if ( pkt_index == 1)
++		sdata->pkt_index = pkt_index - 1;
++		sdata->pkt_max = pkt_max;
++		sdata->pkt_id = pkt_id;
++		if (pkt_index == 1)
+ 		{
+-			sdata->datalen= pktlen-4;
++			sdata->datalen = pktlen - 4;
+ 		}
+ 		else
+ 		{
+-			sdata->datalen= pktlen-8;
++			sdata->datalen = pktlen - 8;
+ 		}
+-		sdata->data= (char*) malloc( sdata->datalen);
++		sdata->data = (char*)malloc(sdata->datalen);
+ 
+-		if ( NULL == sdata->data )
++		if (NULL == sdata->data)
+ 		{
+-			fprintf( stderr, "Out of memory\n" );
+-			cleanup_qserver( server, 1 );
+-			return;
++			return MEM_ERROR;
+ 		}
+-		if ( pkt_index == 1)
++
++		if (pkt_index == 1)
+ 		{
+-			memcpy( sdata->data, &rawpkt[0], sdata->datalen);
++			memcpy(sdata->data, &rawpkt[0], sdata->datalen);
+ 		}
+ 		else
+ 		{
+-			memcpy( sdata->data, &rawpkt[4], sdata->datalen);
++			memcpy(sdata->data, &rawpkt[4], sdata->datalen);
+ 		}
+ 
+ 		/* combine_packets will call us recursively */
+-		combine_packets( server);
+-		return;
+-    }
++		return combine_packets(server);
++	}
+ 
+-    value= dup_n1string( &rawpkt[4], end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(&rawpkt[4], end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    add_rule( server, "gamename", value, NO_VALUE_COPY);
++	add_rule(server, "gamename", value, NO_VALUE_COPY);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    add_rule( server, "hostport", value, NO_VALUE_COPY);
++	add_rule(server, "hostport", value, NO_VALUE_COPY);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    server->server_name= value;
++	server->server_name = value;
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    server->game= value;
+-	add_rule( server, server->type->game_rule, value, NO_FLAGS);
++	server->game = value;
++	add_rule(server, server->type->game_rule, value, NO_FLAGS);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    server->map_name= value;
++	server->map_name = value;
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    add_rule( server, "_version", value, NO_VALUE_COPY);
++	add_rule(server, "_version", value, NO_VALUE_COPY);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    add_rule( server, "_password", value, NO_VALUE_COPY);
++	add_rule(server, "_password", value, NO_VALUE_COPY);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    server->num_players= atoi(value);
+-    free(value);
++	server->num_players = atoi(value);
++	free(value);
+ 
+-    value= dup_n1string( next, end, &next);
+-    if ( value == NULL)
++	value = dup_n1string(next, end, &next);
++	if (value == NULL)
+ 	{
+-		goto eye_protocol_error;
++		return MEM_ERROR;
+ 	}
+-    server->max_players= atoi(value);
+-    free(value);
++	server->max_players = atoi(value);
++	free(value);
+ 
+-    /* rule1,value1,rule2,value2, ... empty string */
++	/* rule1,value1,rule2,value2, ... empty string */
+ 
+-    do
++	do
+ 	{
+-		key= dup_n1string( next, end, &next);
+-		if ( key == NULL)
++		key = dup_n1string(next, end, &next);
++		if (key == NULL)
+ 		{
+ 			break;
+ 		}
+-		else if ( key[0] == '\0')
++		else if (key[0] == '\0')
+ 		{
+ 			free(key);
+ 			break;
+ 		}
+ 
+-		value= dup_n1string( next, end, &next);
+-		if ( value == NULL)
++		value = dup_n1string(next, end, &next);
++		if (value == NULL)
+ 		{
+ 			free(key);
+ 			break;
+ 		}
+ 
+-		add_rule( server, key, value, NO_VALUE_COPY | NO_KEY_COPY);
+-    } while ( 1);
++		add_rule(server, key, value, NO_VALUE_COPY | NO_KEY_COPY);
++	}
++	while (1);
+ 
+-    /* [mask1]<name1><team1><skin1><score1><ping1><time1>[mask2]... */
++	/* [mask1]<name1><team1><skin1><score1><ping1><time1>[mask2]... */
+ 
+-    last_player= & server->players;
+-    while ( next && next < end )
++	last_player = &server->players;
++	while (next && next < end)
+ 	{
+ 		struct player *player;
+-		unsigned mask= *((unsigned char*)next);
++		unsigned mask = *((unsigned char*)next);
+ 		next++;
+-		if ( next >= end)
++		if (next >= end)
+ 		{
+ 			break;
+ 		}
+-		if ( mask == 0)
++		if (mask == 0)
+ 		{
+ 			break;
+ 		}
+-		player= (struct player*) calloc( 1, sizeof(struct player));
+-		if ( player == NULL)
++		player = (struct player*)calloc(1, sizeof(struct player));
++		if (player == NULL)
+ 		{
+ 			break;
+ 		}
+-		if ( mask & EYE_NAME_MASK)
++		if (mask &EYE_NAME_MASK)
+ 		{
+-			player->name= dup_n1string( next, end, &next);
++			player->name = dup_n1string(next, end, &next);
+ 			//fprintf( stderr, "Player '%s'\n", player->name );
+-			if ( player->name == NULL)
++			if (player->name == NULL)
+ 			{
+ 				break;
+ 			}
+ 		}
+-		if ( mask & EYE_TEAM_MASK)
++		if (mask &EYE_TEAM_MASK)
+ 		{
+-			value= dup_n1string( next, end, &next);
+-			if ( value == NULL)
++			value = dup_n1string(next, end, &next);
++			if (value == NULL)
+ 			{
+ 				break;
+ 			}
+-			if ( isdigit((unsigned char)value[0]))
++			if (isdigit((unsigned char)value[0]))
+ 			{
+-				player->team= atoi(value);
++				player->team = atoi(value);
+ 				free(value);
+ 			}
+ 			else
+ 			{
+-				player->team_name= value;
++				player->team_name = value;
+ 			}
+ 		}
+-		if ( mask & EYE_SKIN_MASK)
++		if (mask &EYE_SKIN_MASK)
+ 		{
+-			player->skin= dup_n1string( next, end, &next);
+-			if ( player->skin == NULL)
++			player->skin = dup_n1string(next, end, &next);
++			if (player->skin == NULL)
+ 			{
+ 				break;
+ 			}
+ 		}
+-		if ( mask & EYE_SCORE_MASK)
++		if (mask &EYE_SCORE_MASK)
+ 		{
+-			value= dup_n1string( next, end, &next);
+-			if ( value == NULL)
++			value = dup_n1string(next, end, &next);
++			if (value == NULL)
+ 			{
+ 				break;
+ 			}
+-			player->score= atoi(value);
+-			player->frags= player->score;
++			player->score = atoi(value);
++			player->frags = player->score;
+ 			free(value);
+ 		}
+-		if ( mask & EYE_PING_MASK)
++		if (mask &EYE_PING_MASK)
+ 		{
+-			value= dup_n1string( next, end, &next);
+-			if ( value == NULL)
++			value = dup_n1string(next, end, &next);
++			if (value == NULL)
+ 			{
+ 				break;
+ 			}
+-			player->ping= atoi(value);
++			player->ping = atoi(value);
+ 			free(value);
+ 		}
+-		if ( mask & EYE_TIME_MASK)
++		if (mask &EYE_TIME_MASK)
+ 		{
+-			value= dup_n1string( next, end, &next);
+-			if ( value == NULL)
++			value = dup_n1string(next, end, &next);
++			if (value == NULL)
+ 			{
+ 				break;
+ 			}
+-			player->connect_time= atoi(value);
++			player->connect_time = atoi(value);
+ 			free(value);
+ 		}
+-		*last_player= player;
+-		last_player= & player->next;
++		*last_player = player;
++		last_player = &player->next;
+ 		//fprintf( stderr, "Player '%s'\n", player->name );
+-    }
+-
+-    cleanup_qserver( server, 1);
+-    return;
++	}
+ 
+-eye_protocol_error:
+-    cleanup_qserver( server, 1);
++	return DONE_FORCE;
+ }
+ 
+ static const char hl2_statusresponse[] = "\xFF\xFF\xFF\xFF\x49";
+@@ -10211,9 +11754,7 @@
+ #define HL2_STATUS 1
+ #define HL2_PLAYERS 2
+ #define HL2_RULES 3
+-
+-void
+-deal_with_hl2_packet( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_hl2_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+ 	char *ptr = rawpkt;
+ 	char *end = rawpkt + pktlen;
+@@ -10222,659 +11763,865 @@
+ 	unsigned char protocolver = 0;
+ 	int n_sent = 0;
+ 
++	debug( 2, "deal_with_hl2_packet %p, %d", server, pktlen );
++
+ 	server->n_servers++;
+-	if ( server->server_name == NULL)
++	if (server->server_name == NULL)
+ 	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
++		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
+ 	}
+ 	else
+ 	{
+-		gettimeofday( &server->packet_time1, NULL);
++		gettimeofday(&server->packet_time1, NULL);
+ 	}
+ 
+ 	// Check if correct reply
+-	if ( pktlen < hl2_response_size )
++	if (pktlen < hl2_response_size)
+ 	{
+ 		malformed_packet(server, "short response type");
+-		cleanup_qserver( server, 1);
+-		return;
++		return PKT_ERROR;
+ 	}
+ 	else
+ 	{
+-		if ( 0 == memcmp( hl2_statusresponse, ptr, hl2_response_size ) )
++		if (0 == memcmp(hl2_statusresponse, ptr, hl2_response_size))
+ 		{
+-			if ( pktlen < hl2_response_size + 20 )
++			if (pktlen < hl2_response_size + 20)
+ 			{
+ 				malformed_packet(server, "short packet");
+-				cleanup_qserver( server, 1);
+-				return;
++				return PKT_ERROR;
+ 			}
+ 			type = HL2_STATUS;
+ 		}
+-		else if ( 0 == memcmp( hl2_playersresponse, ptr, hl2_response_size ) )
++		else if (0 == memcmp(hl2_playersresponse, ptr, hl2_response_size))
+ 		{
+ 			type = HL2_PLAYERS;
+ 		}
+-		else if ( 0 == memcmp( hl2_rulesresponse, ptr, hl2_response_size ) )
++		else if (0 == memcmp(hl2_rulesresponse, ptr, hl2_response_size))
+ 		{
+ 			type = HL2_RULES;
+ 		}
+ 		else
+ 		{
+ 			malformed_packet(server, "unknown response");
+-			cleanup_qserver( server, 1);
+-			return;
++			return PKT_ERROR;
+ 		}
+ 	}
+ 
+ 	// header
+ 	ptr += hl2_response_size;
+ 
+-	switch( type )
++	switch (type)
+ 	{
+-	case HL2_STATUS:
++		case HL2_STATUS:
+ 
+-		// protocol version
+-		protocolver = *ptr;
+-		ptr++;
++			// protocol version
++			protocolver = *ptr;
++			ptr++;
+ 
+-		debug(2, "protocol: 0x%02X", protocolver );
+-		// Commented out till out of beta
+-/*
+-		if( '\x02' != protocolver )
+-		{
+-			malformed_packet(server, "protocol version != 0x02");
+-			cleanup_qserver( server, 1);
+-			return;
+-		}
+-*/
++			debug(2, "protocol: 0x%02X", protocolver);
++			// Commented out till out of beta
++			/*
++			if( '\x02' != protocolver )
++			{
++				malformed_packet(server, "protocol version != 0x02");
++				return PKT_ERROR;
++			}
++			 */
+ 
+-		server->protocol_version = protocolver;
+-		sprintf( temp, "%d", protocolver );
+-		add_rule( server, "protocol", temp, NO_FLAGS);
++			server->protocol_version = protocolver;
++			sprintf(temp, "%d", protocolver);
++			add_rule(server, "protocol", temp, NO_FLAGS);
+ 
+-		// server name
+-		server->server_name = strdup( ptr );
+-		ptr += strlen( ptr ) + 1;
++			// server name
++			server->server_name = strdup(ptr);
++			ptr += strlen(ptr) + 1;
+ 
+-		// map
+-		server->map_name = strdup( ptr );
+-		ptr += strlen( ptr ) + 1;
++			// map
++			server->map_name = strdup(ptr);
++			ptr += strlen(ptr) + 1;
+ 
+-		// gamedir
+-		server->game = strdup( ptr );
+-		add_rule( server, "gamedir", ptr, NO_FLAGS );
+-		ptr += strlen( ptr ) + 1;
++			// gamedir
++			server->game = strdup(ptr);
++			add_rule(server, "gamedir", ptr, NO_FLAGS);
++			ptr += strlen(ptr) + 1;
+ 
+-		// description
+-		add_rule( server, "description", ptr, NO_FLAGS );
+-		ptr += strlen( ptr ) + 1;
++			// description
++			add_rule(server, "description", ptr, NO_FLAGS);
++			ptr += strlen(ptr) + 1;
+ 
+-		// appid
+-		ptr += 2;
++			// appid
++			ptr += 2;
+ 
+-		// num players
+-		server->num_players = *ptr;
+-		ptr++;
++			// num players
++			server->num_players = *ptr;
++			ptr++;
+ 
+-		// max players
+-		server->max_players = *ptr;
+-		ptr++;
++			// max players
++			server->max_players = *ptr;
++			ptr++;
+ 
+-		// bot players
+-		sprintf( temp, "%hhu", (*ptr) );
+-		add_rule( server, "bot_players", temp, NO_FLAGS );
+-		ptr++;
++			// bot players
++			sprintf(temp, "%hhu", (*ptr));
++			add_rule(server, "bot_players", temp, NO_FLAGS);
++			ptr++;
+ 
+-		// dedicated
+-		if ( 'd' == *ptr )
+-		{
+-			add_rule( server, "sv_type", "dedicated", NO_FLAGS );
+-		}
+-		else if ( 'l' == *ptr )
+-		{
+-			add_rule( server, "sv_type", "listen", NO_FLAGS );
+-		}
+-		else
+-		{
+-			char tmp[2] = { *ptr, '\0' };
+-			add_rule( server, "sv_type", tmp, NO_FLAGS );
+-		}
+-		ptr++;
++			// dedicated
++			if ('d' == *ptr)
++			{
++				add_rule(server, "sv_type", "dedicated", NO_FLAGS);
++			}
++			else if ('l' == *ptr)
++			{
++				add_rule(server, "sv_type", "listen", NO_FLAGS);
++			}
++			else
++			{
++				char tmp[2] =
++				{
++					*ptr, '\0'
++				}
++				;
++				add_rule(server, "sv_type", tmp, NO_FLAGS);
++			}
++			ptr++;
+ 
+-		// OS
+-		if ( 'l' == *ptr )
+-		{
+-			add_rule( server, "sv_os", "linux", NO_FLAGS );
+-		}
+-		else if ( 'w' == *ptr )
+-		{
+-			add_rule( server, "sv_os", "windows", NO_FLAGS );
+-		}
+-		else
+-		{
+-			char tmp[2] = { *ptr, '\0' };
+-			add_rule( server, "sv_os", tmp, NO_FLAGS );
+-		}
+-		ptr++;
++			// OS
++			if ('l' == *ptr)
++			{
++				add_rule(server, "sv_os", "linux", NO_FLAGS);
++			}
++			else if ('w' == *ptr)
++			{
++				add_rule(server, "sv_os", "windows", NO_FLAGS);
++			}
++			else
++			{
++				char tmp[2] =
++				{
++					*ptr, '\0'
++				}
++				;
++				add_rule(server, "sv_os", tmp, NO_FLAGS);
++			}
++			ptr++;
+ 
+-		// passworded
+-		add_rule( server, "sv_password", *ptr ? "1" : "0", NO_FLAGS);
+-		ptr++;
++			// passworded
++			add_rule(server, "sv_password", *ptr ? "1" : "0", NO_FLAGS);
++			ptr++;
+ 
+-		// secure
+-		add_rule( server, "secure", *ptr ? "1" : "0", NO_FLAGS);
+-		ptr++;
++			// secure
++			add_rule(server, "secure", *ptr ? "1" : "0", NO_FLAGS);
++			ptr++;
+ 
+-		// send the other request packets if wanted
+-		if ( get_server_rules )
+-		{
+-			int requests = server->n_requests;
+-			send_rule_request_packet( server );
+-			server->n_requests = requests; // prevent wrong ping
+-			n_sent++;
+-		}
+-		else if ( get_player_info )
+-		{
+-			int requests = server->n_requests;
+-			send_player_request_packet( server ) ;
+-			server->n_requests = requests; // prevent wrong ping
+-			n_sent++;
+-		}
+-		break;
++			// send the other request packets if wanted
++			if (get_server_rules)
++			{
++				int requests = server->n_requests;
++				send_rule_request_packet(server);
++				server->n_requests = requests; // prevent wrong ping
++				n_sent++;
++			}
++			else if (get_player_info)
++			{
++				int requests = server->n_requests;
++				send_player_request_packet(server);
++				server->n_requests = requests; // prevent wrong ping
++				n_sent++;
++			}
++			break;
+ 
+-	case HL2_RULES:
+-		// num_players
+-		ptr++;
++		case HL2_RULES:
++			// num_players
++			ptr++;
+ 
+-		// max_players
+-		ptr++;
+-		while ( ptr < end )
+-		{
+-			char *var = ptr;
+-			char *val;
+-			ptr += strlen( var ) + 1;
+-			val = ptr;
+-			ptr += strlen( val ) + 1;
+-			add_rule( server, var, val, NO_FLAGS );
+-		}
++			// max_players
++			ptr++;
++			while (ptr < end)
++			{
++				char *var = ptr;
++				char *val;
++				ptr += strlen(var) + 1;
++				val = ptr;
++				ptr += strlen(val) + 1;
++				add_rule(server, var, val, NO_FLAGS);
++			}
+ 
+-		if ( get_player_info )
+-		{
+-			send_player_request_packet( server ) ;
+-			n_sent++;
+-		}
+-		break;
++			if (get_player_info)
++			{
++				send_player_request_packet(server);
++				n_sent++;
++			}
++			break;
+ 
+-	case HL2_PLAYERS:
+-		// num_players
+-		ptr++;
++		case HL2_PLAYERS:
++			// num_players
++			ptr++;
+ 
+-		while ( ptr < end )
+-		{
+-			struct player *player = add_player( server, server->n_player_info );
++			while (ptr < end)
++			{
++				struct player *player = add_player(server, server->n_player_info);
+ 
+-			// player no
+-			ptr++;
++				// player no
++				ptr++;
+ 
+-			// name
+-			player->name = strdup( ptr );
+-			ptr += strlen( ptr ) + 1;
+-
+-			// frags
+-			player->frags = swap_long_from_little( ptr );
+-			ptr += 4;
+-
+-			// time
+-			player->connect_time = swap_float_from_little( ptr );
+-			ptr += 4;
+-		}
++				// name
++				player->name = strdup(ptr);
++				ptr += strlen(ptr) + 1;
+ 
+-		break;
++				// frags
++				player->frags = swap_long_from_little(ptr);
++				ptr += 4;
+ 
+-	default:
+-		malformed_packet( server, "unknown response" );
+-		cleanup_qserver( server, 1);
+-		return;
+-	}
++				// time
++				player->connect_time = swap_float_from_little(ptr);
++				ptr += 4;
++			}
+ 
++			break;
+ 
+-	if ( 0 == n_sent )
+-	{
+-		cleanup_qserver( server, 1 );
++		default:
++			malformed_packet(server, "unknown response");
++			return PKT_ERROR;
+ 	}
+-	return;
++
++	return ( 0 == n_sent ) ? DONE_FORCE : INPROGRESS;
+ }
+ 
+-void
+-deal_with_gamespy_master_response( struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_gamespy_master_response(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    if ( pktlen == 0)  {
+-	int len= server->saved_data.datalen;
+-	char *data= server->saved_data.data;
+-	char *ip, *portstr;
+-	unsigned int ipaddr;
+-	unsigned short port;
+-	int master_pkt_max;
++	debug( 2, "deal_with_gamespy_master_response %p, %d", server, pktlen );
+ 
+-	server->server_name= GAMESPY_MASTER_NAME;
++	if ( 0 == pktlen || ( pktlen > 6 && 0 == strncmp( rawpkt + pktlen - 6, "final\\", 6 ) ) )
++	{
++		int len = server->saved_data.datalen;
++		char *data = server->saved_data.data;
++		char *ip, *portstr;
++		unsigned int ipaddr;
++		unsigned short port;
++		int master_pkt_max;
+ 
+-	master_pkt_max= (len / 20) * 6;
+-	server->master_pkt= (char*) malloc( master_pkt_max);
+-	server->master_pkt_len= 0;
+-
+-	while ( len)  {
+-	    for ( ; len && *data == '\\'; data++, len--) ;
+-	    if ( len < 3) break;
+-	    if ( data[0] == 'i' && data[1] == 'p' && data[2] == '\\')  {
+-		data+= 3;
+-		len-= 3;
+-		ip= data;
+-		portstr= NULL;
+-		for ( ; len && *data != '\\'; data++, len--)  {
+-		    if ( *data == ':')  {
+-			portstr= data+1;
+-			*data= '\0';
+-		    }
+-		}
+-		if ( len == 0)
+-		    break;
+-		*data++= '\0';
+-		len--;
+-		ipaddr= inet_addr( ip);
+-		if ( portstr)
+-		    port= htons( (unsigned short)atoi( portstr));
+-		else
+-		    port= htons( 28000);   /* ## default port */
+-		if ( server->master_pkt_len >= master_pkt_max)  {
+-		    master_pkt_max+= 20*6;
+-		    server->master_pkt= (char*) realloc( server->master_pkt,
+-			master_pkt_max);
+-		}
+-		memcpy( server->master_pkt + server->master_pkt_len,
+-			&ipaddr, 4);
+-		memcpy( server->master_pkt + server->master_pkt_len + 4,
+-			&port, 2);
+-		server->master_pkt_len+= 6;
+-	    }
+-	    else
+-		for ( ; len && *data != '\\'; data++, len--) ;
+-	}
+-
+-	server->n_servers= server->master_pkt_len / 6;
+-	server->next_player_info= -1;
+-	server->retry1= 0;
++		server->server_name = GAMESPY_MASTER_NAME;
+ 
+-	cleanup_qserver( server, 1);
+-	return;
+-    }
++		master_pkt_max = (len / 20) *6;
++		server->master_pkt = (char*)malloc(master_pkt_max);
++		server->master_pkt_len = 0;
++
++		while (len)
++		{
++			for (; len && *data == '\\'; data++, len--)
++				;
++			if (len < 3)
++			{
++				break;
++			}
++			if (data[0] == 'i' && data[1] == 'p' && data[2] == '\\')
++			{
++				data += 3;
++				len -= 3;
++				ip = data;
++				portstr = NULL;
++				for (; len && *data != '\\'; data++, len--)
++				{
++					if (*data == ':')
++					{
++						portstr = data + 1;
++						*data = '\0';
++					}
++				}
++				if (len == 0)
++				{
++					break;
++				}
++				*data++ = '\0';
++				len--;
++				ipaddr = inet_addr(ip);
++				if (portstr)
++				{
++					port = htons((unsigned short)atoi(portstr));
++				}
++				else
++				{
++					port = htons(28000);
++				}
++				/* ## default port */
++				if (server->master_pkt_len >= master_pkt_max)
++				{
++					master_pkt_max += 20 * 6;
++					server->master_pkt = (char*)realloc(server->master_pkt, master_pkt_max);
++				}
++				memcpy(server->master_pkt + server->master_pkt_len, &ipaddr, 4);
++				memcpy(server->master_pkt + server->master_pkt_len + 4, &port, 2);
++				server->master_pkt_len += 6;
++			}
++			else
++			{
++				for (; len && *data != '\\'; data++, len--)
++					;
++			}
++		}
++
++		server->n_servers = server->master_pkt_len / 6;
++		server->next_player_info = - 1;
++		server->retry1 = 0;
+ 
+-    if (! server->saved_data.data)
+-	server->saved_data.data= (char*)malloc( pktlen);
+-    else
+-	server->saved_data.data= (char*)realloc( server->saved_data.data,
+-		server->saved_data.datalen + pktlen);
++		return DONE_FORCE;
++	}
++
++	if (!server->saved_data.data)
++	{
++		server->saved_data.data = (char*)malloc(pktlen);
++	}
++	else
++	{
++		server->saved_data.data = (char*)realloc(server->saved_data.data, server->saved_data.datalen + pktlen);
++	}
+ 
+-    memcpy( server->saved_data.data + server->saved_data.datalen, rawpkt, pktlen);
+-    server->saved_data.datalen+= pktlen;
++	memcpy(server->saved_data.data + server->saved_data.datalen, rawpkt, pktlen);
++	server->saved_data.datalen += pktlen;
+ 
++	return INPROGRESS;
+ }
+ 
+ /* Misc utility functions
+  */
+ 
+-char *
+-strndup( const char *string, size_t len)
++char *strndup(const char *string, size_t len)
+ {
+-    char *result;
+-    result= (char*) malloc( len+1);
+-    memcpy( result, string, len);
+-    result[len]= '\0';
+-    return result;
++	char *result;
++	result = (char*)malloc(len + 1);
++	memcpy(result, string, len);
++	result[len] = '\0';
++	return result;
+ }
+ 
+-unsigned int
+-swap_long( void *l)
++unsigned int swap_long(void *l)
+ {
+-    unsigned char *b= (unsigned char *) l;
+-    return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
++	unsigned char *b = (unsigned char*)l;
++	return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
+ }
+ 
+-unsigned short
+-swap_short( void *l)
++unsigned short swap_short(void *l)
+ {
+-    unsigned char *b= (unsigned char *) l;
+-    return (unsigned short)b[0] | (b[1]<<8);
++	unsigned char *b = (unsigned char*)l;
++	return (unsigned short)b[0] | (b[1] << 8);
+ }
+ 
+-unsigned int
+-swap_long_from_little( void *l)
++unsigned int swap_long_from_little(void *l)
+ {
+-    unsigned char *b= (unsigned char *) l;
+-    unsigned int result;
+-    if ( little_endian)
+-	memcpy( &result, l, 4);
+-    else
+-	result= (unsigned int)b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+-    return result;
++	unsigned char *b = (unsigned char*)l;
++	unsigned int result;
++	if (little_endian)
++	{
++		memcpy(&result, l, 4);
++	}
++	else
++	{
++		result = (unsigned int)b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24);
++	}
++	return result;
+ }
+ 
+-float
+-swap_float_from_little( void *f )
++float swap_float_from_little(void *f)
+ {
+-	union {
++	union
++	{
+ 		int i;
+ 		float fl;
+ 	} temp;
+ 
+ 	temp.i = swap_long_from_little(f);
+ 	return temp.fl;
+-};
++}
+ 
+-unsigned short
+-swap_short_from_little( void *l)
++unsigned short swap_short_from_little(void *l)
+ {
+-    unsigned char *b= (unsigned char *) l;
+-    unsigned short result;
+-    if ( little_endian)
+-	memcpy( &result, l, 2);
+-    else
+-	result= (unsigned short)b[0] | ((unsigned short)b[1]<<8);
+-    return result;
++	unsigned char *b = (unsigned char*)l;
++	unsigned short result;
++	if (little_endian)
++	{
++		memcpy(&result, l, 2);
++	}
++	else
++	{
++		result = (unsigned short)b[0] | ((unsigned short)b[1] << 8);
++	}
++	return result;
+ }
+ 
+ /** write four byte to buf */
+-void put_long_little(unsigned val, char* buf)
++void put_long_little(unsigned val, char *buf)
+ {
+-	buf[0] = val & 0xFF;
+-	buf[1] = (val >> 8) & 0xFF;
+-	buf[2] = (val >> 16) & 0xFF;
+-	buf[3] = (val >> 24) & 0xFF;
++	buf[0] = val &0xFF;
++	buf[1] = (val >> 8) &0xFF;
++	buf[2] = (val >> 16) &0xFF;
++	buf[3] = (val >> 24) &0xFF;
+ }
+ 
+ #define MAXSTRLEN 2048
+ 
+-char *
+-xml_escape( char *string)
++char *xml_escape(char *string)
+ {
+-    static char _buf[4][MAXSTRLEN+8];
+-    static int _buf_index= 0;
+-    char *result, *b, *end;
+-    unsigned char c;
++	static unsigned char _buf[4][MAXSTRLEN + 8];
++	static int _buf_index = 0;
++	unsigned char *result, *b, *end;
++	unsigned int c;
+ 
+-    if ( string == NULL)
+-	return "";
++	if (string == NULL)
++	{
++		return "";
++	}
+ 
+-    result= &_buf[_buf_index][0];
+-    _buf_index= (_buf_index+1) % 4;
++	result = &_buf[_buf_index][0];
++	_buf_index = (_buf_index + 1) % 4;
+ 
+-    end= &result[MAXSTRLEN];
++	end = &result[MAXSTRLEN];
+ 
+-    b= result;
+-    for ( ; *string && b < end; string++)  {
+-	c= *string;
+-	switch ( c)  {
+-	case '&':
+-	    *b++= '&';
+-	    *b++= 'a';
+-	    *b++= 'm';
+-	    *b++= 'p';
+-	    *b++= ';';
+-	    continue;
+-	case '<':
+-	    *b++= '&';
+-	    *b++= 'l';
+-	    *b++= 't';
+-	    *b++= ';';
+-	    continue;
+-	case '>':
+-	    *b++= '&';
+-	    *b++= 'g';
+-	    *b++= 't';
+-	    *b++= ';';
+-	    continue;
+-	default:
+-	    break;
+-	}
+-	if ( ! name_xforms)  {
+-	    *b++= c;
+-	}
+-	else if ( xml_encoding == ENCODING_LATIN_1)  {
+-	    if ( isprint(c))
+-		*b++= c;
+-	    else
+-		b+= sprintf( b, "&#%u;", c);
+-	}
+-	else if ( xml_encoding == ENCODING_UTF_8)  {
+-	    if ( (c & 0x80) == 0)
+-		*b++= c;
+-	    else  {
+-		*b++= 0xC0 | (0x03 & (c >> 6)) ;
+-		*b++= 0x80 | (0x3F & c) ;
+-	    }
++	b = result;
++	for (; *string && b < end; string++)
++	{
++		c = *string;
++		switch (c)
++		{
++			case '&':
++				*b++ = '&';
++				*b++ = 'a';
++				*b++ = 'm';
++				*b++ = 'p';
++				*b++ = ';';
++				continue;
++			case '\'':
++				*b++ = '&';
++				*b++ = 'a';
++				*b++ = 'p';
++				*b++ = 'o';
++				*b++ = 's';
++				*b++ = ';';
++				continue;
++			case '"':
++				*b++ = '&';
++				*b++ = 'q';
++				*b++ = 'u';
++				*b++ = 'o';
++				*b++ = 't';
++				*b++ = ';';
++				continue;
++			case '<':
++				*b++ = '&';
++				*b++ = 'l';
++				*b++ = 't';
++				*b++ = ';';
++				continue;
++			case '>':
++				*b++ = '&';
++				*b++ = 'g';
++				*b++ = 't';
++				*b++ = ';';
++				continue;
++			default:
++				break;
++		}
++
++		// Validate character
++		// http://www.w3.org/TR/2000/REC-xml-20001006#charsets
++		if ( !
++			(
++				0x09 == c ||
++				0xA == c ||
++				0xD == c ||
++				( 0x20 <= c && 0xD7FF >= c ) ||
++				( 0xE000 <= c && 0xFFFD >= c ) ||
++				( 0x10000 <= c && 0x10FFFF >= c )
++			)
++		)
++		{
++			if ( show_errors )
++			{
++				fprintf(stderr, "Encoding error (%d) for U+%x, D+%d\n", 1, c, c);
++			}
++		}
++		else if (xml_encoding == ENCODING_LATIN_1)
++		{
++			if (!name_xforms)
++			{
++				*b++ = c;
++			}
++			else
++			{
++				if (isprint(c))
++				{
++					*b++ = c;
++				}
++				else
++				{
++					b += sprintf( (char *)b, "&#%u;", c);
++				}
++			}
++		}
++		else if (xml_encoding == ENCODING_UTF_8)
++		{
++			unsigned char tempbuf[10] =
++			{
++				0
++			};
++			unsigned char *buf = &tempbuf[0];
++			int bytes = 0;
++			int error = 1;
++
++			// Valid character ranges
++			if (
++				0x09 == c ||
++				0xA == c ||
++				0xD == c ||
++				( 0x20 <= c && 0xD7FF >= c ) ||
++				( 0xE000 <= c && 0xFFFD >= c ) ||
++				( 0x10000 <= c && 0x10FFFF >= c )
++			)
++			{
++				error = 0;
++			}
++
++			if (c < 0x80)
++			/* 0XXX XXXX one byte */
++			{
++				buf[0] = c;
++				bytes = 1;
++			}
++			else if (c < 0x0800)
++			/* 110X XXXX two bytes */
++			{
++				buf[0] = 0xC0 | (0x03 &(c >> 6));
++				buf[1] = 0x80 | (0x3F &c);
++				bytes = 2;
++			}
++			else if (c < 0x10000)
++			/* 1110 XXXX three bytes */
++			{
++				buf[0] = 0xE0 | (c >> 12);
++				buf[1] = 0x80 | ((c >> 6) &0x3F);
++				buf[2] = 0x80 | (c &0x3F);
++
++				bytes = 3;
++				if (c == UTF8BYTESWAPNOTACHAR || c == UTF8NOTACHAR)
++				{
++					error = 3;
++				}
++
++			}
++			else if (c < 0x10FFFF)
++			/* 1111 0XXX four bytes */
++			{
++				buf[0] = 0xF0 | (c >> 18);
++				buf[1] = 0x80 | ((c >> 12) &0x3F);
++				buf[2] = 0x80 | ((c >> 6) &0x3F);
++				buf[3] = 0x80 | (c &0x3F);
++				bytes = 4;
++				if (c > UTF8MAXFROMUCS4)
++				{
++					error = 4;
++				}
++
++			}
++			else if (c < 0x4000000)
++			/* 1111 10XX five bytes */
++			{
++				buf[0] = 0xF8 | (c >> 24);
++				buf[1] = 0x80 | (c >> 18);
++				buf[2] = 0x80 | ((c >> 12) &0x3F);
++				buf[3] = 0x80 | ((c >> 6) &0x3F);
++				buf[4] = 0x80 | (c &0x3F);
++				bytes = 5;
++				error = 5;
++			}
++			else if (c < 0x80000000)
++			/* 1111 110X six bytes */
++			{
++				buf[0] = 0xFC | (c >> 30);
++				buf[1] = 0x80 | ((c >> 24) &0x3F);
++				buf[2] = 0x80 | ((c >> 18) &0x3F);
++				buf[3] = 0x80 | ((c >> 12) &0x3F);
++				buf[4] = 0x80 | ((c >> 6) &0x3F);
++				buf[5] = 0x80 | (c &0x3F);
++				bytes = 6;
++				error = 6;
++			}
++			else
++			{
++				error = 7;
++			}
++
++			if (error)
++			{
++				int i;
++				fprintf(stderr, "UTF-8 encoding error (%d) for U+%x, D+%d : ", error, c, c);
++				for (i = 0; i < bytes; i++)
++				{
++					fprintf(stderr, "0x%02x ", buf[i]);
++				}
++				fprintf(stderr, "\n");
++			}
++			else
++			{
++				int i;
++				for (i = 0; i < bytes; ++i)
++				{
++					*b++ = buf[i];
++				}
++			}
++
++		}
+ 	}
+-    }
+-    *b= '\0';
+-    return result;
++	*b = '\0';
++	return (char*)result;
+ }
+ 
+ 
+-static char *quake3_escape_colors[8] = {
+-"black", "red", "green", "yellow", "blue", "cyan", "magenta", "white"
++static char *quake3_escape_colors[8] =
++{
++	"black", "red", "green", "yellow", "blue", "cyan", "magenta", "white"
+ };
+ 
+-static char *sof_colors[32] = {
+-"FFFFFF","FFFFFF","FF0000","00FF00","FFFF00","0000FF","FF00FF","00FFFF",
+-"000000","7F7F7F","702D07","7F0000","007F00","FFFFFF","007F7F","00007F",
+-"564D28","4C5E36","370B65","005572","54647E","1E2A63","66097B","705E61",
+-"980053","960018","702D07","54492A","61A997","CB8F39","CF8316","FF8020"
++static char *sof_colors[32] =
++{
++	"FFFFFF", "FFFFFF", "FF0000", "00FF00", "FFFF00", "0000FF", "FF00FF", "00FFFF", "000000", "7F7F7F", "702D07", "7F0000", "007F00",
++	"FFFFFF", "007F7F", "00007F", "564D28", "4C5E36", "370B65", "005572", "54647E", "1E2A63", "66097B", "705E61", "980053",
++	"960018", "702D07", "54492A", "61A997", "CB8F39", "CF8316", "FF8020"
+ };
+ 
+ 
+-char *
+-xform_name( char *string, struct qserver *server)
++char *xform_name(char *string, struct qserver *server)
+ {
+-	static char _buf1[2048], _buf2[2048];
+-	static char *_q= &_buf1[0];
+-	unsigned char *s= (unsigned char*) string;
++	static char _buf1[2048], _buf2[2048], _buf3[2048], _buf4[2048];
++	static char *_q = &_buf1[0];
++	static char *_q2 = &_buf3[0];
++	unsigned char *s = (unsigned char*)string;
+ 	char *q;
+ 	int is_server_name = (string == server->server_name);
+-	int font_tag= 0;
++	int font_tag = 0;
++
++	_q = _q == _buf1 ? _buf2 : _buf1;
++	q = _q;
+ 
+-	_q= _q == _buf1 ? _buf2 : _buf1;
+-	q= _q;
++	if (s == NULL)
++	{
++		q[0] = '?';
++		q[1] = '\0';
++		return _q;
++	}
+ 
+-	if ( s == NULL)
++	strcpy(_q, string);
++	if (!name_xforms)
+ 	{
+-		q[0]= '?';
+-		q[1]= '\0';
+ 		return _q;
+ 	}
++	if (name_xforms_strip_unprintable)
++	{
++		// We're double processing so need to use the second set of buffers
++		_q2 = (_q2 == _buf3) ? _buf4 : _buf3;
++		q = _q2;
++		for (; *s; s++)
++		{
++			if (isprint(*s))
++			{
++				*q = *s;
++				q++;
++			}
++		}
++		*q = '\0';
++		s = (unsigned char *)_q2;
++		q = _q;
+ 
+-	if ( ! name_xforms)  {
+-		strcpy( _q, string);
+-		return _q;
++		if (*_q2 == '\0')
++		{
++			q[0] = '?';
++			q[1] = '\0';
++			return _q2;
++		}
+ 	}
+ 
+-	if ( (hex_player_names && !is_server_name) || (hex_server_names && is_server_name))
++	if ((hex_player_names && !is_server_name) || (hex_server_names && is_server_name))
+ 	{
+-		for ( ; *s; s++, q+= 2)
++		for (; *s; s++, q += 2)
+ 		{
+-			sprintf( q, "%02x", *s);
++			sprintf(q, "%02x", *s);
+ 		}
+-		*q= '\0';
++		*q = '\0';
+ 		return _q;
+ 	}
+-	if ( server->type->flags & TF_QUAKE3_NAMES)
++	if (server->type->flags &TF_QUAKE3_NAMES)
+ 	{
+-		for ( ; *s; s++)
++		for (; *s; s++)
+ 		{
+-			if ( *s == '^' && *(s+1) != '^')
++			if (*s == '^' && *(s + 1) != '^')
+ 			{
+-				if ( *(s+1) == '\0')
++				if (*(s + 1) == '\0')
+ 				{
+ 					break;
+ 				}
+-				if ( html_names == 1)
++				if (html_names == 1)
+ 				{
+-					q+= sprintf( q, "%s<font color=\"%s\">",
+-					font_tag?"</font>":"",
+-					quake3_escape_colors[ *(s+1) & 0x7]);
++					q += sprintf(q, "%s<font color=\"%s\">", font_tag ? "</font>" : "", quake3_escape_colors[*(s + 1) &0x7]);
+ 					s++;
+-					font_tag= 1;
++					font_tag = 1;
+ 				}
+-				else if ( strip_carets)
++				else if (strip_carets)
+ 				{
+ 					s++;
+ 				}
+ 				else
+ 				{
+-					*q++= *s;
++					*q++ = *s;
+ 				}
+ 			}
+ 			else
+ 			{
+-				int inc = html_entity( *s, q );
+-				if ( 0 != inc )
++				int inc = html_entity(*s, q);
++				if (0 != inc)
+ 				{
+ 					q += inc;
+ 				}
+-				else if ( isprint(*s))
+-				{
+-					*q++= *s;
+-				}
+-				else if ( *s == '\033')
++				else if (isprint(*s))
+ 				{
++					*q++ = *s;
+ 				}
+-				else if ( *s == 0x80)
++				else if (*s == '\033'){}
++				else if (*s == 0x80)
+ 				{
+-					*q++= '(';
++					*q++ = '(';
+ 				}
+-				else if ( *s == 0x81)
++				else if (*s == 0x81)
+ 				{
+-					*q++= '=';
++					*q++ = '=';
+ 				}
+-				else if ( *s == 0x82)
++				else if (*s == 0x82)
+ 				{
+-					*q++= ')';
++					*q++ = ')';
+ 				}
+-				else if ( *s == 0x10 || *s == 0x90)
++				else if (*s == 0x10 || *s == 0x90)
+ 				{
+-					*q++= '[';
++					*q++ = '[';
+ 				}
+-				else if ( *s == 0x11 || *s == 0x91)
++				else if (*s == 0x11 || *s == 0x91)
+ 				{
+-					*q++= ']';
++					*q++ = ']';
+ 				}
+-				else if ( *s >= 0x92 && *s <= 0x9a)
++				else if (*s >= 0x92 && *s <= 0x9a)
+ 				{
+-					*q++= *s - 98;
++					*q++ = *s - 98;
+ 				}
+-				else if ( *s >= 0xa0 && *s <= 0xe0)
++				else if (*s >= 0xa0 && *s <= 0xe0)
+ 				{
+-					*q++= *s - 128;
++					*q++ = *s - 128;
+ 				}
+-				else if ( *s >= 0xe1 && *s <= 0xfa)
++				else if (*s >= 0xe1 && *s <= 0xfa)
+ 				{
+-					*q++= *s - 160;
++					*q++ = *s - 160;
+ 				}
+-				else if ( *s >= 0xfb && *s <= 0xfe)
++				else if (*s >= 0xfb && *s <= 0xfe)
+ 				{
+-					*q++= *s - 128;
++					*q++ = *s - 128;
+ 				}
+ 			}
+ 		}
+-		*q= '\0';
++		*q = '\0';
+ 	}
+-	else if ( !is_server_name && (server->type->flags & TF_TRIBES2_NAMES))
++	else if (!is_server_name && (server->type->flags &TF_TRIBES2_NAMES))
+ 	{
+-		for ( ; *s; s++)
++		for (; *s; s++)
+ 		{
+-			int inc = html_entity( *s, q );
+-			if ( 0 != inc )
++			int inc = html_entity(*s, q);
++			if (0 != inc)
+ 			{
+ 				q += inc;
+ 				continue;
+ 			}
+-			else if ( isprint(*s))
++			else if (isprint(*s))
+ 			{
+-				*q++= *s;
++				*q++ = *s;
+ 				continue;
+ 			}
+-			if ( html_names == 1 && s[1] != '\0')
++			if (html_names == 1 && s[1] != '\0')
+ 			{
+ 				char *font_color;
+-				switch( *s)
++				switch (*s)
+ 				{
+-				case 0x8: font_color= "white"; break;	/* normal */
+-				case 0xb: font_color= "yellow"; break;	/* tribe tag */
+-				case 0xc: font_color= "blue"; break;	/* alias */
+-				case 0xe: font_color= "green"; break;	/* bot */
+-				default: font_color= NULL;
++					case 0x8:
++						font_color = "white";
++						break; /* normal */
++					case 0xb:
++						font_color = "yellow";
++						break; /* tribe tag */
++					case 0xc:
++						font_color = "blue";
++						break; /* alias */
++					case 0xe:
++						font_color = "green";
++						break; /* bot */
++					default:
++						font_color = NULL;
+ 				}
+-				if ( font_color)
++				if (font_color)
+ 				{
+-					q+= sprintf( q, "%s<font color=\"%s\">",
+-					font_tag?"</font>":"",
+-					font_color);
+-					font_tag= 1;
++					q += sprintf(q, "%s<font color=\"%s\">", font_tag ? "</font>" : "", font_color);
++					font_tag = 1;
+ 				}
+ 			}
+ 		}
+-		*q= '\0';
++		*q = '\0';
+ 	}
+-	else if ( server->type->flags & TF_U2_NAMES )
++	else if (server->type->flags &TF_U2_NAMES)
+ 	{
+-		for ( ; *s; s++)
++		for (; *s; s++)
+ 		{
+-			if ( 0 == memcmp( s, "^\1", 2 ) )
++			if (0 == memcmp(s, "^\1", 2))
+ 			{
+ 				// xmp Color follows
+ 				s += 2;
+-				q += u2xmp_html_color( (unsigned char)*s, q, &font_tag );
++				q += u2xmp_html_color((unsigned char) *s, q, &font_tag);
+ 			}
+-			else if ( 0 == memcmp( s, "\x1b", 1 ) )
++			else if (0 == memcmp(s, "\x1b", 1))
+ 			{
+ 				// Color follows
+ 				s += 1;
+-				q += ut2k4_html_color( s, q, &font_tag );
++				q += ut2k4_html_color(s, q, &font_tag);
+ 				s += 2;
+ 			}
+ 			else
+ 			{
+-				int inc = html_entity( *s, q );
+-				if ( 0 != inc )
++				int inc = html_entity(*s, q);
++				if (0 != inc)
+ 				{
+ 					q += inc;
+ 				}
+-				else if ( isprint(*s))
++				else if (isprint(*s))
+ 				{
+-					*q++= *s;
++					*q++ = *s;
+ 				}
+-				else if ( 0xa0 == *s )
++				else if (0xa0 == *s)
+ 				{
+-					*q++= ' ';
++					*q++ = ' ';
+ 				}
+ 			}
+ 		}
+ 		*q = '\0';
+ 	}
+-	else if ( server->type->flags & TF_TM_NAMES )
++	else if (server->type->flags &TF_TM_NAMES)
+ 	{
+ 		int open = 0;
+-		for ( ; *s; s++)
++		char c1, c2, c3;
++
++		for (; *s; s++)
+ 		{
+-			if ( *s == '$' )
++			if (*s == '$')
+ 			{
+ 				s++;
+-				switch ( *s )
++				switch (*s)
+ 				{
+ 				case 'i':
+ 				case 'I':
+ 					// italic
+-					if ( 1 == html_names )
++					if (1 == html_names)
+ 					{
+-						strcat( q, "<span style=\"font-style:italic\">" );
++						strcat(q, "<span style=\"font-style:italic\">");
+ 						q += 32;
+ 						open++;
+ 					}
+@@ -10894,37 +12641,49 @@
+ 				case 'm':
+ 				case 'M':
+ 					// normal
+-					if ( 1 == html_names )
++					if (1 == html_names)
+ 					{
+-						strcat( q, "<span style=\"font-style:normal\">" );
++						strcat(q, "<span style=\"font-style:normal\">");
+ 						q += 32;
+ 						open++;
+ 					}
+ 					break;
++				case 'o':
++				case 'O':
++					// bold
++					if (1 == html_names)
++					{
++						strcat(q, "<span style=\"font-style:bold\">");
++						q += 30;
++						open++;
++					}
++					break;
+ 				case 'g':
+ 				case 'G':
+ 					// default color
+-					strcat( q, "<span style=\"color:black\">" );
+-					q += 26;
+-					open++;
++					if (1 == html_names)
++					{
++						strcat(q, "<span style=\"color:black\">");
++						q += 26;
++						open++;
++					}
+ 					break;
+ 				case 'z':
+ 				case 'Z':
+ 					// reset all
+-					while ( open )
++					while (open)
+ 					{
+-						strcat( q, "</span>" );
++						strcat(q, "</span>");
+ 						q += 7;
+ 						open--;
+ 					}
+-					open = 0;
+ 					break;
+ 				case 't':
+ 				case 'T':
+ 					// capitalise
+-					if ( 1 == html_names )
++					if (1 == html_names)
+ 					{
+-						strcat( q, "<span style=\"text-transform:capitalize\">" );
++						strcat(q, "<span style=\"text-transform:capitalize\">");
+ 						q += 40;
+ 						open++;
+ 					}
+@@ -10933,21 +12692,25 @@
+ 					// literal $
+ 					*q++ = '$';
+ 					break;
++				case '\0':
++					// Unexpected end
++					break;
+ 				default:
+ 					// color
+-					if ( 1 == html_names )
+-					{
+-						sprintf( q, "<span style=\"color:%c%c%c%c%c%c\">", *s, *s, *(s+1), *(s+1), *(s+2), *(s+2) );
+-						q += 27;
+-						open++;
+-					}
+-					if ( *(s+1) )
+-					{
+-						s++;
+-					}
+-					if ( *(s+1) )
++					c3 = '\0';
++					c1 = *s;
++					s++;
++					c2 = *s;
++					if (c2)
+ 					{
+ 						s++;
++						c3 = *s;
++						if (c3 && 1 == html_names)
++						{
++							sprintf(q, "<span style=\"color:#%c%c%c%c%c%c\">", c1, c1, c2, c2, c3, c3);
++							q += 28;
++							open++;
++						}
+ 					}
+ 					break;
+ 				}
+@@ -10958,89 +12721,80 @@
+ 			}
+ 		}
+ 
+-		while ( open )
++		while (open)
+ 		{
+-			strcat( q, "</span>" );
++			strcat(q, "</span>");
+ 			q += 7;
+ 			open--;
+ 		}
+ 		*q = '\0';
+ 	}
+-	else if ( !is_server_name || server->type->flags & TF_SOF_NAMES )
++	else if (!is_server_name || server->type->flags &TF_SOF_NAMES)
+ 	{
+ 		// Catch all for NOT is_server_name OR TF_SOF_NAMES
+ 		// The may not be the intention but is needed for q1 at least
+-		for ( ; *s; s++)
++		for (; *s; s++)
+ 		{
+-			int inc = html_entity( *s, q );
+-			if ( 0 != inc )
++			int inc = html_entity(*s, q);
++			if (0 != inc)
+ 			{
+ 				q += inc;
+ 				continue;
+ 			}
+ 
+-			if ( *s < ' ' )
++			if (*s < ' ')
+ 			{
+-				if ( html_names == 1)
++				if (html_names == 1)
+ 				{
+-					q+= sprintf( q, "%s<font color=\"#%s\">",
+-					font_tag?"</font>":"",
+-					sof_colors[ *(s) ]);
+-					font_tag= 1;
++					q += sprintf(q, "%s<font color=\"#%s\">", font_tag ? "</font>" : "", sof_colors[*(s)]);
++					font_tag = 1;
+ 				}
+ 			}
+-			else if ( isprint(*s))
++			else if (isprint(*s))
+ 			{
+-				*q++= *s;
++				*q++ = *s;
+ 			}
+ 			// ## more fixes below; double check against real sof servers
+-			else if ( *s >= 0xa0)
++			else if (*s >= 0xa0)
+ 			{
+-				*q++= *s & 0x7f;
++				*q++ = *s &0x7f;
+ 			}
+-			else if ( *s >= 0x92 && *s < 0x9c)
++			else if (*s >= 0x92 && *s < 0x9c)
+ 			{
+-				*q++= '0' + (*s - 0x92);
++				*q++ = '0' + (*s - 0x92);
+ 			}
+-			else if ( *s >= 0x12 && *s < 0x1c)
++			else if (*s >= 0x12 && *s < 0x1c)
+ 			{
+-				*q++= '0' + (*s - 0x12);
++				*q++ = '0' + (*s - 0x12);
+ 			}
+-			else if ( *s == 0x90 || *s == 0x10)
++			else if (*s == 0x90 || *s == 0x10)
+ 			{
+-				*q++= '[';
++				*q++ = '[';
+ 			}
+-			else if ( *s == 0x91 || *s == 0x11)
++			else if (*s == 0x91 || *s == 0x11)
+ 			{
+-				*q++= ']';
++				*q++ = ']';
+ 			}
+-			else if ( *s == 0xa || *s == 0xc || *s == 0xd)
++			else if (*s == 0xa || *s == 0xc || *s == 0xd)
+ 			{
+-				*q++= ']';
++				*q++ = ']';
+ 			}
+ 		}
+-		*q= '\0';
+-	}
+-	else
+-	{
+-		strcpy( _q, string);
++		*q = '\0';
+ 	}
+ 
+-	if ( font_tag )
++	if (font_tag)
+ 	{
+-		q+= sprintf( q, "</font>");
++		q += sprintf(q, "</font>");
+ 	}
+ 
+ 	return _q;
+ }
+-
+-int u2xmp_html_color( short color, char *dest, int *font_tag )
++int u2xmp_html_color(short color, char *dest, int *font_tag)
+ {
+-	if ( 1 == html_names )
++	if (1 == html_names)
+ 	{
+-		int len = sprintf( dest, "%s<font color=\"%s\">",
+-			*font_tag ? "</font>" : "", unreal_rgb_colors[ color - 1 ]
+-		);
++		int len = sprintf(dest, "%s<font color=\"%s\">", *font_tag ? "</font>" : "", unreal_rgb_colors[color - 1]);
+ 		*font_tag = 1;
+ 
+ 		return len;
+@@ -11048,16 +12802,11 @@
+ 	return 0;
+ }
+ 
+-int ut2k4_html_color( const unsigned char *color, char *dest, int *font_tag )
++int ut2k4_html_color(const unsigned char *color, char *dest, int *font_tag)
+ {
+-	if ( 1 == html_names )
++	if (1 == html_names)
+ 	{
+-		int len = sprintf( dest, "%s<font color=\"#%02hhx%02hhx%02hhx\">",
+-			*font_tag ? "</font>" : "",
+-			color[0],
+-			color[1],
+-			color[2]
+-		);
++		int len = sprintf(dest, "%s<font color=\"#%02hhx%02hhx%02hhx\">", *font_tag ? "</font>" : "", color[0], color[1], color[2]);
+ 		*font_tag = 1;
+ 
+ 		return len;
+@@ -11065,188 +12814,225 @@
+ 	return 0;
+ }
+ 
+-int
+-is_default_rule( struct rule *rule)
++int is_default_rule(struct rule *rule)
+ {
+-    if ( strcmp( rule->name, "sv_maxspeed") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_SV_MAXSPEED) == 0;
+-    if ( strcmp( rule->name, "sv_friction") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_SV_FRICTION) == 0;
+-    if ( strcmp( rule->name, "sv_gravity") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_SV_GRAVITY) == 0;
+-    if ( strcmp( rule->name, "noexit") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_NOEXIT) == 0;
+-    if ( strcmp( rule->name, "teamplay") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_TEAMPLAY) == 0;
+-    if ( strcmp( rule->name, "timelimit") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_TIMELIMIT) == 0;
+-    if ( strcmp( rule->name, "fraglimit") == 0)
+-	return strcmp( rule->value, Q_DEFAULT_FRAGLIMIT) == 0;
+-    return 0;
+-}
+-
+-int html_entity( const char c, char *dest )
+-{
+-	if ( html_mode )
+-	{
+-		switch( c )
+-		{
+-		case '<':
+-			strcpy( dest, "&lt;" );
+-			return 4;
+-		case '>':
+-			strcpy( dest, "&gt;" );
+-			return 4;
+-		case '&':
+-			strcpy( dest, "&amp;" );
+-			return 5;
+-		default:
+-			break;
++	if (strcmp(rule->name, "sv_maxspeed") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_SV_MAXSPEED) == 0;
++	} if (strcmp(rule->name, "sv_friction") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_SV_FRICTION) == 0;
++	}
++	if (strcmp(rule->name, "sv_gravity") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_SV_GRAVITY) == 0;
++	}
++	if (strcmp(rule->name, "noexit") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_NOEXIT) == 0;
++	}
++	if (strcmp(rule->name, "teamplay") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_TEAMPLAY) == 0;
++	}
++	if (strcmp(rule->name, "timelimit") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_TIMELIMIT) == 0;
++	}
++	if (strcmp(rule->name, "fraglimit") == 0)
++	{
++		return strcmp(rule->value, Q_DEFAULT_FRAGLIMIT) == 0;
++	}
++	return 0;
++}
++
++int html_entity(const char c, char *dest)
++{
++	if (html_mode)
++	{
++		switch (c)
++		{
++			case '<':
++				strcpy(dest, "&lt;");
++				return 4;
++			case '>':
++				strcpy(dest, "&gt;");
++				return 4;
++			case '&':
++				strcpy(dest, "&amp;");
++				return 5;
++			default:
++				break;
+ 		}
+ 	}
+ 
+ 	return 0;
+ }
+ 
+-char *
+-strherror( int h_err)
++char *strherror(int h_err)
+ {
+-    static char msg[100];
+-    switch (h_err)  {
+-    case HOST_NOT_FOUND:	return "host not found";
+-    case TRY_AGAIN:		return "try again";
+-    case NO_RECOVERY:		return "no recovery";
+-    case NO_ADDRESS:		return "no address";
+-    default:	sprintf( msg, "%d", h_err); return msg;
+-    }
++	static char msg[100];
++	switch (h_err)
++	{
++		case HOST_NOT_FOUND:
++			return "host not found";
++		case TRY_AGAIN:
++			return "try again";
++		case NO_RECOVERY:
++			return "no recovery";
++		case NO_ADDRESS:
++			return "no address";
++		default:
++			sprintf(msg, "%d", h_err);
++			return msg;
++	}
+ }
+ 
+-int
+-time_delta( struct timeval *later, struct timeval *past)
++int time_delta(struct timeval *later, struct timeval *past)
+ {
+-    if ( later->tv_usec < past->tv_usec)  {
+-	later->tv_sec--;
+-	later->tv_usec+= 1000000;
+-    }
+-    return (later->tv_sec - past->tv_sec) * 1000 +
+-	(later->tv_usec - past->tv_usec) / 1000;
++	if (later->tv_usec < past->tv_usec)
++	{
++		later->tv_sec--;
++		later->tv_usec += 1000000;
++	}
++	return (later->tv_sec - past->tv_sec) *1000+(later->tv_usec - past->tv_usec) / 1000;
++}
++
++int connection_inprogress()
++{
++#ifdef _WIN32
++	return WSAGetLastError() == WSAEWOULDBLOCK;
++#else
++	return errno == EINPROGRESS;
++#endif
++}
++
++int connection_refused()
++{
++#ifdef _WIN32
++	return WSAGetLastError() == WSAECONNABORTED;
++#else
++	return errno == ECONNREFUSED;
++#endif
++}
++
++int connection_would_block()
++{
++#ifdef _WIN32
++	return WSAGetLastError() == WSAEWOULDBLOCK;
++#else
++	return errno == EAGAIN;
++#endif
++}
++
++int connection_reset()
++{
++#ifdef _WIN32
++	return WSAGetLastError() == WSAECONNRESET;
++#else
++	return errno == ECONNRESET;
++#endif
+ }
+ 
+-int
+-connection_refused()
++void clear_socketerror()
+ {
+ #ifdef _WIN32
+-    return WSAGetLastError() == WSAECONNABORTED;
++	WSASetLastError(0);
+ #else
+-    return errno == ECONNREFUSED;
++	errno = 0;
+ #endif
+ }
+ 
+-void
+-set_non_blocking( int fd)
++void set_non_blocking(int fd)
+ {
+ #ifdef _WIN32
+-    int one= 1;
+-    ioctlsocket( fd, FIONBIO, (unsigned long*)&one);
++	int one = 1;
++	ioctlsocket(fd, FIONBIO, (unsigned long*) &one);
+ #else
+ #ifdef O_NONBLOCK
+-    fcntl( fd, F_SETFL, O_NONBLOCK);
++	fcntl(fd, F_SETFL, O_NONBLOCK);
+ #else
+-    fcntl( fd, F_SETFL, O_NDELAY);
++	fcntl(fd, F_SETFL, O_NDELAY);
+ #endif // O_NONBLOCK
+ #endif // _WIN32
+ }
+ 
+-char *
+-quake_color( int color)
++char *quake_color(int color)
+ {
+-    static char *colors[] = {
+-	"White",	/* 0 */
+-	"Brown",	/* 1 */
+-	"Lavender",	/* 2 */
+-	"Khaki",	/* 3 */
+-	"Red",		/* 4 */
+-	"Lt Brown",	/* 5 */
+-	"Peach",	/* 6 */
+-	"Lt Peach",	/* 7 */
+-	"Purple",	/* 8 */
+-	"Dk Purple",	/* 9 */
+-	"Tan",		/* 10 */
+-	"Green",	/* 11 */
+-	"Yellow",	/* 12 */
+-	"Blue",		/* 13 */
+-	"Blue",		/* 14 */
+-	"Blue"		/* 15 */
+-    };
++	static char *colors[] =
++	{
++		"White",	/* 0 */
++		"Brown",	/* 1 */
++		"Lavender",	/* 2 */
++		"Khaki",	/* 3 */
++		"Red",		/* 4 */
++		"Lt Brown",	/* 5 */
++		"Peach",	/* 6 */
++		"Lt Peach",	/* 7 */
++		"Purple",	/* 8 */
++		"Dk Purple",/* 9 */
++		"Tan",		/* 10 */
++		"Green",	/* 11 */
++		"Yellow",	/* 12 */
++		"Blue",		/* 13 */
++		"Blue",		/* 14 */
++		"Blue"		/* 15 */
++	};
+ 
+-    static char *rgb_colors[] = {
+-	"#ffffff",	/* 0 */
+-	"#8b4513",	/* 1 */
+-	"#e6e6fa",	/* 2 */
+-	"#f0e68c",	/* 3 */
+-	"#ff0000",	/* 4 */
+-	"#deb887",	/* 5 */
+-	"#eecbad",	/* 6 */
+-	"#ffdab9",	/* 7 */
+-	"#9370db",	/* 8 */
+-	"#5d478b",	/* 9 */
+-	"#d2b48c",	/* 10 */
+-	"#00ff00",	/* 11 */
+-	"#ffff00",	/* 12 */
+-	"#0000ff",	/* 13 */
+-	"#0000ff",	/* 14 */
+-	"#0000ff"	/* 15 */
+-    };
++	static char *rgb_colors[] =
++	{
++		"#ffffff",  /* 0 */
++		"#8b4513",  /* 1 */
++		"#e6e6fa",  /* 2 */
++		"#f0e68c",  /* 3 */
++		"#ff0000",  /* 4 */
++		"#deb887",  /* 5 */
++		"#eecbad",  /* 6 */
++		"#ffdab9",  /* 7 */
++		"#9370db",  /* 8 */
++		"#5d478b",  /* 9 */
++		"#d2b48c",  /* 10 */
++		"#00ff00",  /* 11 */
++		"#ffff00",  /* 12 */
++		"#0000ff",  /* 13 */
++		"#0000ff",  /* 14 */
++		"#0000ff"   /* 15 */
++	};
+ 
+-    static char *color_nr[] = {
+-	"0",
+-	"1",
+-	"2",
+-	"3",
+-	"4",
+-	"5",
+-	"6",
+-	"7",
+-	"8",
+-	"9",
+-	"10",
+-	"11",
+-	"12",
+-	"13",
+-	"14",
+-	"15"
+-    };
++	static char *color_nr[] =
++	{
++		"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"
++	};
+ 
+-	if ( color_names )
++	if (color_names)
+ 	{
+-		if ( color_names == 1)
++		if (color_names == 1)
+ 		{
+-			return colors[color&0xf];
++			return colors[color &0xf];
+ 		}
+ 		else
+ 		{
+-			return rgb_colors[color&0xf];
++			return rgb_colors[color &0xf];
+ 		}
+ 	}
+ 	else
+ 	{
+-		return color_nr[color&0xf];
++		return color_nr[color &0xf];
+ 	}
+ }
+ 
+ 
+-const char *
+-unreal_color( int color )
++const char *unreal_color(int color)
+ {
+-	if ( color_names )
++	if (color_names)
+ 	{
+-		if ( color_names == 1)
++		if (color_names == 1)
+ 		{
+-			return unreal_colors[color&0xf];
++			return unreal_colors[color &0xf];
+ 		}
+ 		else
+ 		{
+-			return unreal_rgb_colors[color&0xf];
++			return unreal_rgb_colors[color &0xf];
+ 		}
+ 	}
+ 
+@@ -11260,296 +13046,368 @@
+ #define FMT_SECOND_1	"%2ds"
+ #define FMT_SECOND_2	"%ds"
+ 
+-char *
+-play_time( int seconds, int show_seconds)
++char *play_time(int seconds, int show_seconds)
+ {
+-    static char time_string[24];
+-    if ( time_format == CLOCK_TIME)  {
+-	char *fmt_hour= show_seconds==2 ? FMT_HOUR_2 : FMT_HOUR_1;
+-	char *fmt_minute= show_seconds==2 ? FMT_MINUTE_2 : FMT_MINUTE_1;
+-	char *fmt_second= show_seconds==2 ? FMT_SECOND_2 : FMT_SECOND_1;
+-	time_string[0]= '\0';
+-	if ( seconds/3600)
+-	    sprintf( time_string, fmt_hour, seconds/3600);
+-	else if ( show_seconds < 2)
+-	    strcat( time_string, "   ");
+-	if ( (seconds%3600)/60 || seconds/3600)
+-	    sprintf( time_string+strlen(time_string), fmt_minute,
+-		(seconds%3600)/60);
+-	else if ( ! show_seconds)
+-	    sprintf( time_string+strlen(time_string), " 0m");
+-	else if ( show_seconds < 2)
+-	    strcat( time_string, "   ");
+-	if ( show_seconds)
+-	    sprintf( time_string+strlen(time_string), fmt_second, seconds%60);
+-    }
+-    else if ( time_format == STOPWATCH_TIME)  {
+-	if ( show_seconds)
+-	    sprintf( time_string, "%02d:%02d:%02d", seconds/3600,
+-		(seconds%3600)/60, seconds % 60);
++	static char time_string[24];
++	if (time_format == CLOCK_TIME)
++	{
++		char *fmt_hour = show_seconds == 2 ? FMT_HOUR_2 : FMT_HOUR_1;
++		char *fmt_minute = show_seconds == 2 ? FMT_MINUTE_2 : FMT_MINUTE_1;
++		char *fmt_second = show_seconds == 2 ? FMT_SECOND_2 : FMT_SECOND_1;
++		time_string[0] = '\0';
++		if (seconds / 3600)
++		{
++			sprintf(time_string, fmt_hour, seconds / 3600);
++		}
++		else if (show_seconds < 2)
++		{
++			strcat(time_string, "   ");
++		}
++		if ((seconds % 3600) / 60 || seconds / 3600)
++		{
++			sprintf(time_string + strlen(time_string), fmt_minute, (seconds % 3600) / 60);
++		}
++		else if (!show_seconds)
++		{
++			sprintf(time_string + strlen(time_string), " 0m");
++		}
++		else if (show_seconds < 2)
++		{
++			strcat(time_string, "   ");
++		}
++		if (show_seconds)
++		{
++			sprintf(time_string + strlen(time_string), fmt_second, seconds % 60);
++		}
++	}
++	else if (time_format == STOPWATCH_TIME)
++	{
++		if (show_seconds)
++		{
++			sprintf(time_string, "%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, seconds % 60);
++		}
++		else
++		{
++			sprintf(time_string, "%02d:%02d", seconds / 3600, (seconds % 3600) / 60);
++		}
++	}
+ 	else
+-	    sprintf( time_string, "%02d:%02d", seconds/3600,
+-		(seconds%3600)/60);
+-    }
+-    else
+-	sprintf( time_string, "%d", seconds);
++	{
++		sprintf(time_string, "%d", seconds);
++	}
+ 
+-    return time_string;
++	return time_string;
+ }
+ 
+-char *
+-ping_time( int ms)
++char *ping_time(int ms)
+ {
+-    static char time_string[24];
+-    if ( ms < 1000)
+-	sprintf( time_string, "%dms", ms);
+-    else if ( ms < 1000000)
+-	sprintf( time_string, "%ds", ms/1000);
+-    else
+-	sprintf( time_string, "%dm", ms/1000/60);
+-    return time_string;
++	static char time_string[24];
++	if (ms < 1000)
++	{
++		sprintf(time_string, "%dms", ms);
++	}
++	else if (ms < 1000000)
++	{
++		sprintf(time_string, "%ds", ms / 1000);
++	}
++	else
++	{
++		sprintf(time_string, "%dm", ms / 1000 / 60);
++	}
++	return time_string;
+ }
+ 
+-int
+-count_bits( int n)
++int count_bits(int n)
+ {
+-    int b= 0;
+-    for ( ; n; n>>=1)
+-	if ( n&1)
+-	    b++;
+-    return b;
++	int b = 0;
++	for (; n; n >>= 1)
++	if (n &1)
++	{
++		b++;
++	}
++	return b;
+ }
+ 
+-int
+-strcmp_withnull( char *one, char *two)
++int strcmp_withnull(char *one, char *two)
+ {
+-    if ( one == NULL && two == NULL)
+-	return 0;
+-    if ( one != NULL && two == NULL)
+-	return -1;
+-    if ( one == NULL)
+-	return 1;
+-    return strcasecmp( one, two);
++	if (one == NULL && two == NULL)
++	{
++		return 0;
++	}
++	if (one != NULL && two == NULL)
++	{
++		return -1;
++	}
++	if (one == NULL)
++	{
++		return 1;
++	}
++	return strcasecmp(one, two);
+ }
+ 
+ /*
+  * Sorting functions
+  */
+ 
+-void
+-sort_servers( struct qserver **array, int size)
++void sort_servers(struct qserver **array, int size)
+ {
+-    quicksort( (void**)array, 0, size-1, (int (*)(void*,void*)) server_compare);
++	quicksort((void **)array, 0, size - 1, (int(*)(void *, void*))server_compare);
+ }
+ 
+-void
+-sort_players( struct qserver *server)
++void sort_players(struct qserver *server)
+ {
+-    struct player **array, *player, *last_team= NULL, **next;
+-    int np, i;
++	struct player **array, *player, *last_team = NULL, **next;
++	int np, i;
+ 
+-    if ( server->num_players == 0 || server->players == NULL)
+-	return;
++	if (server->num_players == 0 || server->players == NULL)
++	{
++		return ;
++	}
+ 
+-    player= server->players;
+-    for ( ; player != NULL && player->number == TRIBES_TEAM; )  {
+-	last_team= player;
+-	player= player->next;
+-    }
+-    if ( player == NULL)
+-	return;
++	player = server->players;
++	for (; player != NULL && player->number == TRIBES_TEAM;)
++	{
++		last_team = player;
++		player = player->next;
++	}
+ 
+-    array= (struct player **) malloc( sizeof(struct player *) *
+-		server->num_players);
+-    for ( np= 0; player != NULL && np < server->num_players; np++)  {
+-	array[np]= player;
+-	player= player->next;
+-    }
+-    quicksort( (void**)array, 0, np-1, (int (*)(void*,void*)) player_compare);
++	if (player == NULL)
++	{
++		return ;
++	}
+ 
+-    if ( last_team)
+-	next= &last_team->next;
+-    else
+-	next= &server->players;
+-
+-    for ( i= 0; i < np; i++)  {
+-	*next= array[i];
+-	array[i]->next= NULL;
+-	next= &array[i]->next;
+-    }
++	array = (struct player **)malloc(sizeof(struct player*)*(server->num_players + server->num_spectators));
++	for (np = 0; player != NULL && np < server->num_players + server->num_spectators; np++)
++	{
++		array[np] = player;
++		player = player->next;
++	} quicksort((void **)array, 0, np - 1, (int(*)(void *, void*))player_compare);
++
++	if (last_team)
++	{
++		next = &last_team->next;
++	}
++	else
++	{
++		next = &server->players;
++	}
++
++	for (i = 0; i < np; i++)
++	{
++		*next = array[i];
++		array[i]->next = NULL;
++		next = &array[i]->next;
++	}
+ 
+-    free( array);
++	free(array);
+ }
+ 
+-int
+-server_compare( struct qserver *one, struct qserver *two)
++int server_compare(struct qserver *one, struct qserver *two)
+ {
+-    int rc;
++	int rc;
+ 
+-    char *key= sort_keys;
+-
+-    for ( ; *key; key++)  {
+-	switch( *key)  {
+-	case 'g':
+-	    rc= strcmp_withnull( one->game, two->game);
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'p':
+-	    if ( one->n_requests == 0)
+-		return two->n_requests;
+-	    else if ( two->n_requests == 0)
+-		return -1;
+-	    rc= one->ping_total/one->n_requests -
+-			two->ping_total/two->n_requests;
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'i':
+-	    if ( one->ipaddr > two->ipaddr)
+-		return 1;
+-	    else if ( one->ipaddr < two->ipaddr)
+-		return -1;
+-	    else if ( one->port > two->port)
+-		return 1;
+-	    else if ( one->port < two->port)
+-		return -1;
+-	    break;
+-	case 'h':
+-	    rc= strcmp_withnull( one->host_name, two->host_name);
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'n':
+-	    rc= two->num_players - one->num_players;
+-	    if ( rc)
+-		return rc;
+-	    break;
++	char *key = sort_keys;
++
++	for (; *key; key++)
++	{
++		switch (*key)
++		{
++			case 'g':
++				rc = strcmp_withnull(one->game, two->game);
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'p':
++				if (one->n_requests == 0)
++				{
++					return two->n_requests;
++				}
++				else if (two->n_requests == 0)
++				{
++					return -1;
++				}
++				rc = one->ping_total / one->n_requests - two->ping_total / two->n_requests;
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'i':
++				if (one->ipaddr > two->ipaddr)
++				{
++					return 1;
++				}
++				else if (one->ipaddr < two->ipaddr)
++				{
++					return -1;
++				}
++				else if (one->port > two->port)
++				{
++					return 1;
++				}
++				else if (one->port < two->port)
++				{
++					return -1;
++				}
++				break;
++			case 'h':
++				rc = strcmp_withnull(one->host_name, two->host_name);
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'n':
++				rc = two->num_players - one->num_players;
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++		}
+ 	}
+-    }
+ 
+-    return 0;
++	return 0;
++}
++
++int type_option_compare(server_type *one, server_type *two)
++{
++	return strcmp_withnull(one->type_option, two->type_option);
+ }
+ 
+-int
+-type_option_compare( server_type *one, server_type *two )
++int type_string_compare(server_type *one, server_type *two)
+ {
+-    return strcmp_withnull( one->type_option, two->type_option );
++	return strcmp_withnull(one->type_string, two->type_string);
+ }
+ 
+-int
+-type_string_compare( server_type *one, server_type *two )
+-{
+-    return strcmp_withnull( one->type_string, two->type_string );
+-}
+-
+-int
+-player_compare( struct player *one, struct player *two)
+-{
+-    int rc;
+-
+-    char *key= sort_keys;
+-
+-    for ( ; *key; key++)  {
+-	switch( *key)  {
+-	case 'P':
+-	    rc= one->ping - two->ping;
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'F':
+-	    rc= two->frags - one->frags;
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'S':
+-	    rc= two->score - one->score;
+-	    if ( rc)
+-		return rc;
+-	    break;
+-	case 'T':
+-	    rc= one->team - two->team;
+-	    if ( rc)
+-		return rc;
+-		rc = strcmp_withnull( one->team_name, two->team_name );
+-	    if ( rc )
+-		return rc;
+-	    break;
+-	case 'N':
+-		rc= strcmp_withnull( one->name, two->name );
+-		if ( rc )
+-		return rc;
+-		return one->number - two->number;
+-		break;
++int player_compare(struct player *one, struct player *two)
++{
++	int rc;
++
++	char *key = sort_keys;
++
++	for (; *key; key++)
++	{
++		switch (*key)
++		{
++			case 'P':
++				rc = one->ping - two->ping;
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'F':
++				rc = two->frags - one->frags;
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'S':
++				rc = two->score - one->score;
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'T':
++				rc = one->team - two->team;
++				if (rc)
++				{
++					return rc;
++				}
++				rc = strcmp_withnull(one->team_name, two->team_name);
++				if (rc)
++				{
++					return rc;
++				}
++				break;
++			case 'N':
++				rc = strcmp_withnull(one->name, two->name);
++				if (rc)
++				{
++					return rc;
++				}
++				return one->number - two->number;
++				break;
++		}
+ 	}
+-    }
+-    return 0;
++	return 0;
+ }
+ 
+-void
+-quicksort( void **array, int i, int j, int (*compare)(void*,void*))
++void quicksort(void **array, int i, int j, int(*compare)(void *, void*))
+ {
+-    int q= 0;
++	int q = 0;
+ 
+-    if ( i < j) {
+-	q = qpartition(array,i,j, compare);
+-	quicksort(array,i,q, compare);
+-	quicksort(array,q+1,j, compare);
+-    }
++	if (i < j)
++	{
++		q = qpartition(array, i, j, compare);
++		quicksort(array, i, q, compare);
++		quicksort(array, q + 1, j, compare);
++	}
+ }
+ 
+-int
+-qpartition( void **array, int a, int b, int (*compare)(void*,void*))
++int qpartition(void **array, int a, int b, int(*compare)(void *, void*))
+ {
+-    /* this is our comparison point. when we are done
+-       splitting this array into 2 parts, we want all the
+-       elements on the left side to be less then or equal
+-       to this, all the elements on the right side need to
+-       be greater then or equal to this
+-    */
+-    void *z;
+-
+-    /* indicies into the array to sort. Used to calculate a partition
+-       point
+-    */
+-    int i = a-1;
+-    int j = b+1;
+-
+-    /* temp pointer used to swap two array elements */
+-    void * tmp = NULL;
+-
+-    z = array[a];
+-
+-    while (1) {
+-
+-        /* move the right indice over until the value of that array
+-           elem is less than or equal to z. Stop if we hit the left
+-           side of the array (ie, j == a);
+-        */
+-	do {
+-	    j--;
+-	} while( j > a && compare(array[j],z) > 0);
+-
+-        /* move the left indice over until the value of that
+-           array elem is greater than or equal to z, or until
+-           we hit the right side of the array (ie i == j)
+-        */
+-	do {
+-	    i++;
+-	} while( i <= j && compare(array[i],z) < 0);
+-
+-        /* if i is less then j, we need to switch those two array
+-           elements, if not then we are done partitioning this array
+-           section
+-        */
+-	if(i < j) {
+-	    tmp = array[i];
+-	    array[i] = array[j];
+-	    array[j] = tmp;
++	/* this is our comparison point. when we are done
++	splitting this array into 2 parts, we want all the
++	elements on the left side to be less then or equal
++	to this, all the elements on the right side need to
++	be greater then or equal to this
++	 */
++	void *z;
++
++	/* indicies into the array to sort. Used to calculate a partition
++	point
++	 */
++	int i = a - 1;
++	int j = b + 1;
++
++	/* temp pointer used to swap two array elements */
++	void *tmp = NULL;
++
++	z = array[a];
++
++	while (1)
++	{
++
++		/* move the right indice over until the value of that array
++		elem is less than or equal to z. Stop if we hit the left
++		side of the array (ie, j == a);
++		 */
++		do
++		{
++			j--;
++		}
++		while (j > a && compare(array[j], z) > 0);
++
++		/* move the left indice over until the value of that
++		array elem is greater than or equal to z, or until
++		we hit the right side of the array (ie i == j)
++		 */
++		do
++		{
++			i++;
++		}
++		while (i <= j && compare(array[i], z) < 0);
++
++		/* if i is less then j, we need to switch those two array
++		elements, if not then we are done partitioning this array
++		section
++		 */
++		if (i < j)
++		{
++			tmp = array[i];
++			array[i] = array[j];
++			array[j] = tmp;
++		}
++		else
++		{
++			return j;
++		}
+ 	}
+-	else
+-	    return j;
+-    }
+ }
+ 
+-// vim: sw=8 ts=8 noet
+diff -urP qstat-2.11/qstat.cfg qstat2/qstat.cfg
+--- qstat-2.11/qstat.cfg	2006-09-14 15:51:01.000000000 -0500
++++ qstat2/qstat.cfg	2009-12-11 18:40:26.531870000 -0500
+@@ -90,6 +90,7 @@
+    status2 packet = \377\377\377\377\002getstatus xxx
+ end
+ 
++# Config for Call of Duty
+ gametype CODS new extend Q3S
+     name = Call of Duty
+     default port = 28960
+@@ -106,6 +107,48 @@
+     master query = full empty
+ end
+ 
++# Config for Call of Duty 2
++gametype COD2S new extend Q3S
++    name = Call of Duty 2
++    default port = 28960
++    template var = CALLOFDUTY2
++    game rule = gamename
++end
++
++gametype COD2M new extend Q3M
++    name = Call of Duty 2 Master
++    default port = 20710
++    template var = COD2MASTER
++    master protocol = 118
++    master for gametype = COD2S
++    master query = full empty
++end
++
++# Config for Call of Duty 4
++gametype COD4S new extend Q3S
++    name = Call of Duty 4
++    default port = 28960
++    template var = CALLOFDUTY4
++    game rule = gamename
++end
++
++gametype COD4M new extend Q3M
++    name = Call of Duty 4 Master
++    default port = 20810
++    template var = COD4MASTER
++    master protocol = 6
++    master for gametype = COD4S
++    master query = full empty
++end
++
++# Config for Call of Duty Word at War
++gametype WAWS new extend Q3S
++    name = Call of Duty World at War
++    default port = 28960
++    template var = CALLOFDUTYWAW
++    game rule = gametype
++end
++
+ # enemy territory
+ gametype WOETS new extend Q3S
+     name = Enemy Territory
+@@ -174,6 +217,7 @@
+     template var = WARSOW
+     game rule = gamename
+     status packet = \377\377\377\377getinfo
++    status2 packet = \377\377\377\377getstatus
+ end
+ gametype WARSOWM new extend Q3M
+     name = Warsow Master
+@@ -215,3 +259,43 @@
+     master for gametype = PREYS
+ end
+ 
++gametype UT3S new extend GS4
++    name = UT3
++    default port = 6500
++    template var = UT3
++end
++
++
++# ioq3 fork
++gametype OPENARENAS new extend Q3S
++    name = OpenArena
++    template var = OPENARENA
++    game rule = gamename
++end
++
++gametype OPENARENAM new extend Q3M
++    name = OpenArena Master
++    template var = OPENARENAMASTER
++    default port = 27950
++    master packet = \377\377\377\377getservers %s %s
++    master protocol = 70
++    master query = empty full
++    master for gametype = OPENARENAS
++end
++
++# ioq3 fork
++gametype IOURTS new extend Q3S
++    name = ioUrbanTerror
++    template var = IOURT
++    game rule = gamename
++end
++
++gametype IOURTM new extend Q3M
++    name = ioUrbanTerror Master
++    template var = IOURTMASTER
++    default port = 27950
++    master packet = \377\377\377\377getservers %s %s
++    master protocol = 68
++    master query = empty full
++    master for gametype = IOURTS
++end
+diff -urP qstat-2.11/qstatdoc.html qstat2/qstatdoc.html
+--- qstat-2.11/qstatdoc.html	2005-11-17 00:06:04.000000000 -0500
++++ qstat2/qstatdoc.html	2009-07-02 06:13:04.183025000 -0400
+@@ -120,6 +120,7 @@
+ <tr><td>-tbs<td>tbs<td>28001<td>Starsiege: Tribes
+ <tr><td>-t2s<td>t2s<td>28000<td>Tribes 2
+ <tr><td>-qwm<td>qwm<td>27000<td>QuakeWorld master
++<tr><td>-hwm<td>hwm<td>26900<td>HexenWorld master
+ <tr><td>-q2m<td>q2m<td>27900<td>Quake II master
+ <tr><td>-hlm<td>hlm<td>27010<td>Half-Life master
+ <tr><td>-stm<td>stm<td>27010<td>Half-Life master (Steam)
+@@ -147,6 +148,7 @@
+ <tr><td>-efs<td>efs<td>29070<td>Jedi Knight: Jedi Academy
+ <tr><td>-efm<td>efm<td>29060<td>Jedi Knight: Jedi Academy master
+ <tr><td>-grs<td>grs<td>2346<td>Ghost Recon
++<tr><td>-etqws<td>etqws<td>27733<td>QuakeWars server
+ </table>
+ <p>
+ The command-line options can be specified multiple times, one for
+@@ -485,7 +487,7 @@
+ You can set these filters with QStat by appending query arguments
+ to the server type.  The general syntax is:
+ <blockquote>
+-<b>hlm,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
++<b>dm3m,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
+ </blockquote>
+ <table border=1>
+ <tr><th>Query Argument</th><th>Values</th><th>Description</th></tr>
+@@ -507,7 +509,7 @@
+ to the server type. The general syntax is:
+ 
+ <blockquote>
+-  <b>hlm,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
++  <b>ut2004m,</b><i>query-arg</i><b>=</b><i>value</i><b>,</b> ...
+ </blockquote>
+ <table border=1>
+   <tr>
+@@ -543,7 +545,13 @@
+   </tr>
+ </table>
+ 
+-See the Tribes 2 master server above for example usage.<br>
++You may need to specify at least one filter option like status or gametype for
++the master to actually return any server.<br>
++
++Example:
++<blockquote>
++<b>qstat -ut2004m,cdkey=/usr/local/games/ut2004/System/cdkey,status=nostandard ut2004master1.epicgames.com:28902<br>
++</blockquote>
+ 
+ <H4>Option Usage</H4>
+ 
+diff -urP qstat-2.11/qstat.h qstat2/qstat.h
+--- qstat-2.11/qstat.h	2006-08-04 15:42:09.000000000 -0500
++++ qstat2/qstat.h	2012-10-10 14:37:04.603408000 -0400
+@@ -11,7 +11,11 @@
+ #define __H_QSTAT
+ 
+ #ifdef HAVE_CONFIG_H
+-#include "gnuconfig.h"
++    #include "gnuconfig.h"
++#else
++    #ifndef VERSION
++        #define VERSION "2.13"
++    #endif
+ #endif
+ 
+ #ifdef __EMX__
+@@ -62,17 +66,45 @@
+ #define GCC_FORMAT_PRINTF(a, b)
+ #endif
+ 
++typedef enum {
++	INPROGRESS =  0,
++	DONE_AUTO  =  1,
++	DONE_FORCE =  2,
++
++	SYS_ERROR  = -1,
++	MEM_ERROR  = -2,
++	PKT_ERROR  = -3,
++	ORD_ERROR  = -4,
++	REQ_ERROR  = -5
++} query_status_t;
++
+ #include "qserver.h"
+ 
++typedef void (*DisplayFunc)( struct qserver *);
++typedef query_status_t (*QueryFunc)( struct qserver *);
++typedef query_status_t (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen);
++
+ // Packet modules
+ #include "ut2004.h"
+ #include "doom3.h"
+ #include "a2s.h"
++#include "fl.h"
+ #include "gps.h"
+ #include "gs2.h"
+ #include "gs3.h"
++#include "haze.h"
+ #include "ts2.h"
++#include "ts3.h"
+ #include "tm.h"
++#include "wic.h"
++#include "ottd.h"
++#include "tee.h"
++#include "cube2.h"
++#include "bfbc2.h"
++#include "ventrilo.h"
++#include "mumble.h"
++#include "terraria.h"
++#include "crysis.h"
+ 
+ /*
+  * Various magic numbers.
+@@ -87,6 +119,7 @@
+ #define QW_DEFAULT_PORT			27500
+ #define QW_MASTER_DEFAULT_PORT		27000
+ #define HW_DEFAULT_PORT			26950
++#define HW_MASTER_DEFAULT_PORT		26900
+ #define UNREAL_DEFAULT_PORT		7777
+ #define UNREAL_MASTER_DEFAULT_PORT	28900
+ #define HALFLIFE_DEFAULT_PORT		27015
+@@ -119,7 +152,16 @@
+ #define HL2_DEFAULT_PORT	27015
+ #define HL2_MASTER_DEFAULT_PORT	27011
+ #define TS2_DEFAULT_PORT 51234
++#define TS3_DEFAULT_PORT 10011
++#define BFBC2_DEFAULT_PORT 48888
+ #define TM_DEFAULT_PORT 5000
++#define WIC_DEFAULT_PORT 5000 // Default is actually disabled
++#define FL_DEFAULT_PORT 5478
++#define VENTRILO_DEFAULT_PORT 3784
++#define CUBE2_DEFAULT_PORT 28785
++#define MUMBLE_DEFAULT_PORT 64738
++#define TERRARIA_DEFAULT_PORT 7777
++#define CRYSIS_DEFAULT_PORT 64087
+ 
+ 
+ #define Q_UNKNOWN_TYPE 0
+@@ -183,8 +225,24 @@
+ #define GAMESPY4_PROTOCOL_SERVER 55
+ #define PREY_SERVER 56
+ #define TM_PROTOCOL_SERVER 57
++#define ETQW_SERVER 58
++#define HAZE_SERVER 59
++#define HW_MASTER (60 | MASTER_SERVER)
++#define WIC_PROTOCOL_SERVER 61
++#define OTTD_SERVER 62
++#define OTTD_MASTER (63 | MASTER_SERVER)
++#define FL_SERVER	64
++#define WOLF_SERVER 65
++#define TEE_SERVER	66
++#define TS3_PROTOCOL_SERVER	67
++#define BFBC2_PROTOCOL_SERVER	68
++#define VENTRILO_PROTOCOL_SERVER	69
++#define CUBE2_SERVER 70
++#define MUMBLE_PROTOCOL_SERVER 71
++#define TERRARIA_PROTOCOL_SERVER 72
++#define CRYSIS_PROTOCOL_SERVER 73
+ 
+-#define LAST_BUILTIN_SERVER  57
++#define LAST_BUILTIN_SERVER  73
+ 
+ #define TF_SINGLE_QUERY		(1<<1)
+ #define TF_OUTFILE		(1<<2)
+@@ -217,10 +275,6 @@
+ 
+ struct q_packet;
+ 
+-typedef void (*DisplayFunc)( struct qserver *);
+-typedef void (*QueryFunc)( struct qserver *);
+-typedef void (*PacketFunc)( struct qserver *, char *rawpkt, int pktlen);
+-
+ /*
+  * Output and formatting functions
+  */
+@@ -247,9 +301,14 @@
+ void display_armyops_player_info( struct qserver *server);
+ void display_gs2_player_info( struct qserver *server);
+ void display_doom3_player_info( struct qserver *server);
+-void display_hl2_player_info( struct qserver *server);
+ void display_ts2_player_info( struct qserver *server);
++void display_ts3_player_info( struct qserver *server);
++void display_bfbc2_player_info( struct qserver *server);
+ void display_tm_player_info( struct qserver *server);
++void display_wic_player_info( struct qserver *server);
++void display_fl_player_info( struct qserver *server);
++void display_tee_player_info( struct qserver *server);
++void display_ventrilo_player_info( struct qserver *server);
+ 
+ void raw_display_server( struct qserver *server);
+ void raw_display_server_rules( struct qserver *server);
+@@ -271,9 +330,14 @@
+ void raw_display_armyops_player_info( struct qserver *server);
+ void raw_display_gs2_player_info( struct qserver *server);
+ void raw_display_doom3_player_info( struct qserver *server);
+-void raw_display_hl2_player_info( struct qserver *server);
+ void raw_display_ts2_player_info( struct qserver *server);
++void raw_display_ts3_player_info( struct qserver *server);
++void raw_display_bfbc2_player_info( struct qserver *server);
+ void raw_display_tm_player_info( struct qserver *server);
++void raw_display_wic_player_info( struct qserver *server);
++void raw_display_fl_player_info( struct qserver *server);
++void raw_display_tee_player_info( struct qserver *server);
++void raw_display_ventrilo_player_info( struct qserver *server);
+ 
+ void xml_display_server( struct qserver *server);
+ void xml_header();
+@@ -295,64 +359,55 @@
+ void xml_display_ghostrecon_player_info( struct qserver *server);
+ void xml_display_eye_player_info( struct qserver *server);
+ void xml_display_armyops_player_info( struct qserver *server);
+-void xml_display_gs2_player_info( struct qserver *server);
++void xml_display_player_info( struct qserver *server);
+ void xml_display_doom3_player_info( struct qserver *server);
+-void xml_display_hl2_player_info( struct qserver *server);
+ void xml_display_ts2_player_info( struct qserver *server);
++void xml_display_ts3_player_info( struct qserver *server);
++void xml_display_bfbc2_player_info( struct qserver *server);
+ void xml_display_tm_player_info( struct qserver *server);
++void xml_display_wic_player_info( struct qserver *server);
++void xml_display_fl_player_info( struct qserver *server);
++void xml_display_tee_player_info( struct qserver *server);
++void xml_display_ventrilo_player_info( struct qserver *server);
+ char *xml_escape( char*);
+ char *str_replace( char *, char *, char *);
+ 
+-void send_server_request_packet( struct qserver *server);
+-void send_qserver_request_packet( struct qserver *server);
+-void send_qwserver_request_packet( struct qserver *server);
+-void send_ut2003_request_packet( struct qserver *server);
+-void send_tribes_request_packet( struct qserver *server);
+-void send_qwmaster_request_packet( struct qserver *server);
+-void send_bfris_request_packet( struct qserver *server);
+-void send_player_request_packet( struct qserver *server);
+-void send_rule_request_packet( struct qserver *server);
+-void send_ravenshield_request_packet( struct qserver *server);
+-void send_savage_request_packet( struct qserver *server);
+-void send_farcry_request_packet( struct qserver *server);
+-void send_gamespy_master_request( struct qserver *server);
+-void send_tribes2_request_packet( struct qserver *server);
+-void send_tribes2master_request_packet( struct qserver *server);
+-void send_ghostrecon_request_packet( struct qserver *server);
+-void send_eye_request_packet( struct qserver *server);
+-void send_gs2_request_packet( struct qserver *server);
+-void send_doom3_request_packet( struct qserver *server);
+-void send_hl2_request_packet( struct qserver *server);
+-void send_ts2_request_packet( struct qserver *server);
+-void send_tm_request_packet( struct qserver *server);
+-
+-void deal_with_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_qw_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q1qw_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_q2_packet( struct qserver *server, char *pkt, int pktlen,
+-	int check_duplicate_rules);
+-void deal_with_doom3master_packet( struct qserver *server, char *rawpkt, int pktlen);
+-void deal_with_qwmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-int deal_with_halflife_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ut2003_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribesmaster_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_bfris_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_gamespy_master_response( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ravenshield_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_savage_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_farcry_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_descent3_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_descent3master_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_eye_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_doom3_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_hl2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen);
+-void deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t send_qserver_request_packet( struct qserver *server);
++query_status_t send_qwserver_request_packet( struct qserver *server);
++query_status_t send_ut2003_request_packet( struct qserver *server);
++query_status_t send_tribes_request_packet( struct qserver *server);
++query_status_t send_qwmaster_request_packet( struct qserver *server);
++query_status_t send_bfris_request_packet( struct qserver *server);
++query_status_t send_player_request_packet( struct qserver *server);
++query_status_t send_rule_request_packet( struct qserver *server);
++query_status_t send_savage_request_packet( struct qserver *server);
++query_status_t send_farcry_request_packet( struct qserver *server);
++query_status_t send_gamespy_master_request( struct qserver *server);
++query_status_t send_tribes2_request_packet( struct qserver *server);
++query_status_t send_tribes2master_request_packet( struct qserver *server);
++query_status_t send_hl2_request_packet( struct qserver *server);
++
++query_status_t deal_with_q_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_qw_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_q1qw_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_q2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t deal_with_qwmaster_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_halflife_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ut2003_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribesmaster_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_bfris_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_gamespy_master_response( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ravenshield_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_savage_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_farcry_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes2_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_tribes2master_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_descent3_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_descent3master_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_ghostrecon_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_eye_packet( struct qserver *server, char *pkt, int pktlen);
++query_status_t deal_with_hl2_packet( struct qserver *server, char *pkt, int pktlen);
+ 
+ struct _server_type  {
+     int id;
+@@ -436,9 +491,9 @@
+ /* QUAKE WORLD */
+ struct {
+     char prefix[4];
+-    char command[7];
++    char command[10];
+ } qw_serverstatus =
+-{ { '\377', '\377', '\377', '\377' }, { 's', 't', 'a', 't', 'u', 's', '\n' } };
++{ { '\377', '\377', '\377', '\377' }, { 's', 't', 'a', 't', 'u', 's', ' ', '2', '3', '\n' } };
+ 
+ /* QUAKE3 */
+ struct {
+@@ -512,6 +567,10 @@
+ #define QW_GET_SERVERS    'c'
+ char qw_masterquery[] = { QW_GET_SERVERS, '\n', '\0' };
+ 
++/* HEXENWORLD MASTER */
++#define HW_GET_SERVERS    'c'
++char hw_masterquery[] = { '\377', HW_GET_SERVERS, '\0' };
++
+ /* QUAKE 2 MASTER */
+ char q2_masterquery[] = { 'q', 'u', 'e', 'r', 'y', '\n', '\0' };
+ 
+@@ -656,7 +715,9 @@
+ // 10: Team information (00 to disable)
+ // 11: Request new format
+ unsigned char gs3_player_query[] = {
+-	0xfe,0xfd,0x00,0x10,0x20,0x30,0x40,0xff,0xff,0xff,0x01
++	0xfe,0xfd,0x00,
++	0x10,0x20,0x30,0x40,
++	0xff,0xff,0xff,0x01
+ };
+ 
+ // Format:
+@@ -687,7 +748,40 @@
+ // 1 - 3: query head
+ // 4 - 7: queryid
+ unsigned char gs3_challenge[] = {
+-	0xfe,0xfd,0x09,0x10,0x20,0x30,0x40
++	0xfe,0xfd,0x09,
++	0x10,0x20,0x30,0x40
++};
++
++// Format:
++// 1 - 8: Query Request
++// 9 - 12: Query Header
++// 13: Query ID
++
++// Query ID is made up of the following
++// 0x01: Basic Info
++// 0x02: Game Rules
++// 0x03: Player Information
++// 0x04: Team Information
++unsigned char haze_status_query[] = {
++	'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y',
++	0x10,0x20,0x30,0x40,
++	0x0A
++};
++
++// Format:
++// 1 - 8: Query Request
++// 9 - 12: Query Header
++// 13: Query ID
++
++// Query ID is made up of the following
++// 0x01: Basic Info
++// 0x02: Game Rules
++// 0x03: Player Information
++// 0x04: Team Information
++unsigned char haze_player_query[] = {
++	'f', 'r', 'd', 'q', 'u', 'e', 'r', 'y',
++	0x10,0x20,0x30,0x40,
++	0x03
+ };
+ 
+ 
+@@ -746,7 +840,39 @@
+ 
+ char ravenshield_serverquery[] = "REPORT";
+ 
+-unsigned char ts2_status_query[] = "si";
++char ottd_master_query[] = {
++	0x04, 0x00,	// packet length
++	0x06,		// packet type
++	0x01		// packet version
++};
++
++char ottd_serverinfo[] = {
++	0x03, 0x00,	// packet length
++	0x00,		// packet type
++};
++
++char ottd_serverdetails[] = {
++	0x03, 0x00,	// packet length
++	0x02,		// packet type
++};
++
++/* Teeworlds */
++
++char tee_serverstatus[14] = { '\x20', '\0', '\0', '\0', '\0', '\0', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' };
++
++/* Cube2 */
++
++char cube2_serverstatus[3] = {'\x80', '\x10', '\x27'};
++
++/* Mumble */
++char mumble_serverstatus[12] = {'\x00', '\x00', '\x00', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08' };
++
++/* Terraria */
++char terraria_serverstatus[] = "GET /v2/server/status HTTP/1.1\x0d\x0a\x0d\x0a";
++
++
++/* SERVER BUILTIN TYPES */
++
+ 
+ server_type builtin_types[] = {
+ {
+@@ -1010,11 +1136,11 @@
+     0,								/* master_len */
+     NULL,							/* master_protocol */
+     NULL,							/* master_query */
+-    display_hl2_player_info,		/* display_player_func */
++    display_halflife_player_info,		/* display_player_func */
+     display_server_rules,			/* display_rule_func */
+-    raw_display_hl2_player_info,	/* display_raw_player_func */
++    raw_display_halflife_player_info,	/* display_raw_player_func */
+     raw_display_server_rules,		/* display_raw_rule_func */
+-    xml_display_hl2_player_info,	/* display_xml_player_func */
++    xml_display_halflife_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,		/* display_xml_rule_func */
+     send_hl2_request_packet,		/* status_query_func */
+     NULL,				/* rule_query_func */
+@@ -1223,7 +1349,6 @@
+     send_qwserver_request_packet,/* status_query_func */
+     NULL,			/* rule_query_func */
+     NULL,			/* player_query_func */
+-    (void (*)( struct qserver *, char *, int))
+     deal_with_halflife_packet,	/* packet_func */
+ },
+ {
+@@ -1663,7 +1788,7 @@
+     raw_display_server_rules,		/* display_raw_rule_func */
+     xml_display_ghostrecon_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,		/* display_xml_rule_func */
+-    send_ghostrecon_request_packet,	/* status_query_func */
++    send_qserver_request_packet,	/* status_query_func */
+     NULL,				/* rule_query_func */
+     NULL,				/* player_query_func */
+     deal_with_ghostrecon_packet,	/* packet_func */
+@@ -1697,7 +1822,7 @@
+     raw_display_server_rules,	/* display_raw_rule_func */
+     xml_display_eye_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,	/* display_xml_rule_func */
+-    send_eye_request_packet,	/* status_query_func */
++    send_qserver_request_packet,	/* status_query_func */
+     NULL,			/* rule_query_func */
+     NULL,			/* player_query_func */
+     deal_with_eye_packet,	/* packet_func */
+@@ -1729,7 +1854,7 @@
+     display_server_rules,	/* display_rule_func */
+     raw_display_gs2_player_info,	/* display_raw_player_func */
+     raw_display_server_rules,	/* display_raw_rule_func */
+-    xml_display_gs2_player_info,	/* display_xml_player_func */
++    xml_display_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,	/* display_xml_rule_func */
+     send_gs2_request_packet,	/* status_query_func */
+     NULL,			/* rule_query_func */
+@@ -1799,7 +1924,7 @@
+     raw_display_server_rules,		/* display_raw_rule_func */
+     xml_display_ravenshield_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,		/* display_xml_rule_func */
+-    send_ravenshield_request_packet,	/* status_query_func */
++    send_qserver_request_packet,	/* status_query_func */
+     NULL,				/* rule_query_func */
+     NULL,				/* player_query_func */
+     deal_with_ravenshield_packet,	/* packet_func */
+@@ -1910,6 +2035,40 @@
+     deal_with_qwmaster_packet,	/* packet_func */
+ },
+ {
++    /* HEXEN2WORLD MASTER */
++    HW_MASTER,			/* id */
++    "HWM",			/* type_prefix */
++    "hwm",			/* type_string */
++    "-hwm",			/* type_option */ /* ## also "-qw" */
++    "HexenWorld Master",	/* game_name */
++    HW_SERVER,			/* master */
++    HW_MASTER_DEFAULT_PORT,	/* default_port */
++    0,				/* port_offset */
++    TF_SINGLE_QUERY|TF_OUTFILE,	/* flags */
++    "",				/* game_rule */
++    "HWMASTER",			/* template_var */
++    NULL,			/* status_packet */
++    0,				/* status_len */
++    NULL,			/* player_packet */
++    0,				/* player_len */
++    NULL,			/* rule_packet */
++    0,				/* rule_len */
++    (char*) &hw_masterquery,	/* master_packet */
++    sizeof( hw_masterquery),	/* master_len */
++    NULL,			/* master_protocol */
++    NULL,			/* master_query */
++    display_qwmaster,		/* display_player_func */
++    NULL,	/* display_rule_func */
++    NULL,	/* display_raw_player_func */
++    NULL,	/* display_raw_rule_func */
++    NULL,	/* display_xml_player_func */
++    NULL,	/* display_xml_rule_func */
++    send_qwmaster_request_packet,/* status_query_func */
++    NULL,			/* rule_query_func */
++    NULL,			/* player_query_func */
++    deal_with_qwmaster_packet,	/* packet_func */
++},
++{
+     /* QUAKE 2 MASTER */
+     Q2_MASTER,			/* id */
+     "Q2M",			/* type_prefix */
+@@ -2375,11 +2534,11 @@
+     0,								/* master_len */
+     NULL,							/* master_protocol */
+     NULL,							/* master_query */
+-    display_hl2_player_info,		/* display_player_func */
++    display_halflife_player_info,		/* display_player_func */
+     display_server_rules,			/* display_rule_func */
+-    raw_display_hl2_player_info,	/* display_raw_player_func */
++    raw_display_halflife_player_info,	/* display_raw_player_func */
+     raw_display_server_rules,		/* display_raw_rule_func */
+-    xml_display_hl2_player_info,	/* display_xml_player_func */
++    xml_display_halflife_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,		/* display_xml_rule_func */
+     send_a2s_request_packet,		/* status_query_func */
+     send_a2s_rule_request_packet,	/* rule_query_func */
+@@ -2447,7 +2606,7 @@
+     display_server_rules,	/* display_rule_func */
+     raw_display_gs2_player_info,	/* display_raw_player_func */
+     raw_display_server_rules,	/* display_raw_rule_func */
+-    xml_display_gs2_player_info,	/* display_xml_player_func */
++    xml_display_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,	/* display_xml_rule_func */
+     send_gs3_request_packet,	/* status_query_func */
+     NULL,			/* rule_query_func */
+@@ -2493,10 +2652,10 @@
+     QUAKE4_SERVER,					/* id */
+     "Q4S",							/* type_prefix */
+     "q4s",							/* type_string */
+-    "-q4s",						/* type_option */
++    "-q4s",							/* type_option */
+     "Quake 4",						/* game_name */
+     0,								/* master */
+-    QUAKE4_DEFAULT_PORT,				/* default_port */
++    QUAKE4_DEFAULT_PORT,			/* default_port */
+     0,								/* port_offset */
+     TF_QUAKE3_NAMES,				/* flags */
+     "fs_game",						/* game_rule */
+@@ -2520,7 +2679,7 @@
+     send_qwserver_request_packet,	/* status_query_func */
+     NULL,							/* rule_query_func */
+     NULL,							/* player_query_func */
+-    deal_with_quake4_packet,			/* packet_func */
++    deal_with_quake4_packet,		/* packet_func */
+ },
+ {
+     /* QUAKE 4 MASTER */
+@@ -2583,7 +2742,7 @@
+     display_server_rules,	/* display_rule_func */
+     raw_display_gs2_player_info,	/* display_raw_player_func */
+     raw_display_server_rules,	/* display_raw_rule_func */
+-    xml_display_gs2_player_info,	/* display_xml_player_func */
++    xml_display_player_info,	/* display_xml_player_func */
+     xml_display_server_rules,	/* display_xml_rule_func */
+     send_gs3_request_packet,	/* status_query_func */
+     NULL,			/* rule_query_func */
+@@ -2659,7 +2818,517 @@
+     deal_with_tm_packet,			/* packet_func */
+ },
+ {
+-    Q_UNKNOWN_TYPE,		/* id */
++    /* Enemy Territory : QuakeWars */
++    ETQW_SERVER,					/* id */
++    "ETQWS",						/* type_prefix */
++    "etqws",						/* type_string */
++    "-etqws",						/* type_option */
++    "QuakeWars",					/* game_name */
++    0,								/* master */
++    ETQW_DEFAULT_PORT,				/* default_port */
++    0,								/* port_offset */
++    TF_QUAKE3_NAMES,				/* flags */
++    "fs_game",						/* game_rule */
++    "QUAKE4",						/* template_var */
++    (char*) &doom3_serverinfo,		/* status_packet */
++    sizeof( doom3_serverinfo),		/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_doom3_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_doom3_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_doom3_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_qwserver_request_packet,	/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_etqw_packet,			/* packet_func */
++},
++{
++    /* HAZE PROTOCOL */
++    HAZE_SERVER,	/* id */
++    "HAZES",			/* type_prefix */
++    "hazes",			/* type_string */
++    "-hazes",			/* type_option */
++    "Haze Protocol",	/* game_name */
++    0,				/* master */
++    0,				/* default_port */
++    0,				/* port_offset */
++    TF_SINGLE_QUERY,		/* flags */
++    "gametype",			/* game_rule */
++    "HAZE",		/* template_var */
++    (char*) &haze_status_query,	/* status_packet */
++    sizeof( haze_status_query),	/* status_len */
++    (char*) &haze_player_query,	/* player_packet */
++    sizeof( haze_player_query),	/* player_len */
++    NULL,			/* rule_packet */
++    0,				/* rule_len */
++    NULL,			/* master_packet */
++    0,				/* master_len */
++    NULL,			/* master_protocol */
++    NULL,			/* master_query */
++    display_gs2_player_info,	/* display_player_func */
++    display_server_rules,	/* display_rule_func */
++    raw_display_gs2_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,	/* display_raw_rule_func */
++    xml_display_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,	/* display_xml_rule_func */
++    send_haze_request_packet,	/* status_query_func */
++    NULL,			/* rule_query_func */
++    NULL,			/* player_query_func */
++    deal_with_haze_packet,	/* packet_func */
++},
++{
++    /* World in Confict PROTOCOL */
++    WIC_PROTOCOL_SERVER,			/* id */
++    "WICS",							/* type_prefix */
++    "wics",							/* type_string */
++    "-wics",						/* type_option */
++    "World in Conflict",			/* game_name */
++    0,								/* master */
++    0,								/* default_port */
++    0,								/* port_offset */
++    TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG,	/* flags */
++    "N/A",							/* game_rule */
++    "WICPROTOCOL",					/* template_var */
++    NULL,							/* status_packet */
++    0,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_wic_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_wic_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_wic_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_wic_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_wic_packet,			/* packet_func */
++},
++{
++    /* openTTD */
++    OTTD_SERVER,			/* id */
++    "OTTDS",			/* type_prefix */
++    "ottds",			/* type_string */
++    "-ottds",			/* type_option */
++    "OpenTTD",			/* game_name */
++    0,				/* master */
++    3979,			/* default_port */
++    0,				/* port_offset */
++    0,				/* flags */
++    "",				/* game_rule */
++    "OPENTTD",			/* template_var */
++    (char*) &ottd_serverinfo,	/* status_packet */
++    sizeof( ottd_serverinfo),	/* status_len */
++    NULL,			/* player_packet */
++    0,				/* player_len */
++    (char*) &ottd_serverdetails,/* rule_packet */
++    sizeof( ottd_serverdetails),   /* rule_len */
++    NULL,			/* master_packet */
++    0,				/* master_len */
++    NULL,			/* master_protocol */
++    NULL,			/* master_query */
++    display_q2_player_info,	/* display_player_func */
++    display_server_rules,	/* display_rule_func */
++    raw_display_q2_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,	/* display_raw_rule_func */
++    xml_display_player_info,/* display_xml_player_func */
++    xml_display_server_rules,	/* display_xml_rule_func */
++    send_ottd_request_packet,	/* status_query_func */
++    NULL,			/* rule_query_func */
++    NULL,			/* player_query_func */
++    deal_with_ottd_packet,	/* packet_func */
++},
++{
++    /* openTTD Master */
++    OTTD_MASTER,		/* id */
++    "OTTDM",			/* type_prefix */
++    "ottdm",			/* type_string */
++    "-ottdm",			/* type_option */
++    "openTTD Master",		/* game_name */
++    OTTD_SERVER,		/* master */
++    3978,			/* default_port */
++    0,				/* port_offset */
++    TF_OUTFILE | TF_QUERY_ARG,	/* flags */
++    "",				/* game_rule */
++    "OTTDMASTER",		/* template_var */
++    NULL,			/* status_packet */
++    0,				/* status_len */
++    NULL,			/* player_packet */
++    0,				/* player_len */
++    NULL,			/* rule_packet */
++    0,				/* rule_len */
++    ottd_master_query,	/* master_packet */
++    sizeof(ottd_master_query),/* master_len */
++    NULL,	/* master_protocol */
++    NULL,	/* master_query */
++    display_qwmaster,		/* display_player_func */
++    NULL,	/* display_rule_func */
++    NULL,	/* display_raw_player_func */
++    NULL,	/* display_raw_rule_func */
++    NULL,	/* display_xml_player_func */
++    NULL,	/* display_xml_rule_func */
++    send_ottdmaster_request_packet,/* status_query_func */
++    NULL,			/* rule_query_func */
++    NULL,			/* player_query_func */
++    deal_with_ottdmaster_packet,	/* packet_func */
++},
++{
++    /* Frontlines-Fuel of War */
++    FL_SERVER,						/* id */
++    "FLS",							/* type_prefix */
++    "fls",							/* type_string */
++    "-fls",							/* type_option */
++    "Frontlines-Fuel of War",		/* game_name */
++    0,								/* master */
++    FL_DEFAULT_PORT,				/* default_port */
++    0,								/* port_offset */
++    TF_QUAKE3_NAMES,				/* flags */
++    "gamedir",						/* game_rule */
++    "FLS",							/* template_var */
++    NULL,							/* status_packet */
++    0,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_fl_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_fl_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_fl_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_fl_request_packet,			/* status_query_func */
++    send_fl_rule_request_packet,	/* rule_query_func */
++	NULL,							/* player_query_func */
++    deal_with_fl_packet,			/* packet_func */
++},
++{
++    /* Wolfenstein */
++    WOLF_SERVER,					/* id */
++    "WOLF",							/* type_prefix */
++    "wolfs",						/* type_string */
++    "-wolfs",						/* type_option */
++    "Wolfenstein",					/* game_name */
++    0,								/* master */
++    WOLF_DEFAULT_PORT,				/* default_port */
++    0,								/* port_offset */
++    TF_QUAKE3_NAMES,				/* flags */
++    "fs_game",						/* game_rule */
++    "QUAKE4",						/* template_var */
++    (char*) &doom3_serverinfo,		/* status_packet */
++    sizeof( doom3_serverinfo),		/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_doom3_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_doom3_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_doom3_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_qwserver_request_packet,	/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_wolf_packet,			/* packet_func */
++},
++{
++    /* Teeworlds */
++    TEE_SERVER,						/* id */
++    "TEE",							/* type_prefix */
++    "tee",							/* type_string */
++    "-tee",							/* type_option */
++    "Teeworlds",		/* game_name */
++    0,								/* master */
++    35515,							/* default_port */
++    0,								/* port_offset */
++    0,				/* flags */
++    "gametype",							/* game_rule */
++    "TEE",							/* template_var */
++    tee_serverstatus,						/* status_packet */
++    sizeof(tee_serverstatus),					/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_tee_player_info,			/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_tee_player_info,		/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_tee_player_info,		/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_tee_request_packet,			/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_tee_packet,			/* packet_func */
++},
++{
++    /* TEAMSPEAK 3 PROTOCOL */
++    TS3_PROTOCOL_SERVER,			/* id */
++    "TS3",							/* type_prefix */
++    "ts3",							/* type_string */
++    "-ts3",							/* type_option */
++    "Teamspeak 3",					/* game_name */
++    0,								/* master */
++    0,								/* default_port */
++    0,								/* port_offset */
++    TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG,	/* flags */
++    "N/A",							/* game_rule */
++    "TS3PROTOCOL",					/* template_var */
++    NULL,							/* status_packet */
++    0,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_ts3_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_ts3_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_ts3_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_ts3_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_ts3_packet,			/* packet_func */
++},
++{
++    /* BATTLEFIELD BAD COMPANY 2 PROTOCOL */
++    BFBC2_PROTOCOL_SERVER,			/* id */
++    "BFBC2",						/* type_prefix */
++    "bfbc2",						/* type_string */
++    "-bfbc2",						/* type_option */
++    "Battlefield Bad Company 2",	/* game_name */
++    0,								/* master */
++    BFBC2_DEFAULT_PORT,				/* default_port */
++    0,								/* port_offset */
++    TF_TCP_CONNECT,					/* flags */
++    "gametype",						/* game_rule */
++    "BFBC2PROTOCOL",				/* template_var */
++    NULL,							/* status_packet */
++    0,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    display_bfbc2_player_info,		/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    raw_display_bfbc2_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_bfbc2_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_bfbc2_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_bfbc2_packet,			/* packet_func */
++},
++{
++    /* VENTRILO PROTOCOL */
++    VENTRILO_PROTOCOL_SERVER,			/* id */
++    "VENTRILO",							/* type_prefix */
++    "ventrilo",							/* type_string */
++    "-vent",							/* type_option */
++    "Ventrilo",							/* game_name */
++    0,									/* master */
++    VENTRILO_DEFAULT_PORT,				/* default_port */
++    0,									/* port_offset */
++    0,									/* flags */
++    "gametype",							/* game_rule */
++    "VENTRILO",							/* template_var */
++    NULL,								/* status_packet */
++    0,									/* status_len */
++    NULL,								/* player_packet */
++    0,									/* player_len */
++    NULL,								/* rule_packet */
++    0,									/* rule_len */
++    NULL,								/* master_packet */
++    0,									/* master_len */
++    NULL,								/* master_protocol */
++    NULL,								/* master_query */
++    display_ventrilo_player_info,		/* display_player_func */
++    display_server_rules,				/* display_rule_func */
++    raw_display_ventrilo_player_info,	/* display_raw_player_func */
++    raw_display_server_rules,			/* display_raw_rule_func */
++    xml_display_ventrilo_player_info,	/* display_xml_player_func */
++    xml_display_server_rules,			/* display_xml_rule_func */
++    send_ventrilo_request_packet,		/* status_query_func */
++    NULL,								/* rule_query_func */
++    NULL,								/* player_query_func */
++    deal_with_ventrilo_packet,			/* packet_func */
++},
++{
++    /* Cube 2/Sauerbraten/Blood Frontier */
++    CUBE2_SERVER,						/* id */
++    "CUBE2",							/* type_prefix */
++    "cube2",							/* type_string */
++    "-cubes",							/* type_option */
++    "Sauerbraten",						/* game_name */
++    0,									/* master */
++    CUBE2_DEFAULT_PORT,					/* default_port */
++    1,									/* port_offset */
++    0,									/* flags */
++    "",									/* game_rule */
++    "CUBE2",							/* template_var */
++    cube2_serverstatus,					/* status_packet */
++    sizeof(cube2_serverstatus),			/* status_len */
++    NULL,								/* player_packet */
++    0,									/* player_len */
++    NULL,								/* rule_packet */
++    0,									/* rule_len */
++    NULL,								/* master_packet */
++    0,									/* master_len */
++    NULL,								/* master_protocol */
++    NULL,								/* master_query */
++    NULL,								/* display_player_func */
++    display_server_rules,				/* display_rule_func */
++    NULL,								/* display_raw_player_func */
++    raw_display_server_rules,			/* display_raw_rule_func */
++    NULL,								/* display_xml_player_func */
++    xml_display_server_rules,			/* display_xml_rule_func */
++    send_cube2_request_packet,			/* status_query_func */
++    NULL,								/* rule_query_func */
++    NULL,								/* player_query_func */
++    deal_with_cube2_packet,				/* packet_func */
++},
++{
++    /* MUMBLE PROTOCOL */
++    MUMBLE_PROTOCOL_SERVER,			/* id */
++    "MUMBLE",						/* type_prefix */
++    "mumble",						/* type_string */
++    "-mumble",						/* type_option */
++    "Mumble",						/* game_name */
++    0,								/* master */
++    MUMBLE_DEFAULT_PORT,			/* default_port */
++    0,								/* port_offset */
++    0,								/* flags */
++    "gametype",						/* game_rule */
++    "MUMBLEPROTOCOL",				/* template_var */
++    mumble_serverstatus,			/* status_packet */
++    12,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    NULL,							/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    NULL,							/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_player_info,		/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_mumble_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_mumble_packet,		/* packet_func */
++},
++{
++    /* TERRARIA PROTOCOL */
++    TERRARIA_PROTOCOL_SERVER,		/* id */
++    "TERRARIA",						/* type_prefix */
++    "terraria",						/* type_string */
++    "-terraria",					/* type_option */
++    "Terraria",						/* game_name */
++    0,								/* master */
++    TERRARIA_DEFAULT_PORT,			/* default_port */
++    0,								/* port_offset */
++    TF_TCP_CONNECT,					/* flags */
++    "gametype",						/* game_rule */
++    "TERRARIPROTOCOL",				/* template_var */
++    terraria_serverstatus,			/* status_packet */
++    sizeof(terraria_serverstatus),	/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    NULL,							/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    NULL,							/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_player_info,		/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_mumble_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_terraria_packet,		/* packet_func */
++},
++{
++    /* CRYSIS PROTOCOL */
++    CRYSIS_PROTOCOL_SERVER,			/* id */
++    "CRYSIS",						/* type_prefix */
++    "crysis",						/* type_string */
++    "-crysis",						/* type_option */
++    "Crysis",						/* game_name */
++    0,								/* master */
++    0,								/* default_port */
++    0,								/* port_offset */
++    TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG,	/* flags */
++    "gamerules",					/* game_rule */
++    "CRYSISPROTOCOL",				/* template_var */
++    NULL,							/* status_packet */
++    0,								/* status_len */
++    NULL,							/* player_packet */
++    0,								/* player_len */
++    NULL,							/* rule_packet */
++    0,								/* rule_len */
++    NULL,							/* master_packet */
++    0,								/* master_len */
++    NULL,							/* master_protocol */
++    NULL,							/* master_query */
++    NULL,							/* display_player_func */
++    display_server_rules,			/* display_rule_func */
++    NULL,							/* display_raw_player_func */
++    raw_display_server_rules,		/* display_raw_rule_func */
++    xml_display_player_info,		/* display_xml_player_func */
++    xml_display_server_rules,		/* display_xml_rule_func */
++    send_crysis_request_packet,		/* status_query_func */
++    NULL,							/* rule_query_func */
++    NULL,							/* player_query_func */
++    deal_with_crysis_packet,		/* packet_func */
++},
++{
++    Q_UNKNOWN_TYPE,	/* id */
+     "",				/* type_prefix */
+     "",				/* type_string */
+     "",				/* type_option */
+@@ -2676,8 +3345,10 @@
+     0,				/* player_len */
+     NULL,			/* rule_packet */
+     0,				/* rule_len */
+-    (char*) NULL,		/* master_packet */
++    (char*) NULL,	/* master_packet */
+     0,				/* master_len */
++    NULL,			/* master_protocol */
++    NULL,			/* master_query */
+     NULL,			/* display_player_func */
+     NULL,			/* display_rule_func */
+     NULL,			/* display_raw_player_func */
+@@ -2873,6 +3544,8 @@
+ int time_delta( struct timeval *later, struct timeval *past);
+ char * strherror( int h_err);
+ int connection_refused();
++int connection_would_block();
++int connection_reset();
+ 
+ void add_file( char *filename);
+ int add_qserver( char *arg, server_type* type, char *outfilename, char *query_arg);
+@@ -2889,6 +3562,7 @@
+ #define CHECK_DUPLICATE_RULES 2
+ #define NO_KEY_COPY 4
+ #define COMBINE_VALUES 8
++#define OVERWITE_DUPLICATES 16
+ 
+ struct player* get_player_by_number( struct qserver *server, int player_number );
+ struct rule* add_rule( struct qserver *server, char *key, char *value,	int flags) ;
+@@ -2949,4 +3623,7 @@
+ #define NO_PLAYER_INFO 0xffff
+ #define NO_SERVER_RULES NULL
+ 
++#define FORCE 1
++#define NO_FORCE 0
++
+ #endif
+diff -urP qstat-2.11/tee.c qstat2/tee.c
+--- qstat-2.11/tee.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tee.c	2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,92 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Teeworlds protocol
++ * Copyright 2008 ? Emiliano Leporati
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' };
++
++query_status_t send_tee_request_packet( struct qserver *server )
++{
++	return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_tee_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	// skip unimplemented ack, crc, etc
++	char *pkt = rawpkt + 6;
++	char *tok = NULL, *version = NULL;
++	int i;
++	struct player* player;
++
++	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
++
++	if (0 == memcmp( pkt, tee_serverinfo, 8)) 
++	{
++		pkt += 8;
++		// version
++		version = strdup(pkt); pkt += strlen(pkt) + 1;
++		// server name
++		server->server_name = strdup(pkt); pkt += strlen(pkt) + 1;
++		// map name
++		server->map_name = strdup(pkt); pkt += strlen(pkt) + 1;
++		// game type
++		switch(atoi(pkt)) {
++		case 0:
++			add_rule( server, server->type->game_rule, "dm", NO_FLAGS);
++			break;
++		case 1:
++			add_rule( server, server->type->game_rule, "tdm", NO_FLAGS);
++			break;
++		case 2:
++			add_rule( server, server->type->game_rule, "ctf", NO_FLAGS);
++			break;
++		default:
++			add_rule( server, server->type->game_rule, "unknown", NO_FLAGS);
++			break;
++		}
++		pkt += strlen(pkt) + 1; 
++		pkt += strlen(pkt) + 1;
++		pkt += strlen(pkt) + 1;
++		// num players
++		server->num_players = atoi(pkt); pkt += strlen(pkt) + 1;
++		// max players
++		server->max_players = atoi(pkt); pkt += strlen(pkt) + 1;
++		// players
++		for(i = 0; i < server->num_players; i++)
++		{
++			player = add_player( server, i );
++			player->name = strdup(pkt); pkt += strlen(pkt) + 1;
++			player->score = atoi(pkt); pkt += strlen(pkt) + 1;
++		}
++		// version reprise
++		server->protocol_version = 0;
++
++		if (NULL == (tok = strtok(version, "."))) return -1;
++		server->protocol_version |= (atoi(tok) & 0x000F) << 12;
++		if (NULL == (tok = strtok(NULL, "."))) return -1;
++		server->protocol_version |= (atoi(tok) & 0x000F) << 8;
++		if (NULL == (tok = strtok(NULL, "."))) return -1;
++		server->protocol_version |= (atoi(tok) & 0x00FF);
++
++		free(version);
++
++		return DONE_FORCE;
++	}
++
++	// unknown packet type
++	return PKT_ERROR;
++}
+diff -urP qstat-2.11/tee.h qstat2/tee.h
+--- qstat-2.11/tee.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tee.h	2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.11
++ * by Steve Jankowski
++ *
++ * Teeworlds protocol
++ * Copyright 2008 ? Emiliano Leporati
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TEE_H
++#define QSTAT_TEE_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_tee_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_tee_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/template/ghostrecon.lst qstat2/template/ghostrecon.lst
+--- qstat-2.11/template/ghostrecon.lst	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/template/ghostrecon.lst	2002-11-13 21:33:53.000000000 -0500
+@@ -0,0 +1,2 @@
++grs 208.144.248.104
++grs,ignoreserverplayer=yes 208.144.248.105
+Only in qstat-2.11/template: Makefile.in
+diff -urP qstat-2.11/template.c qstat2/template.c
+--- qstat-2.11/template.c	2006-02-22 08:44:52.000000000 -0500
++++ qstat2/template.c	2009-12-11 19:26:07.553341000 -0500
+@@ -21,7 +21,7 @@
+ #include <ctype.h>
+ #include <time.h>
+ 
+-#if defined(__hpux) || defined(_AIX) || defined(__FreeBSD__)
++#if defined(__hpux) || defined(_AIX) || defined(__FreeBSD__) || defined(__MidnightBSD__)
+ #include <sys/types.h>
+ #endif
+ 
+diff -urP qstat-2.11/terraria.c qstat2/terraria.c
+--- qstat-2.11/terraria.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/terraria.c	2012-05-16 09:02:58.011773000 -0400
+@@ -0,0 +1,198 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Terraria / TShock query protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#define strtok_ret strtok_r
++#else
++#include <winsock.h>
++#define strtok_ret strtok_s
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++query_status_t send_terraria_request_packet( struct qserver *server )
++{
++    return send_packet( server, server->type->status_packet, server->type->status_len );
++}
++
++query_status_t deal_with_terraria_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *end, *key, *val, *sep, *linep, *varp;
++	int complete_response = 0;
++	char last_char;
++	unsigned short port = 0;
++	debug( 2, "processing..." );
++
++    if ( 0 == pktlen )
++    {
++        // Disconnect?
++        return REQ_ERROR;
++    }
++
++	complete_response = ( 0 == strncmp( "HTTP/1.1 200", rawpkt, 12 ) && '}' == rawpkt[pktlen-1] ) ? 1 : 0;
++	last_char = rawpkt[pktlen-1];
++	rawpkt[pktlen-1] = '\0';
++
++	debug( 3, "packet: combined = %d, complete = %d", server->combined, complete_response );
++	if ( ! complete_response )
++	{
++		if ( ! server->combined )
++		{
++			// response fragment recieved
++			int pkt_id;
++			int pkt_max;
++			server->retry1 = n_retries;
++			if ( 0 == server->n_requests )
++			{
++				server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++				server->n_requests++;
++			}
++
++			// We're expecting more to come
++			debug( 5, "fragment recieved..." );
++			pkt_id = packet_count( server );
++			pkt_max = pkt_id+1;
++			rawpkt[pktlen-1] = last_char; // restore the last character
++			if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++			{
++				// fatal error e.g. out of memory
++				return MEM_ERROR;
++			}
++
++			if ( 0 == pkt_id )
++			{
++				return INPROGRESS;
++			}
++
++			// combine_packets will call us recursively
++			return combine_packets( server );
++		}
++
++		// recursive call which is still incomplete
++		return INPROGRESS;
++	}
++
++	// find the end of the headers
++	s = strstr( rawpkt, "\x0d\x0a\x0d\x0a" );
++
++	if ( NULL == s )
++	{
++		debug(1, "Error: missing end of headers");
++		return REQ_ERROR;
++	}
++
++	s += 4;
++
++	end = &rawpkt[pktlen-1];
++
++	// Correct ping
++	// Not quite right but gives a good estimate
++	server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++	debug( 3, "processing response..." );
++
++	s = strtok_ret( s, "\x0d\x0a", &linep );
++
++	// NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++	while ( NULL != s )
++	{
++		debug( 2, "LINE: %s\n", s );
++		if ( 0 == strcmp( s, "{" ) )
++		{
++			s = strtok_ret( NULL, "\x0d\x0a", &linep );
++			continue;
++		}
++
++		s = strtok_ret( s, "\"", &varp );
++		key = strtok_ret( NULL, "\"", &varp );
++		sep = strtok_ret( NULL, " ", &varp );
++		val = strtok_ret( NULL, "\"", &varp );
++		if ( NULL == val )
++		{
++			// world etc may be empty which results in NULL val
++			s = strtok_ret( NULL, "\x0d\x0a", &linep );
++			continue;
++		}
++		//if ( NULL == val && sep
++		debug( 2, "var: '%s' = '%s', sep: '%s'\n", key, val, sep );
++		if ( 0 == strcmp( key, "name" ) )
++		{
++			server->server_name = strdup( val );
++		}
++		else if ( 0 == strcmp( key, "port" ) )
++		{
++			port = atoi( val );
++			change_server_port( server, port, 0 );
++			add_rule( server, key, val, NO_FLAGS );
++		}
++		else if ( 0 == strcmp( key, "playercount" ) )
++		{
++			server->num_players = atoi( val );
++		}
++		else if ( 0 == strcmp( key, "maxplayers" ) )
++		{
++			server->max_players = atoi( val );
++		}
++		else if ( 0 == strcmp( key, "world" ) )
++		{
++			server->map_name = strdup( val );
++		}
++		else if ( 0 == strcmp( key, "players" ) )
++		{
++			// Players are ", " seperated but potentially player names can have spaces
++			// so we manually check for the leading space and fix if found
++			char *playersp;
++			char *player_name = strtok_ret( val, ",", &playersp );
++			while ( NULL != player_name )
++			{
++				struct player *player = add_player( server, server->n_player_info );
++				if ( NULL != player )
++				{
++					if ( ' ' == player_name[0] )
++					{
++						player_name++;
++					}
++					player->name = strdup( player_name );
++					debug( 4, "Player: %s\n", player->name );
++				}
++				player_name = strtok_ret( NULL, ",", &playersp );
++			}
++		}
++		else if ( 0 == strcmp( key, "status" ) )
++		{
++			if ( 200 != atoi( val ) )
++			{
++				server->server_name = DOWN;
++				return DONE_FORCE;
++			}
++		}
++		else
++		{
++			add_rule( server, key, val, NO_FLAGS);
++		}
++
++		s = strtok_ret( NULL, "\x0d\x0a", &linep );
++	}
++
++	gettimeofday( &server->packet_time1, NULL );
++
++	return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/terraria.h qstat2/terraria.h
+--- qstat-2.11/terraria.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/terraria.h	2012-02-07 08:55:59.913866000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Terraria / TShock protocol
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TERRARIA_H
++#define QSTAT_TERRARIA_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_terraria_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_terraria_request_packet( struct qserver *server );
++
++#endif
++
+Binary files qstat-2.11/tests/etqw_1.4_test1 and qstat2/tests/etqw_1.4_test1 differ
+Binary files qstat-2.11/tests/etqw_1.4_test2 and qstat2/tests/etqw_1.4_test2 differ
+diff -urP qstat-2.11/tests/etqws.pl qstat2/tests/etqws.pl
+--- qstat-2.11/tests/etqws.pl	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tests/etqws.pl	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,53 @@
++#!/usr/bin/perl -w
++# simple responder for doom3/quake4/etqw queries
++
++use strict;
++use IO::Socket::INET;
++use IO::File;
++
++my $sock = IO::Socket::INET->new(
++	LocalAddr => 'localhost',
++	Proto     => 'udp');
++
++print $sock->sockport(),"\n";
++
++my $rin = '';
++vec($rin, $sock->fileno, 1) = 1;
++
++my @files = @ARGV;
++die "USAGE: $0 <file>" unless @files;
++
++sub _stat_size($)
++{
++	my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
++	      $atime,$mtime,$ctime,$blksize,$blocks) = stat($_[0]);
++	return $size;
++}
++
++sub readfile($)
++{
++	my $fn = shift;
++	print "reading $fn\n";
++	my $f = IO::File->new($fn, "r");
++	my $buf;
++	$f->read($buf, _stat_size($f));
++	$f->close;
++	return $buf;
++}
++
++while (select(my $rout = $rin, undef, undef, undef)) {
++	my $data = '';
++	my $hispaddr;
++	$hispaddr = $sock->recv($data, 0xffff, 0) || die "recv: $!";
++	my ($port, $hisiaddr) = sockaddr_in($hispaddr);
++	printf '%d bytes from %s:%d'."\n", length($data), inet_ntoa($hisiaddr), $port;
++
++	if($data !~ /^\xff\xffgetInfo/) {
++		printf 'invalid packet from %s:%d'."\n", inet_ntoa($hisiaddr), $port;
++		next;
++	}
++
++	my $buf = readfile($files[0]);
++
++	$sock->send($buf, 0, $hispaddr);
++}
+diff -urP qstat-2.11/tests/xml.pl qstat2/tests/xml.pl
+--- qstat-2.11/tests/xml.pl	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/tests/xml.pl	2008-04-27 12:06:26.613995000 -0400
+@@ -0,0 +1,65 @@
++#!/usr/bin/perl -w
++# simple qstat xml output parser, prints values specified as command
++# line arguments
++#
++# Author: Ludwig Nussel
++#
++# Usage Examples:
++#
++# Print server name:
++#   "qstat/server/name"
++#
++# Print second server rule (numbered from zero):
++#   "qstat/server/rules/rule/1"
++#
++# Print the rule with name "gamename":
++#   "qstat/server/rules/rule/[name=gamename]"
++#
++# Print name of sixth player:
++#   "qstat/server/players/player/5/name"
++#
++# Print clan of player with name "suCk3r":
++#   "qstat/server/players/player/[name=suCk3r]/clan"
++
++use strict;
++use XML::Bare;
++use Data::Dumper;
++
++sub getvalue {
++	my $x = shift;
++	my @a = split(/\//, shift);
++	for my $n (@a) {
++		if ($n =~ /^[[:digit:]]+$/) {
++			return undef unless exists $x->[$n];
++			$x = $x->[$n];
++		} elsif ($n =~ /\[(.*)=(.*)\]/) {
++			my ($k, $v) = ($1, $2);
++			my $r;
++			for my $i (@$x) {
++				next unless exists $i->{$k};
++				if($i->{$k}->{value} eq $v) {
++					$r = $i;
++					last;
++				}
++			}
++			return undef unless $r;
++			$x = $r;
++		} else {
++			return undef unless exists $x->{$n};
++			$x = $x->{$n};
++		}
++	}
++	return $x->{value};
++}
++
++sub printvalue {
++	my $val = getvalue(@_);
++	$val = "(undefined)" unless defined $val;
++	print $val, "\n"
++}
++
++my $xml = XML::Bare->new(text => join('', <STDIN>))->parse();
++
++for (@ARGV) {
++	printvalue($xml, $_);
++}
+diff -urP qstat-2.11/tm.c qstat2/tm.c
+--- qstat-2.11/tm.c	2006-08-14 04:51:21.000000000 -0500
++++ qstat2/tm.c	2009-07-02 06:11:41.032181000 -0400
+@@ -22,35 +22,48 @@
+ #include "qstat.h"
+ #include "packet_manip.h"
+ 
++#define TM_XML_PREFIX "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n"
++#define TM_XML_SUFFIX "</data></array></value></param></params>\n</methodCall>"
++#define TM_SERVERINFO "<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n"
++#define TM_PLAYERLIST "<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n"
++#define TM_AUTH_TEMPLATE "<value><struct>\n<member><name>methodName</name><value><string>Authenticate</string></value></member>\n<member><name>params</name><value><array><data>\n<value><string>%s</string></value>\n<value><string>%s</string></value></data></array></value></member></struct></value>\n"
+ 
+-void send_tm_request_packet( struct qserver *server )
++query_status_t send_tm_request_packet( struct qserver *server )
+ {
+ 	char buf[2048];
+ 	char *xmlp = buf + 8;
+ 	unsigned int len;
++	char *user = get_param_value( server, "user", NULL );
++	char *password = get_param_value( server, "password", NULL );
+ 
+ 	if ( ! server->protocol_version )
+ 	{
+ 		// No seen the version yet wait
+ 		// register_send here to ensure that timeouts function correctly
+-		register_send( server );
+-		return;
++		return register_send( server );
+ 	}
+ 
+-	if ( get_player_info )
++	// build the query xml
++	len = sprintf( xmlp, TM_XML_PREFIX );
++
++	if ( user != NULL && password != NULL )
+ 	{
+-		server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
+-		// TODO: add more calls to get full player info?
+-		strcpy( xmlp, "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n</data></array></value></param></params>\n</methodCall>" );
++		len += sprintf( xmlp + len, TM_AUTH_TEMPLATE, user, password );
+ 	}
+ 	else
+ 	{
+-		server->flags |= TF_STATUS_QUERY;
+-		strcpy( xmlp, "<?xml version=\"1.0\"?>\n<methodCall>\n<methodName>system.multicall</methodName>\n<params><param><value><array><data>\n<value><struct><member><name>methodName</name><value>GetServerOptions</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetCurrentChallengeInfo</value></member><member><name>params</name><value><array><data></data></array></value></member></struct></value>\n<value><struct><member><name>methodName</name><value>GetPlayerList</value></member><member><name>params</name><value><array><data><value><i4>100</i4></value><value><i4>0</i4></value></data></array></value></member></struct></value>\n</data></array></value></param></params>\n</methodCall>" );
++		// Default to User / User
++		len += sprintf( xmlp + len, TM_AUTH_TEMPLATE, "User", "User" );
+ 	}
+ 
++	// Always get Player info otherwise player count is invalid
++	// TODO: add more calls to get full player info?
++	server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++	len += sprintf( xmlp + len, TM_SERVERINFO );
++	len += sprintf( xmlp + len, TM_PLAYERLIST );
++	len += sprintf( xmlp + len, TM_XML_SUFFIX );
++
+ 	// First 4 bytes is the length of the request
+-	len = strlen( xmlp );
+ 	memcpy( buf, &len, 4 );
+ 	// Second 4 bytes is the handle identifier ( id )
+ 	memcpy( buf+4, &server->challenge, 4 );
+@@ -59,13 +72,14 @@
+ 	// we expect at least 1 packet response
+ 	server->saved_data.pkt_max = 1;
+ 
+-	send_packet( server, buf, len + 8 );
++	return send_packet( server, buf, len + 8 );
+ }
+ 
+ 
+-void deal_with_tm_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_tm_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+-	char *s, *end;
++	char *s;
++	char *pkt = rawpkt;
+ 	char *key = NULL, *value = NULL, *tmpp = NULL;
+ 	char fullname[256];
+ 	struct player *player = NULL;
+@@ -75,20 +89,30 @@
+ 	debug( 2, "processing..." );
+ 
+ 	s = rawpkt;
+-	if ( 4 == pktlen && 0 == memcmp( rawpkt, "\x0b\x00\x00\x00", 4 ) )
++
++	// We may get the setup handle and the protocol version in one packet we may not
++	// So we continue to parse if we see the handle
++	if ( 4 <= pktlen && 0 == memcmp( pkt, "\x0b\x00\x00\x00", 4 ) )
+ 	{
+ 		// setup handle identifier
+ 		// greater 2^31 = XML-RPC, less = callback
+ 		server->challenge = 0x80000001;
+-		return;
++		if ( 4 == pktlen )
++		{
++			return 0;
++		}
++		pktlen -= 4;
++		pkt += 4;
+ 	}
+-	else if ( 10 < pktlen && 1 == sscanf( rawpkt, "GBXRemote %d", &server->protocol_version ) )
++
++	if ( 11 <= pktlen && 1 == sscanf( pkt, "GBXRemote %d", &server->protocol_version ) )
+ 	{
+ 		// Got protocol version send request
+ 		send_tm_request_packet( server );
+-		return;	
++		return 0;
+ 	}
+-	else if ( 8 <= pktlen && 0 == memcmp( rawpkt+4, &server->challenge, 4 ) )
++
++	if ( 8 <= pktlen && 0 == memcmp( pkt+4, &server->challenge, 4 ) )
+ 	{
+ 		// first 4 bytes = the length
+ 		// Note: We use pkt_id to store the length of the expected packet
+@@ -104,9 +128,9 @@
+ 			if ( ! add_packet( server, len, 0, 2, pktlen, rawpkt, 1 ) )
+ 			{
+ 				// fatal error e.g. out of memory
+-				return;
++				return -1;
+ 			}
+-			return;
++			return 0;
+ 		}
+ 		else
+ 		{
+@@ -117,7 +141,8 @@
+ 	}
+ 
+ 	total_len = combined_length( server, server->saved_data.pkt_id );
+-	expected_len = server->saved_data.pkt_id; 
++	expected_len = server->saved_data.pkt_id;
++	debug( 2, "total: %d, expected: %d\n", total_len, expected_len );
+ 	if ( total_len < expected_len + 8 )
+ 	{
+ 		// we dont have a complete response add the packet
+@@ -136,15 +161,15 @@
+ 		if ( ! add_packet( server, server->saved_data.pkt_id, pkt_max - 1, new_max, pktlen, rawpkt, 1 ) )
+ 		{
+ 			// fatal error e.g. out of memory
+-			return;
++			return -1;
+ 		}
+ 
+ 		if ( last )
+ 		{
+ 			// we are the last packet run combine to call us back
+-			combine_packets( server );
++			return combine_packets( server );
+ 		}
+-		return;
++		return 0;
+ 	}
+ 
+ 	server->n_servers++;
+@@ -157,11 +182,18 @@
+ 		gettimeofday( &server->packet_time1, NULL);
+ 	}
+ 
+-	rawpkt[pktlen]= '\0';
+-	end = &rawpkt[pktlen];
++	// Terminate the packet data
++	pkt = (char*)malloc( pktlen + 1 );
++	if ( NULL == pkt )
++	{
++		debug( 0, "Failed to malloc memory for packet terminator\n" );
++		return MEM_ERROR;
++	}
++	memcpy( pkt, rawpkt, pktlen );
++	pkt[pktlen] = '\0';
+ 
+ //fprintf( stderr, "S=%s\n", s );
+-	s = strtok( s, "\015\012" );
++	s = strtok( pkt + 8, "\015\012" );
+ 	while ( NULL != s )
+ 	{
+ //fprintf( stderr, "S=%s\n", s );
+@@ -177,7 +209,7 @@
+ 			value = NULL;
+ 			continue;
+ 		}
+-		else if ( 0 == strncmp( s, "<value>", 7 ) )
++		else if ( NULL != key && 0 == strncmp( s, "<value>", 7 ) )
+ 		{
+ 			// value
+ 			s += 7;
+@@ -223,7 +255,7 @@
+ 			method_response++;
+ 		}
+ 
+-		if ( NULL != value )
++		if ( NULL != value && NULL != key )
+ 		{
+ 			switch( method_response )
+ 			{
+@@ -297,9 +329,13 @@
+ 		s = strtok( NULL, "\015\012" );
+ 	}
+ 
++	free( pkt );
++
+ 	if ( 0 == strncmp( rawpkt + pktlen - 19, "</methodResponse>", 17 ) )
+ 	{
+ 		// last packet seen
+-		cleanup_qserver( server, 1 );
++		return DONE_FORCE;
+ 	}
++
++	return INPROGRESS;
+ }
+diff -urP qstat-2.11/tm.h qstat2/tm.h
+--- qstat-2.11/tm.h	2006-08-04 15:42:09.000000000 -0500
++++ qstat2/tm.h	2009-07-02 06:11:41.032181000 -0400
+@@ -7,14 +7,14 @@
+  *
+  * Licensed under the Artistic License, see LICENSE.txt for license terms
+  */
+-#ifndef QSTAT_GPS_H
+-#define QSTAT_GPS_H
++#ifndef QSTAT_TM_H
++#define QSTAT_TM_H
+ 
+ #include "qserver.h"
+ 
+ // Packet processing methods
+-void deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_tm_request_packet( struct qserver *server );
++query_status_t deal_with_tm_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_tm_request_packet( struct qserver *server );
+ 
+ #endif
+ 
+diff -urP qstat-2.11/ts2.c qstat2/ts2.c
+--- qstat-2.11/ts2.c	2006-05-24 09:26:00.000000000 -0500
++++ qstat2/ts2.c	2009-09-30 14:28:16.595305000 -0400
+@@ -2,7 +2,7 @@
+  * qstat 2.8
+  * by Steve Jankowski
+  *
+- * Gamespy query protocol
++ * Teamspeak 2 query protocol
+  * Copyright 2005 Steven Hartland
+  *
+  * Licensed under the Artistic License, see LICENSE.txt for license terms
+@@ -22,7 +22,7 @@
+ #include "packet_manip.h"
+ 
+ 
+-void send_ts2_request_packet( struct qserver *server )
++query_status_t send_ts2_request_packet( struct qserver *server )
+ {
+ 	char buf[256];
+ 
+@@ -42,25 +42,25 @@
+ 		server->saved_data.pkt_index = 1;
+ 	}
+ 
+-	send_packet( server, buf, strlen( buf ) );
++	return send_packet( server, buf, strlen( buf ) );
+ }
+ 
+ 
+-void deal_with_ts2_packet( struct qserver *server, char *rawpkt, int pktlen )
++query_status_t deal_with_ts2_packet( struct qserver *server, char *rawpkt, int pktlen )
+ {
+ 	char *s, *end;
+-	int ping, connect_time;
++	int ping, connect_time, mode = 0;
+ 	char name[256];
+ 	debug( 2, "processing..." );
+ 
+ 	server->n_servers++;
+-	if ( server->server_name == NULL)
+-	{
+-		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
+-	}
+-	else
++	server->n_requests++;
++	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++	if ( 0 == pktlen )
+ 	{
+-		gettimeofday( &server->packet_time1, NULL);
++		// Invalid password
++		return REQ_ERROR;
+ 	}
+ 
+ 	rawpkt[pktlen]= '\0';
+@@ -71,67 +71,96 @@
+ 
+ 	while ( NULL != s )
+ 	{
+-		char *key = s;
+-		char *value = strchr( key, '=' );
+-		if ( NULL != value )
++		if ( 0 == mode )
+ 		{
+-			// Server Rule
+-			*value = '\0';
+-			value++;
+-			if ( 0 == strcmp( "server_name", key ) )
+-			{
+-				server->server_name = strdup( value );
++			// Rules
++			char *key = s;
++			char *value = strchr( key, '=' );
++			if ( NULL != value )
++			{
++				// Server Rule
++				*value = '\0';
++				value++;
++				if ( 0 == strcmp( "server_name", key ) )
++				{
++					server->server_name = strdup( value );
++				}
++				else if ( 0 == strcmp( "server_udpport", key ) )
++				{
++					change_server_port( server, atoi( value ), 0 );
++					add_rule( server, key, value, NO_FLAGS );
++				}
++				else if ( 0 == strcmp( "server_maxusers", key ) )
++				{
++					server->max_players = atoi( value );
++				}
++				else if ( 0 == strcmp( "server_currentusers", key ) )
++				{
++					server->num_players = atoi( value);
++				}
++				else
++				{
++					add_rule( server, key, value, NO_FLAGS);
++				}
++			}
++			else if ( 0 == strcmp( "OK", s ) )
++			{
++				// end of rules request
++				server->saved_data.pkt_index--;
++				mode++;
++			}
++			else if ( 0 == strcmp( "[TS]", s ) )
++			{
++				// nothing to do
++			}
++			else if ( 0 == strcmp( "ERROR, invalid id", s ) )
++			{
++				// bad server
++				server->server_name = DOWN;
++				server->saved_data.pkt_index = 0;
+ 			}
+-			else if ( 0 == strcmp( "server_udpport", key ) )
++		}
++		else if ( 1 == mode )
++		{
++			// Player info
++			if ( 3 == sscanf( s, "%*d %*d %*d %*d %*d %*d %*d %d %d %*d %*d %*d %*d \"0.0.0.0\" \"%255[^\"]", &ping, &connect_time, name ) )
+ 			{
+-				change_server_port( server, atoi( value ), 0 );
+-				add_rule( server, key, value, NO_FLAGS );
++				// Player info
++				struct player *player = add_player( server, server->n_player_info );
++				if ( NULL != player )
++				{
++					player->name = strdup( name );
++					player->ping = ping;
++					player->connect_time = connect_time;
++				}
+ 			}
+-			else if ( 0 == strcmp( "server_maxusers", key ) )
++			else if ( 0 == strcmp( "OK", s ) )
+ 			{
+-				server->max_players = atoi( value );
++				// end of rules request
++				server->saved_data.pkt_index--;
++				mode++;
+ 			}
+-			else if ( 0 == strcmp( "server_currentusers", key ) )
++			else if ( 0 == strcmp( "[TS]", s ) )
+ 			{
+-				server->num_players = atoi( value);
++				// nothing to do
+ 			}
+-			else
++			else if ( 0 == strcmp( "ERROR, invalid id", s ) )
+ 			{
+-				add_rule( server, key, value, NO_FLAGS);
++				// bad server
++				server->server_name = DOWN;
++				server->saved_data.pkt_index = 0;
+ 			}
+ 		}
+-		else if ( 3 == sscanf( s, "%*d %*d %*d %*d %*d %*d %*d %d %d %*d %*d %*d %*d \"0.0.0.0\" \"%255[^\"]", &ping, &connect_time, name ) )
+-		{
+-			// Player info
+-			struct player *player = add_player( server, server->n_player_info );
+-			if ( NULL != player )
+-			{
+-				player->name = strdup( name );
+-				player->ping = ping;
+-				player->connect_time = connect_time;
+-			}
+-		}
+-		else if ( 0 == strcmp( "OK", s ) )
+-		{
+-			// end of request result
+-			server->saved_data.pkt_index--;
+-		}
+-		else if ( 0 == strcmp( "[TS]", s ) )
+-		{
+-			// nothing to do
+-		}
+-		else if ( 0 == strcmp( "ERROR, invalid id", s ) )
+-		{
+-			// bad server
+-			server->server_name = DOWN;
+-			server->saved_data.pkt_index = 0;
+-		}
+ 		s = strtok( NULL, "\015\012" );
+ 	}
+ 
++	gettimeofday( &server->packet_time1, NULL );
++
+ 	if ( 0 == server->saved_data.pkt_index )
+ 	{
+ 		server->map_name = strdup( "N/A" );
+-		cleanup_qserver( server, 1 );
++		return DONE_FORCE;
+ 	}
++
++	return INPROGRESS;
+ }
+diff -urP qstat-2.11/ts2.h qstat2/ts2.h
+--- qstat-2.11/ts2.h	2005-09-21 09:46:11.000000000 -0500
++++ qstat2/ts2.h	2009-07-02 06:11:41.032181000 -0400
+@@ -7,14 +7,14 @@
+  *
+  * Licensed under the Artistic License, see LICENSE.txt for license terms
+  */
+-#ifndef QSTAT_GPS_H
+-#define QSTAT_GPS_H
++#ifndef QSTAT_TS2_H
++#define QSTAT_TS2_H
+ 
+ #include "qserver.h"
+ 
+ // Packet processing methods
+-void deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen );
+-void send_ts2_request_packet( struct qserver *server );
++query_status_t deal_with_ts2_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ts2_request_packet( struct qserver *server );
+ 
+ #endif
+ 
+diff -urP qstat-2.11/ts3.c qstat2/ts3.c
+--- qstat-2.11/ts3.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ts3.c	2012-10-10 14:34:51.376884000 -0400
+@@ -0,0 +1,476 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Teamspeak 3 query protocol
++ * Copyright 2009 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#else
++#include <winsock.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++char *decode_ts3_val( char *val )
++{
++	val = str_replace( val, "\\\\", "\\" );
++	val = str_replace( val, "\\/", "/" );
++	val = str_replace( val, "\\s", " " );
++	val = str_replace( val, "\\p", "|" );
++	val = str_replace( val, "\\a", "\007" );
++	val = str_replace( val, "\\b", "\010" );
++	val = str_replace( val, "\\f", "\014" );
++	val = str_replace( val, "\\n", "\012" );
++	val = str_replace( val, "\\r", "\015" );
++	val = str_replace( val, "\\t", "\011" );
++
++	return str_replace( val, "\\v", "\013" );
++}
++
++// NOTE: replace must be smaller or equal in size to find
++char *str_replace( char *source, char *find, char *replace )
++{
++	char *s = strstr( source, find );
++	int rlen = strlen( replace );
++	int flen = strlen( find );
++	while( NULL != s )
++	{
++		strncpy( s, replace, rlen );
++		strcpy( s + rlen, s + flen );
++		s += rlen;
++		s = strstr( s, find );
++	}
++
++	return source;
++}
++
++int all_ts3_servers( struct qserver *server )
++{
++	return ( 1 == get_param_i_value( server, "allservers", 0 ) ) ? 1 : 0;
++}
++
++query_status_t send_ts3_all_servers_packet( struct qserver *server )
++{
++	char buf[256], *password, *username;
++	switch ( server->challenge )
++	{
++	case 0:
++		// Not seen a challenge yet, wait for it
++		server->n_servers = 999;
++		return INPROGRESS;
++
++	case 1:
++		password = get_param_value( server, "password", "" );
++		if ( 0 != strlen( password ) )
++		{
++			username = get_param_value( server, "username", "serveradmin" );
++			sprintf( buf, "login %s %s\015\012", username, password );
++			break;
++		}
++		// NOTE: no break so we fall through
++		server->challenge++;
++
++	case 2:
++		// NOTE: we currently don't support player info
++		server->flags |= TF_STATUS_QUERY;
++		server->n_servers = 3;
++		sprintf( buf, "serverlist\015\012" );
++		break;
++
++	case 3:
++		sprintf( buf, "quit\015\012" );
++		break;
++
++	case 4:
++		return DONE_FORCE;
++	}
++
++	server->saved_data.pkt_max = -1;
++
++	return send_packet( server, buf, strlen( buf ) );
++}
++
++
++query_status_t send_ts3_single_server_packet( struct qserver *server )
++{
++	char buf[256], *password, *username;
++	int serverport;
++	switch ( server->challenge )
++	{
++	case 0:
++		// Not seen a challenge yet, wait for it
++		server->n_servers = 999;
++		return INPROGRESS;
++
++	case 1:
++		// Login if needed
++		password = get_param_value( server, "password", "" );
++		if ( 0 != strlen( password ) )
++		{
++			username = get_param_value( server, "username", "serveradmin" );
++			sprintf( buf, "login %s %s\015\012", password, username );
++			break;
++		}
++		// NOTE: no break so we fall through
++		server->challenge++;
++
++	case 2:
++		// Select port
++		serverport = get_param_i_value( server, "port", 0 ); 
++		change_server_port( server, serverport, 1 );
++		// NOTE: we use n_servers as an indication of how many responses we are expecting to get
++		if ( get_player_info )
++		{
++			server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++			server->n_servers = 5;
++		}
++		else
++		{
++			server->flags |= TF_STATUS_QUERY;
++			server->n_servers = 4;
++		}
++		sprintf( buf, "use port=%d\015\012", serverport );
++		break;
++
++	case 3:
++		// Server Info
++		sprintf( buf, "serverinfo\015\012" );
++		break;
++
++	case 4:
++		// Player Info, Quit or Done
++		sprintf( buf, ( get_player_info ) ? "clientlist\015\012" : "quit\015\012" );
++		break;
++
++	case 5:
++		// Quit or Done
++		if ( get_player_info )	
++		{
++			sprintf( buf, "quit\015\012" );
++		}
++		else
++		{
++			return DONE_FORCE;
++		}
++		break;
++	}
++	server->saved_data.pkt_max = -1;
++
++	return send_packet( server, buf, strlen( buf ) );
++}
++
++query_status_t send_ts3_request_packet( struct qserver *server )
++{
++	debug( 3, "send_ts3_request_packet: state = %ld", server->challenge );
++	return ( all_ts3_servers( server ) ) ? send_ts3_all_servers_packet( server ) : send_ts3_single_server_packet( server );
++}
++
++int valid_ts3_response( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *end = &rawpkt[pktlen-1];
++	char *s = rawpkt;
++
++	if ( 0 == strncmp( "TS3", s, 3 ) )
++	{
++		// Challenge
++		return 1;
++	}
++
++	while ( NULL != s )
++	{
++		// use response
++		if ( 0 == strncmp( "error", s, 5 ) )
++		{
++			// end cmd response
++			if ( 0 == strncmp( "error id=0", s, 9 ) )
++			{
++				return 1;
++			}
++			else
++			{
++				// bad server
++				server->server_name = DOWN;
++				server->saved_data.pkt_index = 0;
++				return DONE_FORCE;
++			}
++		}
++		s = strstr( s, "\012\015" );
++		if ( NULL != s )
++		{
++			s = ( s + 2 < end ) ? s + 2 : NULL;
++		}
++	}
++
++	return 0;
++}
++
++query_status_t deal_with_ts3_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *end, *player_name = "unknown";
++	int valid_response = 0, mode = 0, all_servers = 0;
++	char last_char;
++	unsigned short port = 0, down = 0, auth_seen = 0;
++	debug( 2, "processing..." );
++
++	if ( 0 == pktlen )
++	{
++		// Invalid password
++		return REQ_ERROR;
++	}
++
++	last_char = rawpkt[pktlen-1];
++	rawpkt[pktlen-1] = '\0';
++	end = &rawpkt[pktlen-1];
++	s = rawpkt;
++	all_servers = all_ts3_servers( server );
++
++	debug( 3, "packet: combined = %d, challenge = %ld, n_servers = %d", server->combined, server->challenge, server->n_servers );
++	if ( ! server->combined )
++	{
++		server->retry1 = n_retries;
++		if ( 0 == server->n_requests )
++		{
++			server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
++			server->n_requests++;
++		}
++
++		valid_response = valid_ts3_response( server, rawpkt, pktlen );
++
++		if ( 0 > valid_response )
++		{
++			// Error occured
++			return valid_response;
++		}
++
++		// only update on the original packed not on a combined call
++		server->challenge += valid_response;
++
++		debug( 3, "new packet: valid_response = %d, challenge = %ld, n_servers = %d", valid_response, server->challenge, server->n_servers );
++
++		if ( valid_response )
++		{
++			// Got a valid response, send the next request
++			int ret = send_ts3_request_packet( server );
++			if ( 0 != ret )
++			{
++				// error sending packet
++				debug( 4, "Request failed: %d", ret );
++				return ret;
++			}
++		}
++
++		if ( server->n_servers >= server->challenge )
++		{
++			// response fragment recieved
++			int pkt_id;
++			int pkt_max;
++
++			// We're expecting more to come
++			debug( 5, "fragment recieved..." );
++			pkt_id = packet_count( server );
++			pkt_max = ( 0 == pkt_id ) ? 2 : pkt_id + 1;
++			rawpkt[pktlen-1] = last_char; // restore the last character
++			if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
++			{
++				// fatal error e.g. out of memory
++				return MEM_ERROR;
++			}
++
++			// combine_packets will call us recursively
++			return combine_packets( server );
++		}
++	}
++	else if ( server->n_servers > server->challenge )
++	{
++		// recursive call which is still incomplete
++		return INPROGRESS;
++	}
++
++	// Correct ping
++	// Not quite right but gives a good estimate
++	server->ping_total = ( server->ping_total * server->n_requests ) / 2;
++
++	debug( 3, "processing response..." );
++
++	s = strtok( rawpkt, "\012\015 |" );
++
++	// NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
++	while ( NULL != s )
++	{
++		debug( 2, "LINE: %d, %s\n", mode, s );
++		switch ( mode )
++		{
++		case 0:
++			// prompt, use or serverlist response
++			if ( 0 == strcmp( "TS3", s ) )
++			{
++				// nothing to do unless in all servers mode
++				if ( 1 == all_servers )
++				{
++					mode++;
++				}
++			}
++			else if ( 0 == strncmp( "error", s, 5 ) )
++			{
++				// end of use response
++				mode++;
++			}
++			break;
++
++		case 1:
++			// serverinfo or serverlist response including condition authentication
++			if ( 0 == auth_seen && 0 != strlen( get_param_value( server, "password", "" ) ) && 0 == strncmp( "error", s, 5 ) )
++			{
++				// end of auth response
++				auth_seen = 1;
++			}
++			else if ( 0 == strncmp( "error", s, 5 ) )
++			{
++				// end of serverinfo response
++				mode++;
++			}
++			else
++			{
++				// Server Rule
++				char *key = s;
++				char *value = strchr( key, '=' );
++				if ( NULL != value )
++				{
++					*value = '\0';
++					value++;
++					debug( 6, "Rule: %s = %s\n", key, value );
++					if ( 0 == strcmp( "virtualserver_name", key ) )
++					{
++						if ( 1 == all_servers )
++						{
++							struct qserver *new_server = add_qserver_byaddr( ntohl( server->ipaddr ), port, server->type, NULL );
++							if ( NULL != new_server )
++							{
++								if ( down )
++								{
++									// Status indicates this server is actually offline
++									new_server->server_name = DOWN;
++								}
++								else
++								{
++									new_server->max_players = server->max_players;
++									new_server->num_players = server->num_players;
++									new_server->server_name = strdup( decode_ts3_val( value ) );
++									new_server->map_name = strdup( "N/A" );
++									new_server->ping_total = server->ping_total;
++									new_server->n_requests = server->n_requests;
++								}
++								cleanup_qserver( new_server, FORCE );
++							}
++							down = 0;
++						}
++						else
++						{
++							server->server_name = strdup( decode_ts3_val( value ) );
++						}
++					}
++					else if ( 0 == strcmp( "virtualserver_port", key ) )
++					{
++						port = atoi( value );
++						change_server_port( server, port, 0 );
++						add_rule( server, key, value, NO_FLAGS );
++					}
++					else if ( 0 == strcmp( "virtualserver_maxclients", key ) )
++					{
++						server->max_players = atoi( value );
++					}
++					else if ( 0 == strcmp( "virtualserver_clientsonline", key ) )
++					{
++						server->num_players = atoi( value );
++					}
++					else if ( 0 == strcmp( "virtualserver_queryclientsonline", key ) )
++					{
++						// clientsonline includes queryclientsonline so remove these from our count
++						server->num_players -= atoi( value );
++					}
++					else if ( 0 == strcmp( "virtualserver_status", key ) && 0 != strcmp( "online", value ) )
++					{
++						// Server is actually offline to client so display as down
++						down = 1;
++						if ( 1 != all_servers )
++						{
++							server->server_name = DOWN;
++							//server->saved_data.pkt_index = 0;
++							return DONE_FORCE;
++						}
++					}
++					else if ( 0 == strcmp( "id", key ) || 0 == strcmp( "msg", key ) )
++					{
++						// Ignore details from the response code
++					}
++					else if ( 1 != all_servers )
++					{
++						add_rule( server, key, value, NO_FLAGS);
++					}
++				}
++			}
++			break;
++
++		case 2:
++			// clientlist response
++			if ( 0 == strncmp( "error", s, 5 ) )
++			{
++				// end of serverinfo response
++				mode++;
++			}
++			else
++			{
++				// Client
++				char *key = s;
++				char *value = strchr( key, '=' );
++				if ( NULL != value )
++				{
++					*value = '\0';
++					value++;
++					debug( 6, "Player: %s = %s\n", key, value );
++					if ( 0 == strcmp( "client_nickname", key ) )
++					{
++						player_name = value;
++					}
++					else if ( 0 == strcmp( "clid", key ) )
++					{
++					}
++					else if ( 0 == strcmp( "client_type", key ) && 0 == strcmp( "0", value ) )
++					{
++						struct player *player = add_player( server, server->n_player_info );
++						if ( NULL != player )
++						{
++							player->name = strdup( decode_ts3_val( player_name ) );
++						}
++					}
++					else if ( 0 == strcmp( "id", key ) || 0 == strcmp( "msg", key ) )
++					{
++						// Ignore details from the response code
++					}
++				}
++			}
++			break;
++		}
++		s = strtok( NULL, "\012\015 |" );
++	}
++
++	gettimeofday( &server->packet_time1, NULL );
++
++	server->map_name = strdup( "N/A" );
++	return DONE_FORCE;
++}
++
+diff -urP qstat-2.11/ts3.h qstat2/ts3.h
+--- qstat-2.11/ts3.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ts3.h	2009-12-21 13:38:56.452334000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Teamspeak 3 protocol
++ * Copyright 2005 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_TS3_H
++#define QSTAT_TS3_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_ts3_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ts3_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/ut2004.c qstat2/ut2004.c
+--- qstat-2.11/ut2004.c	2006-05-28 06:48:38.000000000 -0500
++++ qstat2/ut2004.c	2009-07-02 06:12:25.035239000 -0400
+@@ -30,47 +30,47 @@
+  */
+ static void bin2hex(const char* in, size_t len, char* out);
+ 
+-#define CD_KEY_LENGTH     23
++#define CD_KEY_LENGTH	 23
+ 
+ // arbitrary
+ #define MAX_LISTING_RECORD_LEN 0x04FF
+ 
+-#define RESPONSE_OFFSET_CDKEY     5
++#define RESPONSE_OFFSET_CDKEY	 5
+ #define RESPONSE_OFFSET_CHALLENGE 39
+ 
+ static const char challenge_response[] = {
+-    0x68, 0x00, 0x00, 0x00,  '!', 0xCD, 0xCD, 0xCD, 
++    0x68, 0x00, 0x00, 0x00,  '!', 0xCD, 0xCD, 0xCD,
+     /* length             |   !   MD5SUM, CD is placeholder */
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+     0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00,
+-                                         '!', 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 
+-    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00, 
++                                         '!', 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
++    0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00,
+ 
+     0x0c,  'U',  'T',  '2',  'K',  '4',  'C',  'L',
+     /*^^ 12 byte string  */
+-     'I',  'E',  'N',  'T', 0x00, 0xfb, 0x0c, 0x00, 
++     'I',  'E',  'N',  'T', 0x00, 0xfb, 0x0c, 0x00,
+     /*                          | unknown        */
+-    0x00, 0x06, 0x04,  'i',  'n',  't', 0x00, 0x00, 
+-    /*        |   ^^ 4 byte string          | ?  */    
+-    0x00, 0x00, 0x00, 0xee, 0xee, 0x00, 0x00, 0x11, 
++    0x00, 0x06, 0x04,  'i',  'n',  't', 0x00, 0x00,
++    /*        |   ^^ 4 byte string          | ?  */
++    0x00, 0x00, 0x00, 0xee, 0xee, 0x00, 0x00, 0x11,
+     /* unknown                                   */
+     0x00, 0x00, 0x00, 0x01 };
+ 
+ static const char approved[] = {
+     0x0e, 0x00, 0x00, 0x00, 0x09,  'A',  'P',  'P',
+-     'R',  'O',  'V',  'E',  'D', 0x00, 0x03, 0x00, 
++     'R',  'O',  'V',  'E',  'D', 0x00, 0x03, 0x00,
+     0x00, 0x00 };
+ 
+ static const char approved_response[] = {
+-    0x22, 0x00, 0x00, 0x00,  '!',  '0',  '0',  '0', 
+-     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', 
+-     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', 
+-     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', 
++    0x22, 0x00, 0x00, 0x00,  '!',  '0',  '0',  '0',
++     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0',
++     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0',
++     '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0',
+      '0',  '0',  '0',  '0',  '0', 0x00 };
+ 
+ static const char verified[] = {
+@@ -79,167 +79,171 @@
+ 
+ #if 0
+ struct server_listing_record_head {
+-    unsigned len;
+-    unsigned ip;
+-    short port;
+-    short queryport;
+-    char name[];
+-    // char map[]
++	unsigned len;
++	unsigned ip;
++	short port;
++	short queryport;
++	char name[];
++	// char map[]
+ };
+ 
+ struct server_listing_record_foot {
+-    unsigned char marker1[3];
+-    unsigned char unknown1;
+-    unsigned char maxplayers;
+-    unsigned char unknown2[4];
+-    unsigned char marker2[3];
++	unsigned char marker1[3];
++	unsigned char unknown1;
++	unsigned char maxplayers;
++	unsigned char unknown2[4];
++	unsigned char marker2[3];
+ };
+ #endif
+ 
+ static char cdkey[CD_KEY_LENGTH+1] = "";
+ 
+ enum ut2004_state {
+-    STATE_CHALLENGE = 0x00,
+-    STATE_APPROVED  = 0x01,
+-    STATE_VERIFIED  = 0x02,
+-    STATE_LISTING   = 0x03,
++	STATE_CHALLENGE = 0x00,
++	STATE_APPROVED  = 0x01,
++	STATE_VERIFIED  = 0x02,
++	STATE_LISTING   = 0x03,
+ };
+ 
+-void send_ut2004master_request_packet(struct qserver *server)
++query_status_t send_ut2004master_request_packet(struct qserver *server)
+ {
+-    if(server->n_packets)
+-    {
+-	cleanup_qserver(server, 1);
+-	return;
+-    }
+-
+-    if(!*cdkey)
+-    {
+-	char* param = get_param_value( server, "cdkey", NULL);
+-	if(!param)
+-	{
+-	    debug(0, "Error: missing cdkey parameter");
+-	    server->server_name = SYSERROR;
+-	    cleanup_qserver(server, 1);
+-	    return;
+-	}
+-
+-	if(*param == '/')
+-	{
+-	    FILE* fp = fopen(param, "r");
+-	    if(!fp || fread(cdkey, 1, CD_KEY_LENGTH, fp) != CD_KEY_LENGTH)
+-	    {
+-		debug(0, "Error: can't key from %s", param);
+-		server->server_name = SYSERROR;
+-		cleanup_qserver(server, 1);
+-		if(fp) fclose(fp);
+-		return;
+-	    }
+-	    fclose(fp);
+-	}
+-	else if(strchr(param, '-') && strlen(param) == CD_KEY_LENGTH)
++	int ret;
++	if(server->n_packets)
+ 	{
+-	    memcpy(cdkey, param, CD_KEY_LENGTH);
++		return DONE_FORCE;
+ 	}
+-	else if(   *param == '$'
+-		&& (param = getenv(param+1)) // replaces param!
+-		&& strlen(param) == CD_KEY_LENGTH)
+-	{
+-	    memcpy(cdkey, param, CD_KEY_LENGTH);
+-	}
+-	else
++
++	if(!*cdkey)
+ 	{
+-	    debug(0, "Error: invalid cdkey parameter");
+-	    server->server_name = SYSERROR;
+-	    cleanup_qserver(server, 1);
+-	    return;
++		char* param = get_param_value( server, "cdkey", NULL);
++		if(!param)
++		{
++			debug(0, "Error: missing cdkey parameter");
++			server->server_name = SYSERROR;
++			return PKT_ERROR;
++		}
++
++		if(*param == '/')
++		{
++			FILE* fp = fopen(param, "r");
++			if(!fp || fread(cdkey, 1, CD_KEY_LENGTH, fp) != CD_KEY_LENGTH)
++			{
++				debug(0, "Error: can't key from %s", param);
++				server->server_name = SYSERROR;
++				if(fp)
++				{
++					fclose(fp);
++				}
++				return PKT_ERROR;
++			}
++			fclose(fp);
++		}
++		else if(strchr(param, '-') && strlen(param) == CD_KEY_LENGTH)
++		{
++			memcpy(cdkey, param, CD_KEY_LENGTH);
++		}
++		else if(   *param == '$'
++			&& (param = getenv(param+1)) // replaces param!
++			&& strlen(param) == CD_KEY_LENGTH)
++		{
++			memcpy(cdkey, param, CD_KEY_LENGTH);
++		}
++		else
++		{
++			debug(0, "Error: invalid cdkey parameter");
++			server->server_name = SYSERROR;
++			return PKT_ERROR;
++		}
+ 	}
+-    }
+ 
+-    qserver_send(server, NULL, 0);
++	ret = qserver_send(server, NULL, 0);
+ 
+ #if 0
+-    // XXX since we do not send but rather expect a reply directly after
+-    // connect it's pointless to retry doing nothing
+-    debug(0, "retry1: %d", server->retry1);
+-    server->retry1 = 0;
++	// XXX since we do not send but rather expect a reply directly after
++	// connect it's pointless to retry doing nothing
++	debug(0, "retry1: %d", server->retry1);
++	server->retry1 = 0;
+ #endif
+ 
+-    server->master_query_tag[0] = STATE_CHALLENGE;
++	server->master_query_tag[0] = STATE_CHALLENGE;
++	return ret;
+ }
+ 
+ static void ut2004_server_done(struct qserver* server)
+ {
+-    if(server->saved_data.next)
+-    {
+-	debug(0, "%d bytes of unprocessed data left. Premature EOF!?",
+-		server->saved_data.next->datalen);
+-	free(server->saved_data.next->data);
+-	free(server->saved_data.next);
+-	server->saved_data.next = NULL;
+-    }
++	if(server->saved_data.next)
++	{
++		debug(0, "%d bytes of unprocessed data left. Premature EOF!?", server->saved_data.next->datalen);
++		free(server->saved_data.next->data);
++		free(server->saved_data.next);
++		server->saved_data.next = NULL;
++	}
+ }
+ 
+ // we use n_servers to store number of used bytes in master_pkt so
+ // it needs to be divided by 6 when finished
+ static void ut2004_parse_record(struct qserver* server, char* pkt)
+ {
+-    char* dest;
++	char* dest;
+ 
+ #if 0
+-    unsigned ip;
+-    unsigned short port;
++	unsigned ip;
++	unsigned short port;
+ 
+-    memcpy(&ip, pkt+4, 4);
+-    port = swap_short_from_little(pkt+4+4);
++	memcpy(&ip, pkt+4, 4);
++	port = swap_short_from_little(pkt+4+4);
+ 
+-    debug(2, "got %d.%d.%d.%d:%hu", ip&0xff, (ip>>8)&0xff, (ip>>16)&0xff, (ip>>24)&0xff, port);
++	debug(2, "got %d.%d.%d.%d:%hu", ip&0xff, (ip>>8)&0xff, (ip>>16)&0xff, (ip>>24)&0xff, port);
+ #endif
+ 
+-    if(server->n_servers+6 > server->master_pkt_len)
+-    {
+-	if(!server->master_pkt_len)
+-	    server->master_pkt_len = 180;
+-	else
+-	    server->master_pkt_len *= 2;
+-	server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
+-    }
+-
+-    dest = server->master_pkt + server->n_servers;
+-
+-    memcpy(dest, pkt+4, 4 );
+-    dest[4] = pkt[9];
+-    dest[5] = pkt[8];
+-    server->n_servers += 6;
++	if(server->n_servers+6 > server->master_pkt_len)
++	{
++		if(!server->master_pkt_len)
++		{
++			server->master_pkt_len = 180;
++		}
++		else
++		{
++			server->master_pkt_len *= 2;
++		}
++		server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++	}
++
++	dest = server->master_pkt + server->n_servers;
++
++	memcpy(dest, pkt+4, 4 );
++	dest[4] = pkt[9];
++	dest[5] = pkt[8];
++	server->n_servers += 6;
+ }
+ 
+ static char* put_bytes(char* buf, const char* bytes, size_t len, size_t* left)
+ {
+-    if(!buf || len > *left)
++	if(!buf || len > *left)
+ 	return NULL;
+ 
+-    memcpy(buf, bytes, len);
+-    *left -= len;
++	memcpy(buf, bytes, len);
++	*left -= len;
+ 
+-    return buf+len;
++	return buf+len;
+ }
+ 
+ static char* put_string(char* buf, const char* string, size_t* left)
+ {
+-    size_t len = strlen(string)+1;
+-    char l;
++	size_t len = strlen(string)+1;
++	char l;
+ 
+-    if(!buf || len > 0xFF || *left < len+1)
+-	return NULL;
++	if(!buf || len > 0xFF || *left < len+1)
++		return NULL;
+ 
+-    l = len;
++	l = len;
+ 
+-    buf = put_bytes(buf, &l, 1, left);
+-    return put_bytes(buf, string, len, left);
++	buf = put_bytes(buf, &l, 1, left);
++	return put_bytes(buf, string, len, left);
+ }
+ 
+ /** \brief assemble the server filter and send the master query
+-  
++
+   the query consists of four bytes length (excluding the four length bytes), a
+   null byte and then the number of item pairs that follow.
+ 
+@@ -249,416 +253,409 @@
+  */
+ static int ut2004_send_query(struct qserver* server)
+ {
+-    char buf[4096] = {0};
+-    size_t left = sizeof(buf);
+-    char *b = buf;
+-    char *param, *r, *sep= "";
+-    unsigned flen = 0;
+-    unsigned char items = 0;
+-
+-    // header is done later
+-    b += 6;
+-    left -= 6;
+-
+-    param = get_param_value( server, "gametype", NULL);
+-    if(param)
+-    {
+-	++items;
+-	b = put_string(b, "gametype", &left);
+-	b = put_string(b, param, &left);
+-	b = put_bytes(b, "", 1, &left);
+-    }
+-
+-    param = get_param_value( server, "status", NULL);
+-    r = param;
+-    while ( param && sep )
+-    {
+-	sep= strchr( r, ':');
+-	if ( sep )
+-	    flen= sep-r;
+-	else
+-	    flen= strlen(r);
+-
+-	if (   strncmp( r, "standard", flen) == 0
+-	    || strncmp( r, "nostandard", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "standard", &left);
+-	    if(*r == 'n')
+-		b = put_string(b, "false", &left);
+-	    else
+-		b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	else if (  strncmp( r, "password", flen) == 0
+-		|| strncmp( r, "nopassword", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "password", &left);
+-	    if(*r == 'n')
+-		b = put_string(b, "false", &left);
+-	    else
+-		b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	else if ( strncmp( r, "notempty", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "currentplayers", &left);
+-	    b = put_string(b, "0", &left);
+-	    b = put_bytes(b, "\x04", 1, &left);
+-	}
+-	else if ( strncmp( r, "notfull", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "freespace", &left);
+-	    b = put_string(b, "0", &left);
+-	    b = put_bytes(b, "\x04", 1, &left);
+-	}
+-	else if ( strncmp( r, "nobots", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "nobots", &left);
+-	    b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	else if (  strncmp( r, "stats", flen) == 0
+-		|| strncmp( r, "nostats", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "stats", &left);
+-	    if(*r == 'n')
+-		b = put_string(b, "false", &left);
+-	    else
+-		b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	else if (  strncmp( r, "weaponstay", flen) == 0
+-		|| strncmp( r, "noweaponstay", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "weaponstay", &left);
+-	    if(*r == 'n')
+-		b = put_string(b, "false", &left);
+-	    else
+-		b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	else if (  strncmp( r, "transloc", flen) == 0
+-		|| strncmp( r, "notransloc", flen) == 0)
+-	{
+-	    ++items;
+-	    b = put_string(b, "transloc", &left);
+-	    if(*r == 'n')
+-		b = put_string(b, "false", &left);
+-	    else
+-		b = put_string(b, "true", &left);
+-	    b = put_bytes(b, "", 1, &left);
+-	}
+-	r= sep+1;
+-    }
++	char buf[4096] = {0};
++	size_t left = sizeof(buf);
++	char *b = buf;
++	char *param, *r, *sep= "";
++	unsigned flen = 0;
++	unsigned char items = 0;
++
++	// header is done later
++	b += 6;
++	left -= 6;
++
++	param = get_param_value( server, "gametype", NULL);
++	if(param)
++	{
++		++items;
++		b = put_string(b, "gametype", &left);
++		b = put_string(b, param, &left);
++		b = put_bytes(b, "", 1, &left);
++	}
++
++	param = get_param_value( server, "status", NULL);
++	r = param;
++	while ( param && sep )
++	{
++		sep= strchr( r, ':');
++		if ( sep )
++			flen= sep-r;
++		else
++			flen= strlen(r);
+ 
+-    param = get_param_value( server, "mutator", NULL);
+-    r = param;
+-    sep = "";
+-    while ( param && sep )
+-    {
+-	char neg = '\0';
+-	unsigned char l;
+-	sep= strchr( r, ':');
+-	if ( sep )
+-	    flen= sep-r;
+-	else
+-	    flen= strlen(r);
+-	
+-	if(*r == '-')
+-	{
+-	    neg = '\x04';
+-	    ++r;
+-	    --flen;
++		if (   strncmp( r, "standard", flen) == 0
++			|| strncmp( r, "nostandard", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "standard", &left);
++			if(*r == 'n')
++			b = put_string(b, "false", &left);
++			else
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		else if (  strncmp( r, "password", flen) == 0
++			|| strncmp( r, "nopassword", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "password", &left);
++			if(*r == 'n')
++			b = put_string(b, "false", &left);
++			else
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		else if ( strncmp( r, "notempty", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "currentplayers", &left);
++			b = put_string(b, "0", &left);
++			b = put_bytes(b, "\x04", 1, &left);
++		}
++		else if ( strncmp( r, "notfull", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "freespace", &left);
++			b = put_string(b, "0", &left);
++			b = put_bytes(b, "\x04", 1, &left);
++		}
++		else if ( strncmp( r, "nobots", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "nobots", &left);
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		else if (  strncmp( r, "stats", flen) == 0
++			|| strncmp( r, "nostats", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "stats", &left);
++			if(*r == 'n')
++			b = put_string(b, "false", &left);
++			else
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		else if (  strncmp( r, "weaponstay", flen) == 0
++			|| strncmp( r, "noweaponstay", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "weaponstay", &left);
++			if(*r == 'n')
++			b = put_string(b, "false", &left);
++			else
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		else if (  strncmp( r, "transloc", flen) == 0
++			|| strncmp( r, "notransloc", flen) == 0)
++		{
++			++items;
++			b = put_string(b, "transloc", &left);
++			if(*r == 'n')
++			b = put_string(b, "false", &left);
++			else
++			b = put_string(b, "true", &left);
++			b = put_bytes(b, "", 1, &left);
++		}
++		r= sep+1;
+ 	}
+ 
+-	if(!flen)
+-	    continue;
++	param = get_param_value( server, "mutator", NULL);
++	r = param;
++	sep = "";
++	while ( param && sep )
++	{
++		char neg = '\0';
++		unsigned char l;
++		sep= strchr( r, ':');
++		if ( sep )
++			flen= sep-r;
++		else
++			flen= strlen(r);
+ 
+-	b = put_string(b, "mutator", &left);
+-	l = flen+1;
+-	b = put_bytes(b, (char*)&l, 1, &left);
+-	b = put_bytes(b, r, flen, &left);
+-	b = put_bytes(b, "", 1, &left);
+-	b = put_bytes(b, &neg, 1, &left);
+-	++items;
+-
+-	r= sep+1;
+-    }
+-
+-    if(!b)
+-    {
+-	debug(0, "Error: query buffer too small. Please file a bug report!");
+-	return 0;
+-    }
++		if(*r == '-')
++		{
++			neg = '\x04';
++			++r;
++			--flen;
++		}
++
++		if(!flen)
++			continue;
++
++		b = put_string(b, "mutator", &left);
++		l = flen+1;
++		b = put_bytes(b, (char*)&l, 1, &left);
++		b = put_bytes(b, r, flen, &left);
++		b = put_bytes(b, "", 1, &left);
++		b = put_bytes(b, &neg, 1, &left);
++		++items;
++
++		r= sep+1;
++	}
++
++	if(!b)
++	{
++		debug(0, "Error: query buffer too small. Please file a bug report!");
++		return 0;
++	}
+ 
+-    put_long_little(b-buf-4, buf);
+-    buf[5] = items;
++	put_long_little(b-buf-4, buf);
++	buf[5] = items;
+ 
+-    return (qserver_send(server, buf, sizeof(buf)-left) > 0);
++	return (qserver_send(server, buf, sizeof(buf)-left) > 0);
+ }
+ 
+-void deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen)
++query_status_t deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen)
+ {
+-    unsigned char* state = (unsigned char*)&server->master_query_tag[0];
++	unsigned char* state = (unsigned char*)&server->master_query_tag[0];
+ 
+-    md5_state_t md5;
++	md5_state_t md5;
+ 
+-    if(!pktlen)
+-    {
+-	ut2004_server_done(server);
+-	goto cleanup_out;
+-    }
++	if(!pktlen)
++	{
++		ut2004_server_done(server);
++		goto cleanup_out;
++	}
+ 
+-    server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
++	server->ping_total+= time_delta( &packet_recv_time, &server->packet_time1);
+ 
+-    switch(*state)
+-    {
++	switch(*state)
++	{
+ 	case STATE_CHALLENGE:
+-	    // ensure at least one byte challenge, fit into buffer,
+-	    // match challenge, null terminated
+-	    if(    pktlen < 4 +1 +1 +1
++		// ensure at least one byte challenge, fit into buffer,
++		// match challenge, null terminated
++		if(	pktlen < 4 +1 +1 +1
+ 		|| pktlen > 4 +1 +8 +1
+ 		|| rawpkt[pktlen-1] != '\0')
+-	    {
+-		malformed_packet(server, "invalid challenge" );
+-		goto cleanup_out;
+-	    }
+-	    else
+-	    {
+-		char response[sizeof(challenge_response)];
+-		char* challenge = rawpkt+5;
+-		char sum[16];
+-
+-		memcpy(response, challenge_response, sizeof(challenge_response));
+-
+-		debug(2, "challenge: %s", challenge);
+-
+-		md5_init(&md5);
+-		md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
+-		md5_finish(&md5, (unsigned char*)sum);
+-		bin2hex(sum, 16, response+RESPONSE_OFFSET_CDKEY);
+-
+-		md5_init(&md5);
+-		md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
+-		md5_append(&md5, (unsigned char*)challenge, strlen(challenge));
+-		md5_finish(&md5, (unsigned char*)sum);
+-		bin2hex(sum, 16, response+RESPONSE_OFFSET_CHALLENGE);
+-
+-		qserver_send(server, response, sizeof(response));
+-
+-		server->server_name = MASTER;
+-
+-		*state = STATE_APPROVED;
+-	    }
+-	    break;
++		{
++			malformed_packet(server, "invalid challenge" );
++			goto cleanup_out;
++		}
++		else
++		{
++			char response[sizeof(challenge_response)];
++			char* challenge = rawpkt+5;
++			char sum[16];
++
++			memcpy(response, challenge_response, sizeof(challenge_response));
++
++			debug(2, "challenge: %s", challenge);
++
++			md5_init(&md5);
++			md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
++			md5_finish(&md5, (unsigned char*)sum);
++			bin2hex(sum, 16, response+RESPONSE_OFFSET_CDKEY);
++
++			md5_init(&md5);
++			md5_append(&md5, (unsigned char*)cdkey, CD_KEY_LENGTH);
++			md5_append(&md5, (unsigned char*)challenge, strlen(challenge));
++			md5_finish(&md5, (unsigned char*)sum);
++			bin2hex(sum, 16, response+RESPONSE_OFFSET_CHALLENGE);
++
++			if(qserver_send(server, response, sizeof(response)))
++				goto cleanup_out;
++
++			server->server_name = MASTER;
++
++			*state = STATE_APPROVED;
++		}
++		break;
+ 
+ 	case STATE_APPROVED:
+ 
+-	    if(pktlen != sizeof(approved)
++		if(pktlen != sizeof(approved)
+ 		|| 0 != memcmp(rawpkt, approved, pktlen))
+-	    {
+-		malformed_packet(server, "CD key not approved" );
+-		goto cleanup_out;
+-	    }
++		{
++			malformed_packet(server, "CD key not approved" );
++			goto cleanup_out;
++		}
++
++		debug(2, "got approval, sending verify");
+ 
+-	    debug(2, "got approval, sending verify");
++		if(qserver_send(server, approved_response, sizeof(approved_response)))
++			goto cleanup_out;
+ 
+-	    qserver_send(server, approved_response, sizeof(approved_response));
+-	    *state = STATE_VERIFIED;
++		*state = STATE_VERIFIED;
+ 
+-	    break;
++		break;
+ 
+ 	case STATE_VERIFIED:
+ 
+-	    if(pktlen != sizeof(verified)
++		if(pktlen != sizeof(verified)
+ 		|| 0 != memcmp(rawpkt, verified, pktlen))
+-	    {
+-		malformed_packet(server, "CD key not verified" );
+-		goto cleanup_out;
+-	    }
++		{
++			malformed_packet(server, "CD key not verified" );
++			goto cleanup_out;
++		}
+ 
+-	    if(!ut2004_send_query(server))
+-		goto cleanup_out;
++		debug(2, "CD key verified, sending query");
+ 
+-	    *state = STATE_LISTING;
++		if(ut2004_send_query(server))
++			goto cleanup_out;
++
++		*state = STATE_LISTING;
+ 
+-	    break;
++		break;
+ 	case STATE_LISTING:
+-	    // first packet. contains number of servers to expect
+-	    if(!server->saved_data.pkt_id)
+-	    {
++		// first packet. contains number of servers to expect
++		if(!server->saved_data.pkt_id)
++		{
+ 		/*
+-		server->saved_data.data = malloc(pktlen);
+-		memcpy(server->saved_data.data, rawpkt, pktlen);
+-		server->saved_data.datalen = pktlen;
+-		*/
+-		server->saved_data.pkt_id = 1;
++			server->saved_data.data = malloc(pktlen);
++			memcpy(server->saved_data.data, rawpkt, pktlen);
++			server->saved_data.datalen = pktlen;
++			*/
++			server->saved_data.pkt_id = 1;
+ 
+-		if(pktlen == 9)
+-		{
+-		    unsigned num = swap_long_from_little(rawpkt+4);
+-		    debug(2, "expect %u servers", num);
++			if(pktlen == 9)
++			{
++				unsigned num = swap_long_from_little(rawpkt+4);
++				debug(2, "expect %u servers", num);
+ #if 1
+-		    if(num < 10000)
+-		    {
+-			server->master_pkt_len = num*6;
+-			server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
+-		    }
++				if(num < 10000)
++				{
++					server->master_pkt_len = num*6;
++					server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len);
++				}
+ #endif
+-		}
+-	    }
+-	    else if(pktlen < 4)
+-	    {
+-		malformed_packet(server, "packet too short");
+-		goto cleanup_out;
+-	    }
+-	    else
+-	    {
+-		char* p = rawpkt;
+-		unsigned recordlen = 0;
+-
+-		if(server->saved_data.next)
+-		{
+-		    unsigned need = 0;
+-		    SavedData* data = server->saved_data.next;
+-		    // nasty, four bytes of record length are split up. since
+-		    // we alloc'ed at least four bytes we just copy the 4-x
+-		    // bytes to data->data
+-		    if(data->datalen < 4)
+-		    {
+-			need = 4 - data->datalen;
+-			debug(2, "need %d bytes more for recordlen", need);
+-			if( need > pktlen)
+-			{
+-			    // XXX ok, im lazy now. Stupid server can't even
+-			    // send four bytes in a row
+-			    malformed_packet(server, "chunk too small");
+-			    goto cleanup_out;
+ 			}
+-			memcpy(data->data+data->datalen, p, need);
+-			p += need;
+-			data->datalen = 4;
+-		    }
+-
+-		    recordlen = swap_long_from_little(data->data);
+-
+-		    if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
+-		    {
+-			malformed_packet(server,
+-				"record lengthx %x out of range, position %d", recordlen, (int)(p-rawpkt));
++		}
++		else if(pktlen < 4)
++		{
++			malformed_packet(server, "packet too short");
+ 			goto cleanup_out;
+-		    }
+-
+-		    need = 4+recordlen - data->datalen;
+-
+-		    debug(2, "recordlen: %d, saved: %d, pkglen: %d, needed: %d", recordlen, data->datalen, pktlen, need);
+-
+-		    if( need <= pktlen)
+-		    {
+-			data->data = realloc(data->data, 4+recordlen);
+-			memcpy(data->data + data->datalen, p, need);
+-			ut2004_parse_record(server, data->data);
+-			p += need;
+-
+-			free(data->data);
+-			free(data);
+-			server->saved_data.next = NULL;
+-		    }
+ 		}
+-
+-		while(!server->saved_data.next && p-rawpkt+4 < pktlen)
++		else
+ 		{
+-		    recordlen = swap_long_from_little(p);
++			char* p = rawpkt;
++			unsigned recordlen = 0;
+ 
+-		    // record too large
+-		    if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
+-		    {
+-			malformed_packet(server,
+-				"record length %x out of range, position %d", recordlen, (int)(p-rawpkt));
+-			goto cleanup_out;
+-		    }
+-		    // recordlen itself is four bytes
+-		    recordlen += 4;
+-
+-		    // record fully inside packet
+-		    if(p-rawpkt+recordlen <= pktlen)
+-		    {
+-			ut2004_parse_record(server, p);
+-			p += recordlen;
+-		    }
+-		    else
+-			break;
+-		}
+-
+-		// record continues in next packet. save it.
+-		if(p-rawpkt < pktlen)
+-		{
+-		    SavedData* data = server->saved_data.next;
+-		    unsigned tosave = pktlen - (p-rawpkt);
+-		    if(!data)
+-		    {
+-			data = malloc(sizeof(SavedData));
+-			data->data = malloc(tosave<4?4:tosave); // alloc at least four bytes
+-			data->datalen = tosave;
+-			memcpy(data->data, p, data->datalen);
+-			data->next = NULL;
+-			server->saved_data.next = data;
+-			
+-			debug(1, "saved %d bytes", data->datalen );
+-		    }
+-		    else
+-		    {
+-			data->data = realloc(data->data, data->datalen + tosave );
+-			memcpy(data->data+data->datalen, p, tosave);
+-			data->datalen += tosave;
+-			
+-			debug(1, "saved %d bytes (+)", data->datalen );
+-		    }
+-		}
+-	    }
+-	    break;
+-    }
++			if(server->saved_data.next)
++			{
++				unsigned need = 0;
++				SavedData* data = server->saved_data.next;
++				// nasty, four bytes of record length are split up. since
++				// we alloc'ed at least four bytes we just copy the 4-x
++				// bytes to data->data
++				if(data->datalen < 4)
++				{
++					need = 4 - data->datalen;
++					debug(2, "need %d bytes more for recordlen", need);
++					if( need > pktlen)
++					{
++						// XXX ok, im lazy now. Stupid server can't even
++						// send four bytes in a row
++						malformed_packet(server, "chunk too small");
++						goto cleanup_out;
++					}
++					memcpy(data->data+data->datalen, p, need);
++					p += need;
++					data->datalen = 4;
++				}
++
++				recordlen = swap_long_from_little(data->data);
++
++				if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
++				{
++					malformed_packet(server,
++						"record lengthx %x out of range, position %d", recordlen, (int)(p-rawpkt));
++					goto cleanup_out;
++				}
++
++				need = 4+recordlen - data->datalen;
++
++				debug(2, "recordlen: %d, saved: %d, pkglen: %d, needed: %d", recordlen, data->datalen, pktlen, need);
++
++				if( need <= pktlen)
++				{
++					data->data = realloc(data->data, 4+recordlen);
++					memcpy(data->data + data->datalen, p, need);
++					ut2004_parse_record(server, data->data);
++					p += need;
++
++					free(data->data);
++					free(data);
++					server->saved_data.next = NULL;
++				}
++			}
+ 
+-#if 0 // would cause extremely short timeout
+-    server->next_player_info= -1; // would clean up otherwise <- not true?
+-#endif
++			while(!server->saved_data.next && p-rawpkt+4 < pktlen)
++			{
++				recordlen = swap_long_from_little(p);
+ 
++				// record too large
++				if(!recordlen || recordlen > MAX_LISTING_RECORD_LEN)
++				{
++					malformed_packet(server,
++						"record length %x out of range, position %d", recordlen, (int)(p-rawpkt));
++					goto cleanup_out;
++				}
++				// recordlen itself is four bytes
++				recordlen += 4;
++
++				// record fully inside packet
++				if(p-rawpkt+recordlen <= pktlen)
++				{
++					ut2004_parse_record(server, p);
++					p += recordlen;
++				}
++				else
++					break;
++			}
+ 
+-    debug(2, "%d servers total", server->n_servers/6);
++			// record continues in next packet. save it.
++			if(p-rawpkt < pktlen)
++			{
++				SavedData* data = server->saved_data.next;
++				unsigned tosave = pktlen - (p-rawpkt);
++				if(!data)
++				{
++					data = malloc(sizeof(SavedData));
++					data->data = malloc(tosave<4?4:tosave); // alloc at least four bytes
++					data->datalen = tosave;
++					memcpy(data->data, p, data->datalen);
++					data->next = NULL;
++					server->saved_data.next = data;
++
++					debug(1, "saved %d bytes", data->datalen );
++				}
++				else
++				{
++					data->data = realloc(data->data, data->datalen + tosave );
++					memcpy(data->data+data->datalen, p, tosave);
++					data->datalen += tosave;
+ 
+-#if 0 // harms now with new ping scheduling
+-    server->retry1= 0;
+-    cleanup_qserver( server, 0);
+-    bind_sockets();
+-#endif
++					debug(1, "saved %d bytes (+)", data->datalen );
++				}
++			}
++		}
++		break;
++	}
++
++	debug(2, "%d servers total", server->n_servers/6);
+ 
+-    return;
++	return INPROGRESS;
+ 
+ cleanup_out:
+-    server->master_pkt_len = server->n_servers;
+-    server->n_servers /= 6;
+-    cleanup_qserver(server, 1);
+-    return;
++	server->master_pkt_len = server->n_servers;
++	server->n_servers /= 6;
++	return DONE_FORCE;
+ }
+ 
+ static const char hexchar[] = "0123456789abcdef";
+ 
+ static void bin2hex(const char* in, size_t len, char* out)
+ {
+-    char* o = out+len*2;
+-    in += len;
+-    do
+-    {
+-	*--o = hexchar[*--in&0x0F];
+-	*--o = hexchar[(*in>>4)&0x0F];
+-    } while(o != out);
++	char* o = out+len*2;
++	in += len;
++	do
++	{
++		*--o = hexchar[*--in&0x0F];
++		*--o = hexchar[(*in>>4)&0x0F];
++	} while(o != out);
+ }
+ 
+-// vim: sw=4 ts=8 noet
++// vim: sw=4 ts=4 noet
+diff -urP qstat-2.11/ut2004.h qstat2/ut2004.h
+--- qstat-2.11/ut2004.h	2004-10-17 15:41:53.000000000 -0500
++++ qstat2/ut2004.h	2009-07-02 06:11:41.032181000 -0400
+@@ -12,7 +12,7 @@
+ 
+ #include "qstat.h"
+ 
+-void send_ut2004master_request_packet(struct qserver *server);
+-void deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen);
++query_status_t send_ut2004master_request_packet(struct qserver *server);
++query_status_t deal_with_ut2004master_packet(struct qserver *server, char *rawpkt, int pktlen);
+ 
+ #endif
+diff -urP qstat-2.11/utils.c qstat2/utils.c
+--- qstat-2.11/utils.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/utils.c	2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,70 @@
++/*
++ * Utility Functions
++ *
++ */
++
++#include "utils.h"
++
++#if !HAVE_STRNSTR
++/*
++ * strnstr -
++ * Copyright (c) 2001 Mike Barcroft <mike at FreeBSD.org>
++ * Copyright (c) 1990, 1993
++ *	The Regents of the University of California.  All rights reserved.
++ *
++ * This code is derived from software contributed to Berkeley by
++ * Chris Torek.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ *    must display the following acknowledgement:
++ *	This product includes software developed by the University of
++ *	California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include <string.h>
++
++char *
++qstat_strnstr(const char *s, const char *find, size_t slen)
++{
++	char c, sc;
++	size_t len;
++
++	if ((c = *find++) != '\0') {
++		len = strlen(find);
++		do {
++			do {
++				if (slen-- < 1 || (sc = *s++) == '\0')
++					return (NULL);
++			} while (sc != c);
++			if (len > slen)
++				return (NULL);
++		} while (strncmp(s, find, len) != 0);
++		s--;
++	}
++	return ((char *)s);
++}
++
++#endif /* HAVE_STRNSTR */
+diff -urP qstat-2.11/utils.h qstat2/utils.h
+--- qstat-2.11/utils.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/utils.h	2012-04-20 08:51:31.440424000 -0400
+@@ -0,0 +1,23 @@
++/*
++ * Utility Functions
++ * Copyright 2012 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_UTILS_H
++#define QSTAT_UTILS_H
++
++// BSD has strnstr
++#if defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__OpenBSD__)
++#ifndef HAVE_STRNSTR
++#define HAVE_STRNSTR 1
++#endif /* HAVE_STRNSTR */
++#endif
++
++#if !HAVE_STRNSTR
++#include <string.h>
++char * qstat_strnstr(const char *s, const char *find, size_t slen);
++#define strnstr(s,find,slen) qstat_strnstr(s,find,slen)
++#endif
++
++#endif
+diff -urP qstat-2.11/ventrilo.c qstat2/ventrilo.c
+--- qstat-2.11/ventrilo.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ventrilo.c	2011-02-23 16:58:56.672759000 -0500
+@@ -0,0 +1,367 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Ventrilo query protocol
++ * Algorithm by Luigi Auriemma <www.aluigi.org>
++ * QStat port 2010 by Michael Willigens <michael at willigens.de>
++ * Fixes by Steven Hartland <steven.hartland at multiplay.co.uk>
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#define strtok_ret strtok_r
++#define VENTRILO_RAND random()
++#else
++#include <winsock.h>
++#define strtok_ret strtok_s
++#define VENTRILO_RAND rand()
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++int VENTRILO_COMMAND_GENERIC_INFO = 1;
++int VENTRILO_COMMAND_DETAILED_INFO = 2;	// includes generic infos
++int VENTRILO_COMMAND_DIFFERENT = 7;
++
++typedef struct {
++	unsigned short pckkey;  // key for decoding this header
++	unsigned short zero;	// ever 0
++	unsigned short cmd;	 // command number: 1 generic info, 2 for details
++	unsigned short id;	  // packet ID used for tracking the replies
++	unsigned short totlen;  // total data size (for data splitted in packets)
++	unsigned short len;	 // size of the data in this packet (max 492)
++	unsigned short totpck;  // total amount of packets (max 32)
++	unsigned short pck;	 // current packet number
++	unsigned short datakey; // key for decoding the data
++	unsigned short crc;	 // checksum of the total plain-text data
++} ventrilo_udp_head;
++
++const static unsigned char  ventrilo_udp_encdata_head[] =
++	"\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e"
++	"\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea"
++	"\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24"
++	"\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb"
++	"\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c"
++	"\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53"
++	"\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae"
++	"\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09"
++	"\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62"
++	"\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5"
++	"\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f"
++	"\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff"
++	"\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f"
++	"\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f"
++	"\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a"
++	"\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d";
++
++const static unsigned char  ventrilo_udp_encdata_data[] =
++	"\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23"
++	"\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48"
++	"\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e"
++	"\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe"
++	"\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42"
++	"\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81"
++	"\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36"
++	"\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59"
++	"\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24"
++	"\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e"
++	"\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2"
++	"\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67"
++	"\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08"
++	"\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e"
++	"\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90"
++	"\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29";
++
++const static unsigned short ventrilo_crc_table[] =
++{
++	0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
++	0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
++	0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
++	0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
++	0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
++	0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
++	0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
++	0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
++	0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
++	0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
++	0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
++	0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
++	0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
++	0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
++	0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
++	0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
++	0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
++	0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
++	0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
++	0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
++	0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
++	0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
++	0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
++	0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
++	0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
++	0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
++	0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
++	0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
++	0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
++	0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
++	0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
++	0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
++};
++
++void ventrilo_udp_head_dec(unsigned char *data)
++{
++	int i;
++	unsigned short *p;
++	unsigned char a1;
++	unsigned char a2;
++
++	p = (unsigned short *)data;
++	data += 2;
++
++	*p = ntohs(*p);
++	a1 = *p;
++	if( ! a1 )
++	{
++		return;
++	}
++
++	a2 = *p >> 8;
++	for( i = 0; i < 18; i++ )
++	{
++		data[i] -= ventrilo_udp_encdata_head[a2] + (i % 5);
++		a2 += a1;
++	}
++
++	for( i = 0; i < 9; i++ )
++	{
++		p++;
++		*p = ntohs(*p);
++	}
++}
++
++void ventrilo_udp_head_enc(unsigned char *data)
++{
++	int i;
++	unsigned short *p;
++	unsigned char a1;
++	unsigned char a2;
++
++	p = (unsigned short *)data;
++	data += 2;
++
++	*p = (((VENTRILO_RAND * 0x343fd) + 0x269ec3) >> 16) & 0x7fff;
++	a1 = *p;
++	a2 = *p >> 8;
++	if( ! a2 )
++	{
++		a2 = 69;
++		*p |= (a2 << 8);
++	}
++
++	for( i = 0; i < 10; i++ )
++	{
++		*p = htons(*p);
++		p++;
++	}
++
++	for( i = 0; i < 18; i++ )
++	{
++		data[i] += ventrilo_udp_encdata_head[a2] + (i % 5);
++		a2 += a1;
++	}
++}
++
++void ventrilo_udp_data_dec(unsigned char *data, int len, unsigned short key)
++{
++	int i;
++	unsigned char a1;
++	unsigned char a2;
++
++	a1 = key;
++	if( ! a1 )
++	{
++		return;
++	}
++
++	a2 = key >> 8;
++	for( i = 0; i < len; i++ )
++	{
++		data[i] -= ventrilo_udp_encdata_data[a2] + (i % 72);
++		a2 += a1;
++	}
++}
++
++unsigned short ventrilo_udp_data_enc(unsigned char *data, int len)
++{
++	int i;
++	unsigned short key;
++	unsigned char a1;
++	unsigned char a2;
++
++	key = (((VENTRILO_RAND * 0x343fd) + 0x269ec3) >> 16) & 0x7fff;
++	a1 = key;
++	a2 = key >> 8;
++	if( ! a2 )
++	{
++		a2 = 1;
++		key |= (a2 << 8);
++	}
++
++	for( i = 0; i < len; i++ )
++	{
++		data[i] += ventrilo_udp_encdata_data[a2] + (i % 72);
++		a2 += a1;
++	}
++
++	return key;
++}
++
++
++
++unsigned short ventrilo_udp_crc(unsigned char *data, int len)
++{
++	unsigned short  crc = 0;
++
++	while( len-- )
++	{
++		crc = ventrilo_crc_table[crc >> 8] ^ *data ^ (crc << 8);
++		data++;
++	}
++
++	return(crc);
++}
++
++int buildVentriloRequest(unsigned char *buf, int cmd, char *pass, int id)
++{
++	ventrilo_udp_head *stat = (ventrilo_udp_head *) buf;
++	unsigned char *data = buf + 20;
++
++	strncpy((char *) data, pass, 16);
++
++	stat->zero = 0;
++	stat->cmd = cmd;
++	stat->id = id;
++	stat->totlen = 16;
++	stat->len = 16;
++	stat->totpck = 1;
++	stat->pck = 0;
++	stat->crc = ventrilo_udp_crc(data, 16);
++	stat->datakey = ventrilo_udp_data_enc(data, 16);
++	ventrilo_udp_head_enc(buf);
++
++	return 20+16;
++}
++
++query_status_t send_ventrilo_request_packet( struct qserver *server )
++{
++	char buf[1500];
++	char *password = get_param_value( server, "password", "" );
++	int size = buildVentriloRequest((unsigned char *) buf, VENTRILO_COMMAND_DETAILED_INFO, password, server->challenge);
++	server->n_requests++;
++	gettimeofday( &server->packet_time1, NULL );
++
++	debug( 2, "send status request");
++
++	return send_packet( server, buf, size );
++}
++
++query_status_t deal_with_ventrilo_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *line, *last_line;
++
++	debug( 3, "deal_with_ventrilo_packet: state = %ld", server->challenge );
++
++	if( 20 > pktlen )
++	{
++		debug( 2, "wrong or incomplete packet received. retrying...");
++		return INPROGRESS;
++	}
++
++	/* add to the data previously saved to get a full packet*/
++	if ( ! server->combined )
++	{
++		ventrilo_udp_head *header = (ventrilo_udp_head *) rawpkt;
++		unsigned char *data = (unsigned char *) header+20;
++		ventrilo_udp_head_dec( (unsigned char*) header );
++		ventrilo_udp_data_dec(data, header->len, header->datakey);
++
++		if (header->id != server->challenge)
++		{
++			debug( 2, "ignoring unnecessary packet...");
++			return INPROGRESS;
++		}
++
++		debug( 3, "decoded ventrilo fragment: id:%i - len:%i - totlen:%i - totpck:%i - zero:%i - cmd:%i", header->id, header->len, header->totlen, header->totpck, header->zero, header->cmd);
++
++		add_packet(server, header->id, packet_count(server), header->totpck, header->len, (char *) data, 0);
++		return combine_packets( server );
++	}
++
++	debug( 4, "combined ventrilo packet: %s", rawpkt);
++
++	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++
++	line = strtok_ret( rawpkt, "\n", &last_line );
++	debug( 3, "processing detailed response..." );
++	while (line != NULL)
++	{
++		debug( 4, "processing line: %s", line );
++		if ( 0 == strncmp( line, "NAME: ", 6 ) )
++		{
++			server->server_name = strdup( line + 6 );
++		}
++		else if ( 0 == strncmp( line, "MAXCLIENTS: ", 12 ) )
++		{
++			server->max_players = atoi( line + 12 );
++		}
++		else if ( 0 == strncmp( line, "CLIENTCOUNT: ", 13 ) )
++		{
++			server->num_players = atoi( line + 13 );
++		}
++		else if ( 0 == strncmp( line, "CLIENT: ", 8 ) )
++		{
++			// Client e.g.
++			// CLIENT: UID=406,ADMIN=0,CID=17,PHAN=0,PING=73,SEC=2245,NAME=Darrimu,COMM=
++			struct player *player = add_player( server, server->n_player_info );
++			char *last_info = NULL;
++			char *player_info = strtok_ret( line + 8, ",", &last_info );
++			while ( NULL != player_info )
++			{
++				debug( 5, "player info: %s", player_info );
++				if ( 0 ==  strncmp( player_info, "NAME=", 5 ) )
++				{
++					player->name = strdup( player_info + 5 );
++				}
++				else if ( strncmp( player_info, "PING=", 5 ) )
++				{
++					player->ping = atoi( player_info + 5 );
++				}
++				else if ( 0 == strncmp( player_info, "CID=", 4 ) )
++				{
++					player->team = atoi( player_info + 4 );
++				}
++				else if ( strncmp( player_info, "SEC=", 4 ) )
++				{
++					player->connect_time = atoi( player_info + 4 );
++				}
++				player_info = strtok_ret( NULL, ",", &last_info );
++			}
++		}
++
++		line = strtok_ret( NULL, "\n", &last_line );
++	}
++	server->map_name = strdup( "N/A" );
++
++	return DONE_FORCE;
++}
+diff -urP qstat-2.11/ventrilo.h qstat2/ventrilo.h
+--- qstat-2.11/ventrilo.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/ventrilo.h	2011-02-23 16:58:56.672759000 -0500
+@@ -0,0 +1,21 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * Ventrilo query protocol
++ * Algorithm by Luigi Auriemma <www.aluigi.org> (Reversing and first version in c)
++ * Copyright 2010 Michael Willigens <michael at willigens.de>
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_VENTRILO_H
++#define QSTAT_VENTRILO_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_ventrilo_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_ventrilo_request_packet( struct qserver *server );
++
++#endif
++
+diff -urP qstat-2.11/wic.c qstat2/wic.c
+--- qstat-2.11/wic.c	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/wic.c	2009-09-14 19:22:08.993154000 -0400
+@@ -0,0 +1,191 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * World in Conflict Protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ *
++ */
++
++#include <sys/types.h>
++#ifndef _WIN32
++#include <sys/socket.h>
++#endif
++#include <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "debug.h"
++#include "qstat.h"
++#include "packet_manip.h"
++
++
++query_status_t send_wic_request_packet( struct qserver *server )
++{
++	char buf[256];
++
++	int serverport = get_param_i_value( server, "port", 0 );
++	char *password = get_param_value( server, "password", "N/A" );
++	change_server_port( server, serverport, 1 );
++
++	if ( get_player_info )
++	{
++		server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
++		sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password );
++		server->saved_data.pkt_index = 2;
++	}
++	else
++	{
++		server->flags |= TF_STATUS_QUERY;
++		sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password );
++		server->saved_data.pkt_index = 1;
++	}
++
++	return send_packet( server, buf, strlen( buf ) );
++}
++
++
++query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen )
++{
++	char *s, *end, *team = NULL;
++	int mode = server->n_servers, slot, score;
++	char name[256], role[256];
++
++	debug( 2, "processing n_requests %d, retry1 %d, n_retries %d, delta %d", server->n_requests, server->retry1, n_retries, time_delta( &packet_recv_time, &server->packet_time1 ) );
++
++	server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
++	server->n_requests++;
++
++	if ( 0 == pktlen )
++	{
++		// Invalid password
++		return REQ_ERROR;
++	}
++
++	gettimeofday( &server->packet_time1, NULL);
++
++	rawpkt[pktlen]= '\0';
++	end = &rawpkt[pktlen];
++
++	s = rawpkt;
++
++	while ( NULL != s )
++	{
++		int len = strlen( s );
++		*(s + len - 2) = '\0'; // strip off \x0D\x0A
++		debug( 2, "Line[%d]: %s", mode, s );
++
++		if ( 0 == mode )
++		{
++			// Settings
++			// TODO: make parse safe
++			if ( 0 == strncmp( s, "Settings: ", 9 ) )
++			{
++				// Server Rule
++				char *key = s + 10;
++				char *value = strchr( key, ' ' );
++				*value = '\0';
++				value++;
++				debug( 2, "key: '%s' = '%s'", key, value );
++				if ( 0 == strcmp( "myGameName", key ) )
++				{
++					server->server_name = strdup( value );
++				}
++				else if ( 0 == strcmp( "myMapFilename", key ) )
++				{
++					server->map_name = strdup( value );
++				}
++				else if ( 0 == strcmp( "myMaxPlayers", key ) )
++				{
++					server->max_players = atoi( value );
++				}
++				else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) )
++				{
++					server->num_players = atoi( value);
++				}
++				else
++				{
++					add_rule( server, key, value, NO_FLAGS );
++				}
++			}
++			else if ( 0 == strcmp( "Listing players", s ) )
++			{
++				// end of rules request
++				server->saved_data.pkt_index--;
++				mode++;
++			}
++			else if ( 0 == strcmp( "Exit confirmed.", s ) )
++			{
++				server->n_servers = mode;
++				return DONE_FORCE;
++			}
++		}
++		else if ( 1 == mode )
++		{
++			// Player info
++			if ( 0 == strncmp( s, "Team: ", 6 ) )
++			{
++				team = s + 6;
++				debug( 2, "Team: %s", team );
++			}
++			else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) )
++			{
++				// Player info
++				struct player *player = add_player( server, server->n_player_info );
++				if ( NULL != player )
++				{
++					player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++					player->name = strdup( name );
++					player->score = score;
++					player->team_name = team;
++					player->tribe_tag = strdup( role );
++					// Indicate if its a bot
++					player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
++				}
++				debug( 2, "player %d, role %s, score %d, name %s", slot, role, score, name );
++			}
++			else if ( 3 == sscanf( s, "Slot: %d Role:  Score: %d Name: %255[^\x0d\x0a]", &slot, &score, name ) )
++			{
++				// Player info
++				struct player *player = add_player( server, server->n_player_info );
++				if ( NULL != player )
++				{
++					player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
++					player->name = strdup( name );
++					player->score = score;
++					player->team_name = team;
++					// Indicate if its a bot
++					player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
++				}
++				debug( 2, "player %d, score %d, name %s", slot, score, name );
++			}
++			else if ( 0 == strcmp( "Exit confirmed.", s ) )
++			{
++				server->n_servers = mode;
++				return DONE_FORCE;
++			}
++		}
++
++		s += len;
++		if ( s + 1 < end )
++		{
++			s++; // next line
++		}
++		else
++		{
++			s = NULL;
++		}
++	}
++
++	server->n_servers = mode;
++
++	if ( 0 == server->saved_data.pkt_index )
++	{
++		server->map_name = strdup( "N/A" );
++		return DONE_FORCE;
++	}
++
++	return INPROGRESS;
++}
+diff -urP qstat-2.11/wic.h qstat2/wic.h
+--- qstat-2.11/wic.h	1969-12-31 19:00:00.000000000 -0500
++++ qstat2/wic.h	2009-07-02 06:11:41.032181000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * qstat 2.8
++ * by Steve Jankowski
++ *
++ * World in Conflict Protocol
++ * Copyright 2007 Steven Hartland
++ *
++ * Licensed under the Artistic License, see LICENSE.txt for license terms
++ */
++#ifndef QSTAT_WIC_H
++#define QSTAT_WIC_H
++
++#include "qserver.h"
++
++// Packet processing methods
++query_status_t deal_with_wic_packet( struct qserver *server, char *pkt, int pktlen );
++query_status_t send_wic_request_packet( struct qserver *server );
++
++#endif
++
diff --git a/qstat.spec b/qstat.spec
index 9b3d1cb..d1ae65e 100644
--- a/qstat.spec
+++ b/qstat.spec
@@ -1,14 +1,14 @@
 Summary: Real-time Game Server Status for FPS game servers
 Name: qstat
 Version: 2.11
-Release: 15.20080912svn311%{?dist}
+Release: 16.20131212svn382%{?dist}
 License: Artistic 2.0
 Group: Amusements/Games
 URL: http://sourceforge.net/projects/qstat/
 Source: http://downloads.sourceforge.net/qstat/qstat-%{version}.tar.gz
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Patch0: qstat-2.11-20080912svn311.patch
-Patch1:	qstat-2.11-format-security.patch
+Patch0: qstat-2.11-20131212svn382.patch
+BuildRequires: autoconf, automake, libtool
 
 %description
 QStat is a command-line program that gathers real-time statistics
@@ -18,7 +18,7 @@ person shooter variety (Quake, Half-Life, etc)
 %prep
 %setup -q
 %patch0 -p1
-%patch1 -p1 -b .format-security
+autoreconf -ifv
 
 %build
 %configure
@@ -46,6 +46,9 @@ rm -rf %{buildroot}
 %{_bindir}/quakestat
 
 %changelog
+* Thu Dec 12 2013 Tom Callaway <spot at fedoraproject.org> - 2.11-16.20131212svn382
+- update to svn 382
+
 * Thu Dec 12 2013 Tom Callaway <spot at fedoraproject.org> - 2.11-15.20080912svn311
 - apply fixes for format-security issues
 


More information about the scm-commits mailing list