/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2002-2015 Q3Rally Team (Per Thormann - q3rally@gmail.com)

This file is part of q3rally source code.

q3rally source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

q3rally source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with q3rally; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/


/*
=======================================================================

MULTIPLAYER MENU (SERVER BROWSER)

=======================================================================
*/


#include "ui_local.h"


#define MAX_GLOBALSERVERS               128
// #define MAX_PINGREQUESTS             16
#define MAX_ADDRESSLENGTH               64
// STONELANCE
//#define MAX_HOSTNAMELENGTH    22
//#define MAX_MAPNAMELENGTH             16
#define MAX_HOSTNAMELENGTH              42
#define MAX_MAPNAMELENGTH               12
// END
#define MAX_LISTBOXITEMS                128
#define MAX_LOCALSERVERS                128
#define MAX_STATUSLENGTH                64
#define MAX_LEAGUELENGTH                28
// STONELANCE
//#define MAX_LISTBOXWIDTH              61
#define MAX_LISTBOXWIDTH                90

/*
#define ART_BACK0                               "menu/art/back_0"
#define ART_BACK1                               "menu/art/back_1"
#define ART_CREATE0                             "menu/art/create_0"
#define ART_CREATE1                             "menu/art/create_1"
#define ART_SPECIFY0                    "menu/art/specify_0"
#define ART_SPECIFY1                    "menu/art/specify_1"
#define ART_REFRESH0                    "menu/art/refresh_0"
#define ART_REFRESH1                    "menu/art/refresh_1"
#define ART_CONNECT0                    "menu/art/fight_0"
#define ART_CONNECT1                    "menu/art/fight_1"
#define ART_ARROWS0                             "menu/art/arrows_vert_0"
#define ART_ARROWS_UP                   "menu/art/arrows_vert_top"
#define ART_ARROWS_DOWN                 "menu/art/arrows_vert_bot"
*/
// END
#define ART_UNKNOWNMAP                  "menu/art/unknownmap"
#define ART_REMOVE0                             "menu/art/delete_0"
#define ART_REMOVE1                             "menu/art/delete_1"
#define ART_SAVE0                               "menu/art/save_0"
#define ART_SAVE1                               "menu/art/save_1"
#define GLOBALRANKINGS_LOGO             "menu/art/gr/grlogo"

#define ID_MASTER                       10
#define ID_GAMETYPE                     11
#define ID_SORTKEY                      12
#define ID_SHOW_FULL            13
#define ID_SHOW_EMPTY           14
#define ID_ONLY_HUMANS          15
#define ID_LIST                         16
// STONELANCE
//#define ID_SCROLL_UP          16
//#define ID_SCROLL_DOWN        17
// END
#define ID_BACK                         18
#define ID_REFRESH                      19
#define ID_SPECIFY                      20
#define ID_CREATE                       21
#define ID_CONNECT                      22
#define ID_REMOVE                       23
#define ID_SAVE                         24

#define GR_LOGO                         30
#define GR_LETTERS                      31

#define UIAS_LOCAL			0
#define UIAS_GLOBAL0			1
#define UIAS_GLOBAL1			2
#define UIAS_GLOBAL2			3
#define UIAS_GLOBAL3			4
#define UIAS_GLOBAL4			5
#define UIAS_GLOBAL5			6
#define UIAS_FAVORITES			7
#define UIAS_NUM_SOURCES		8

#define UI_MAX_MASTER_SERVERS	6

#define SORT_HOST                       0
#define SORT_MAP                        1
#define SORT_CLIENTS            2
#define SORT_GAME                       3
#define SORT_PING                       4
#define SORT_NUM_SORTS                  5

#define GAMES_ALL                       0
// STONELANCE
/*
#define GAMES_FFA                       1
#define GAMES_TEAMPLAY          2
#define GAMES_TOURNEY           3
#define GAMES_CTF                       4
*/
#define GAMES_RACING                    1
#define GAMES_RACING_DM                 2
#define GAMES_DERBY                             3
#define GAMES_DEATHMATCH                4
#define GAMES_TEAM_RACING               5
#define GAMES_TEAM_RACING_DM    6
#define GAMES_TEAMPLAY                  7
#define GAMES_CTF                               8
#define GAMES_DOMINATION                9
#define GAMES_NUM_GAMES                 10
// END

static const char *master_items[] = {
	"Local",
	"Internet",
	"Master1",
	"Master2",
	"Master3",
	"Master4",
	"Master5",
	"Favorites",
	NULL
};

static const char *servertype_items[] = {
        "All",
// STONELANCE
/*
        "Free For All",
        "Team Deathmatch",
        "Tournament",
        "Capture the Flag",
*/
        "Racing",
        "Racing Deathmatch",
        "Demolition Derby",
        "Deathmatch",
        "Team Racing",
        "Team Racing Deathmatch",
        "Team Deathmatch",
        "Capture the Flag",
        "Domination",
// END
        0
};

static const char *sortkey_items[] = {
        "Server Name",
        "Map Name",
        "Open Player Spots",
        "Game Type",
        "Ping Time",
        0
};

static char* gamenames[] = {
// STONELANCE
/*
        "DM ",  // deathmatch
        "1v1",  // tournament
        "SP ",  // single player
        "Team DM",      // team deathmatch
        "CTF",  // capture the flag
        "One Flag CTF",         // one flag ctf
        "OverLoad",                             // Overload
        "Harvester",                    // Harvester
        "Rocket Arena 3",       // Rocket Arena 3
        "Q3F",                                          // Q3F
        "Urban Terror",         // Urban Terror
        "OSP",                                          // Orange Smoothie Productions
*/
        "Race",
        "Race DM",
        "SP ",
        "Derby",
        "DM ",
        "TRace",
        "TRace DM",
        "Team DM",      // team deathmatch
        "CTF",  // capture the flag
        "Domination",   // domination
// END
        "???",                  // unknown
        0
};

static char* netnames[] = {
        "??? ",
        "UDP ",
        "UDP6",
        NULL
};

// STONELANCE
//static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
static char quake3worldMessage[] = "Visit www.q3rally.com - News, Forums, Files";
// END
static char globalRankingsMessage[] = "Visit www.globalrankings.com - you kick ass, we'll take names";

typedef struct {
        char    adrstr[MAX_ADDRESSLENGTH];
        int             start;
} pinglist_t;

typedef struct servernode_s {
        char    adrstr[MAX_ADDRESSLENGTH];
        char    hostname[MAX_HOSTNAMELENGTH];
        char    mapname[MAX_MAPNAMELENGTH];
        int             numclients;
        int             humanclients;
        int             maxclients;
        int             pingtime;
        int             gametype;
        char    gamename[12];
        int             nettype;
        int             minPing;
        int             maxPing;


} servernode_t;

typedef struct {
        char                    buff[MAX_LISTBOXWIDTH];
        servernode_t*   servernode;
} table_t;

typedef struct {
        menuframework_s         menu;

        menutext_s                      banner;

        menulist_s                      master;
        menulist_s                      gametype;
        menulist_s                      sortkey;
        menuradiobutton_s       showfull;
        menuradiobutton_s       showempty;
        menuradiobutton_s       onlyhumans;

        menulist_s              list;
        menutext_s              save;
        menubitmap_s            mappic;
        // STONELANCE
/*
        menubitmap_s            arrows;
        menubitmap_s            up;
        menubitmap_s            down;
*/
// END
        menutext_s                      status;
        menutext_s                      statusbar;

// STONELANCE
/*
        menubitmap_s            remove;
        menubitmap_s            back;
        menubitmap_s            refresh;
        menubitmap_s            specify;
        menubitmap_s            create;
        menubitmap_s            go;
*/
        menutext_s                      remove;
        menutext_s                      back;
        menutext_s                      refresh;
        menutext_s                      specify;
        menutext_s                      create;
        menutext_s                      go;
// END

        pinglist_t                      pinglist[MAX_PINGREQUESTS];
        table_t                         table[MAX_LISTBOXITEMS];
        char*                           items[MAX_LISTBOXITEMS];
        int                                     numqueriedservers;
        int                                     *numservers;
        servernode_t            *serverlist;    
        int                                     currentping;
        qboolean                        refreshservers;
        int                                     nextpingtime;
        int                                     maxservers;
        int                                     refreshtime;
        char                            favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
        int                                     numfavoriteaddresses;


        menubitmap_s    grlogo;
        menubitmap_s    grletters;
        menutext_s              league;
        menutext_s              practice;

} arenaservers_t;

static arenaservers_t   g_arenaservers;


static servernode_t             g_globalserverlist[UI_MAX_MASTER_SERVERS][MAX_GLOBALSERVERS];
static int                              g_numglobalservers[UI_MAX_MASTER_SERVERS];
static servernode_t             g_localserverlist[MAX_LOCALSERVERS];
static int                              g_numlocalservers;
static servernode_t             g_favoriteserverlist[MAX_FAVORITESERVERS];
static int                              g_numfavoriteservers;
static int                              g_servertype;
static int                              g_gametype;
static int                              g_sortkey;
static int                              g_onlyhumans;
static int                              g_emptyservers;
static int                              g_fullservers;

/*
=================
ArenaServers_AddToFavorites
=================
*/
static void ArenaServers_AddToFavorites(void)
{
   servernode_t* servernodeptr;
   int i;

   // check favourite server list isn't full
   if (g_numfavoriteservers == MAX_FAVORITESERVERS)
      return;

   // check we have a server list available
   if (!g_arenaservers.list.numitems)
      return;

   // check the server isn't on the favourites list already
   servernodeptr=g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
   for (i=0; i < g_numfavoriteservers; i++)
   if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
      return;

   // we already have a responsive server, no further sanity checks required
   strcpy(g_arenaservers.favoriteaddresses[g_numfavoriteservers],
      servernodeptr->adrstr);

   // copy over server details
   memcpy( &g_favoriteserverlist[g_numfavoriteservers],
      servernodeptr,sizeof(servernode_t));

   g_numfavoriteservers++;
   g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
}

/*
=================
ArenaServers_MaxPing
=================
*/
static int ArenaServers_MaxPing( void ) {
        int             maxPing;

        maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
        if( maxPing < 100 ) {
                maxPing = 100;
        }
        return maxPing;
}


/*
=================
ArenaServers_Compare
=================
*/
static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
        float                   f1;
        float                   f2;
        servernode_t*   t1;
        servernode_t*   t2;

        t1 = (servernode_t *)arg1;
        t2 = (servernode_t *)arg2;

        switch( g_sortkey ) {
        case SORT_HOST:
                return Q_stricmp( t1->hostname, t2->hostname );

        case SORT_MAP:
                return Q_stricmp( t1->mapname, t2->mapname );

        case SORT_CLIENTS:
                f1 = t1->maxclients - t1->numclients;
                if( f1 < 0 ) {
                        f1 = 0;
                }

                f2 = t2->maxclients - t2->numclients;
                if( f2 < 0 ) {
                        f2 = 0;
                }

                if( f1 < f2 ) {
                        return 1;
                }
                if( f1 == f2 ) {
                        return 0;
                }
                return -1;

        case SORT_GAME:
                if( t1->gametype < t2->gametype ) {
                        return -1;
                }
                if( t1->gametype == t2->gametype ) {
                        return 0;
                }
                return 1;

        case SORT_PING:
                if( t1->pingtime < t2->pingtime ) {
                        return -1;
                }
                if( t1->pingtime > t2->pingtime ) {
                        return 1;
                }
                return Q_stricmp( t1->hostname, t2->hostname );
        }

        return 0;
}

/*
=================
ArenaServers_SourceForLAN

Convert ui's g_servertype to AS_* used by trap calls.
=================
*/
int ArenaServers_SourceForLAN(void) {
	switch( g_servertype ) {
	default:
	case UIAS_LOCAL:
		return AS_LOCAL;
	case UIAS_GLOBAL0:
	case UIAS_GLOBAL1:
	case UIAS_GLOBAL2:
	case UIAS_GLOBAL3:
	case UIAS_GLOBAL4:
	case UIAS_GLOBAL5:
		return AS_GLOBAL;
	case UIAS_FAVORITES:
		return AS_FAVORITES;
	}
}

/*
=================
ArenaServers_GametypeForGames
=================
*/
int ArenaServers_GametypeForGames(int games) {
        int gametype;

        switch( games ) {
        default:
        case GAMES_ALL:
                gametype = -1;
                break;
// STONELANCE
/*
        case GAMES_FFA:
                gametype = GT_FFA;
                break;
*/
        case GAMES_RACING:
                gametype = GT_RACING;
                break;

        case GAMES_RACING_DM:
                gametype = GT_RACING_DM;
                break;

        case GAMES_DERBY:
                gametype = GT_DERBY;
                break;

        case GAMES_TEAM_RACING:
                gametype = GT_TEAM_RACING;
                break;

        case GAMES_TEAM_RACING_DM:
                gametype = GT_TEAM_RACING_DM;
                break;

        case GAMES_DEATHMATCH:
                gametype = GT_DEATHMATCH;
                break;
// END
        case GAMES_TEAMPLAY:
                gametype = GT_TEAM;
                break;
// STONELANCE - removed gametype
/*
        case GAMES_TOURNEY:
                gametype = GT_TOURNAMENT;
                break;
*/
// END
        case GAMES_CTF:
                gametype = GT_CTF;
                break;
                
        case GAMES_DOMINATION:
                gametype = GT_DOMINATION;
                break;
	}

	return gametype;
}

/*
=================
ArenaServers_Go
=================
*/
static void ArenaServers_Go( void ) {
        servernode_t*   servernode;

        servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
        if( servernode ) {
                trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
        }
}


/*
=================
ArenaServers_UpdatePicture
=================
*/
static void ArenaServers_UpdatePicture( void ) {
        static char             picname[64];
        servernode_t*   servernodeptr;

        if( !g_arenaservers.list.numitems ) {
                g_arenaservers.mappic.generic.name = NULL;
        }
        else {
                servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
                Com_sprintf( picname, sizeof(picname), "levelshots/%s", servernodeptr->mapname );
                g_arenaservers.mappic.generic.name = picname;
       
        }


        // force shader update during draw
        g_arenaservers.mappic.shader = 0;
}


/*
=================
ArenaServers_UpdateMenu
=================
*/
static void ArenaServers_UpdateMenu( void ) {
        int                             i;
        int                             j;
        int                             count;
        char*                   buff;
        servernode_t*   servernodeptr;
        table_t*                tableptr;
        char                    *pingColor;
        int                     gametype;

        if( g_arenaservers.numqueriedservers > 0 ) {
                // servers found
                if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
                        // show progress
                        Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
                        g_arenaservers.statusbar.string  = "Press SPACE to stop";
                        qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
                }
                else {
                        // all servers pinged - enable controls
                        g_arenaservers.save.generic.flags &= ~QMF_GRAYED;
                        g_arenaservers.master.generic.flags             &= ~QMF_GRAYED;
                        g_arenaservers.gametype.generic.flags   &= ~QMF_GRAYED;
                        g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
                        g_arenaservers.showempty.generic.flags  &= ~QMF_GRAYED;
                        g_arenaservers.onlyhumans.generic.flags &= ~QMF_GRAYED;
                        g_arenaservers.showfull.generic.flags   &= ~QMF_GRAYED;
// STONELANCE
//                      g_arenaservers.list.generic.flags               &= ~QMF_GRAYED;
                        g_arenaservers.list.generic.flags               &= ~QMF_SCROLL_ONLY;
// END
                        g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
                        g_arenaservers.go.generic.flags                 &= ~QMF_GRAYED;

                        // update status bar
                        if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) {
                                g_arenaservers.statusbar.string = quake3worldMessage;
                        }
                        else {
                                g_arenaservers.statusbar.string = "";
                        }
                        if (!(g_arenaservers.grlogo.generic.flags & QMF_HIDDEN)) {
                                g_arenaservers.statusbar.string = globalRankingsMessage;
                        }

                }
        }
        else {
                // no servers found
                if( g_arenaservers.refreshservers ) {
                        strcpy( g_arenaservers.status.string,"Scanning For Servers." );
                        g_arenaservers.statusbar.string = "Press SPACE to stop";

                        // disable controls during refresh
                        g_arenaservers.save.generic.flags |= QMF_GRAYED;
                        g_arenaservers.master.generic.flags             |= QMF_GRAYED;
                        g_arenaservers.gametype.generic.flags   |= QMF_GRAYED;
                        g_arenaservers.sortkey.generic.flags    |= QMF_GRAYED;
                        g_arenaservers.showempty.generic.flags  |= QMF_GRAYED;
                        g_arenaservers.onlyhumans.generic.flags |= QMF_GRAYED;
                        g_arenaservers.showfull.generic.flags   |= QMF_GRAYED;
// STONELANCE
//                      g_arenaservers.list.generic.flags               |= QMF_GRAYED;
                        g_arenaservers.list.generic.flags               |= QMF_SCROLL_ONLY;
// END
                        g_arenaservers.refresh.generic.flags    |= QMF_GRAYED;
                        g_arenaservers.go.generic.flags                 |= QMF_GRAYED;
                }
                else {
                        if( g_arenaservers.numqueriedservers < 0 ) {
                                strcpy(g_arenaservers.status.string,"No Response From Master Server." );
                        }
                        else {
                                strcpy(g_arenaservers.status.string,"No Servers Found." );
                        }

                        // update status bar
                        if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) {
                                g_arenaservers.statusbar.string = quake3worldMessage;
                        }
                        else {
                                g_arenaservers.statusbar.string = "";
                        }

                        // end of refresh - set control state
                        g_arenaservers.save.generic.flags |= QMF_GRAYED;
                        g_arenaservers.master.generic.flags             &= ~QMF_GRAYED;
                        g_arenaservers.gametype.generic.flags   &= ~QMF_GRAYED;
                        g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
                        g_arenaservers.showempty.generic.flags  &= ~QMF_GRAYED;
                        g_arenaservers.onlyhumans.generic.flags &= ~QMF_GRAYED;
                        g_arenaservers.showfull.generic.flags   &= ~QMF_GRAYED;
// STONELANCE
//                      g_arenaservers.list.generic.flags               |= QMF_GRAYED;
                        g_arenaservers.list.generic.flags               |= QMF_SCROLL_ONLY;
// END
                        g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
                        g_arenaservers.go.generic.flags                 |= QMF_GRAYED;
                }

                // zero out list box
                g_arenaservers.list.numitems = 0;
                g_arenaservers.list.curvalue = 0;
// STONELANCE
//              g_arenaservers.list.top      = 0;
// END

                // update picture
                ArenaServers_UpdatePicture();
                return;
        }

        // build list box strings - apply culling filters
        servernodeptr = g_arenaservers.serverlist;
        count         = *g_arenaservers.numservers;
        for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
                tableptr = &g_arenaservers.table[j];
                tableptr->servernode = servernodeptr;
                buff = tableptr->buff;

                // can only cull valid results
                if( !g_emptyservers && !servernodeptr->numclients ) {
                        continue;
                }

                //If "Show only humans" and "Hide empty server" are enabled hide servers that only have bots
                if( !g_emptyservers && g_onlyhumans && !servernodeptr->humanclients ) {
                        continue;
                }

                if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
                        continue;
                }

                gametype = ArenaServers_GametypeForGames(g_gametype);
                if( gametype != -1 && servernodeptr->gametype != gametype ) {
                        continue;
                }

                if( servernodeptr->pingtime < servernodeptr->minPing ) {
                        pingColor = S_COLOR_BLUE;
                }
                else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
                        pingColor = S_COLOR_BLUE;
                }
                else if( servernodeptr->pingtime < 200 ) {
                        pingColor = S_COLOR_GREEN;
                }
                else if( servernodeptr->pingtime < 400 ) {
                        pingColor = S_COLOR_YELLOW;
                }
                else {
                        pingColor = S_COLOR_RED;
                }

// STONELANCE
/*
                Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d",
                        servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
                        servernodeptr->maxclients, servernodeptr->gamename,
                        netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime );
*/
                Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-42.42s  %-12.12s  %2d/%2d  %-10.10s  %4s %s%5d",
                        servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
                        servernodeptr->maxclients, servernodeptr->gamename,
                        netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime );
// END

                j++;
        }

//      Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", j, *g_arenaservers.numservers );

        g_arenaservers.list.numitems = j;
        g_arenaservers.list.curvalue = 0;
// STONELANCE
//      g_arenaservers.list.top      = 0;
// END

        // update picture
        ArenaServers_UpdatePicture();
}


/*
=================
ArenaServers_Remove
=================
*/
static void ArenaServers_Remove( void )
{
        int                             i;
        servernode_t*   servernodeptr;
        table_t*                tableptr;

        if (!g_arenaservers.list.numitems)
                return;

        // remove selected item from display list
        // items are in scattered order due to sort and cull
        // perform delete on list box contents, resync all lists

        tableptr      = &g_arenaservers.table[g_arenaservers.list.curvalue];
        servernodeptr = tableptr->servernode;

        // find address in master list
        for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
                if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
                                break;

        // delete address from master list
        if (i <= g_arenaservers.numfavoriteaddresses-1)
        {
                if (i < g_arenaservers.numfavoriteaddresses-1)
                {
                        // shift items up
                        memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)*MAX_ADDRESSLENGTH);
                }
                g_arenaservers.numfavoriteaddresses--;
        }      

        // find address in server list
        for (i=0; i<g_numfavoriteservers; i++)
                if (&g_favoriteserverlist[i] == servernodeptr)
                                break;

        // delete address from server list
        if (i <= g_numfavoriteservers-1)
        {
                if (i < g_numfavoriteservers-1)
                {
                        // shift items up
                        memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
                }
                g_numfavoriteservers--;
        }      

        g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
        g_arenaservers.currentping       = g_arenaservers.numfavoriteaddresses;
}


/*
=================
ArenaServers_Insert
=================
*/
static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
{
        servernode_t*   servernodeptr;
        char*                   s;
        int                             i;


        if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != UIAS_FAVORITES))
        {
                // slow global or local servers do not get entered
                return;
        }
        //remove
        //trap_Cmd_ExecuteText( EXEC_NOW, va( "echo infostring: %s\n", info )  );

        if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
                // list full;
                servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
        } else {
                // next slot
                servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
                (*g_arenaservers.numservers)++;
        }

        Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );

        Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
        Q_CleanStr( servernodeptr->hostname );
        Q_strupr( servernodeptr->hostname );

        Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
        Q_CleanStr( servernodeptr->mapname );
        // ZTM: Fix using linux without pk3s
        //Q_strupr( servernodeptr->mapname );

        servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
        servernodeptr->humanclients = atoi( Info_ValueForKey( info, "g_humanplayers") );
        servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
        servernodeptr->pingtime   = pingtime;
        servernodeptr->minPing    = atoi( Info_ValueForKey( info, "minPing") );
        servernodeptr->maxPing    = atoi( Info_ValueForKey( info, "maxPing") );

        /*
        s = Info_ValueForKey( info, "nettype" );
        for (i=0; ;i++)
        {
                if (!netnames[i])
                {
                        servernodeptr->nettype = 0;
                        break;
                }
                else if (!Q_stricmp( netnames[i], s ))
                {
                        servernodeptr->nettype = i;
                        break;
                }
        }
        */
        servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
        if (servernodeptr->nettype < 0 || servernodeptr->nettype >= ARRAY_LEN(netnames) - 1) {
                servernodeptr->nettype = 0;
        }

        s = Info_ValueForKey( info, "game");
        i = atoi( Info_ValueForKey( info, "gametype") );
        if( i < 0 ) {
                i = 0;
        }
// STONELANCE
        else if( i >= GT_MAX_GAME_TYPE ) {
                i = GT_MAX_GAME_TYPE;
        }
/*
        else if( i > 11 ) {
                i = 12;
        }
*/
// END
        if ( *s ) {
                servernodeptr->gametype = -1;
                Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
        }
        else {
                servernodeptr->gametype = i;
                Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
        }
}



/*
=================
ArenaServers_InsertFavorites

Insert nonresponsive address book entries into display lists.
=================
*/
void ArenaServers_InsertFavorites( void )
{
        int             i;
        int             j;
        char    info[MAX_INFO_STRING];

        // resync existing results with new or deleted cvars
        info[0] = '\0';
        Info_SetValueForKey( info, "hostname", "No Response" );
        for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
        {
                // find favorite address in refresh list
                for (j=0; j<g_numfavoriteservers; j++)
                        if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
                                break;

                if ( j >= g_numfavoriteservers)
                {
                        // not in list, add it
                        ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
                }
        }
}


/*
=================
ArenaServers_LoadFavorites

Load cvar address book entries into local lists.
=================
*/
void ArenaServers_LoadFavorites( void )
{
        int                             i;
        int                             j;
        int                             numtempitems;
        char                    adrstr[MAX_ADDRESSLENGTH];
        servernode_t    templist[MAX_FAVORITESERVERS];
        qboolean                found;

        found        = qfalse;

        // copy the old
        memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
        numtempitems = g_numfavoriteservers;

        // clear the current for sync
        memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
        g_numfavoriteservers = 0;

        // resync existing results with new or deleted cvars
        for (i=0; i<MAX_FAVORITESERVERS; i++)
        {
                trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
                if (!adrstr[0])
                        continue;

                // quick sanity check to avoid slow domain name resolving
                // first character must be numeric
                if (adrstr[0] < '0' || adrstr[0] > '9')
                        continue;

                // favorite server addresses must be maintained outside refresh list
                // this mimics local and global netadr's stored in client
                // these can be fetched to fill ping list
                strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );

                // find this server in the old list
                for (j=0; j<numtempitems; j++)
                        if (!Q_stricmp( templist[j].adrstr, adrstr ))
                                break;

                if (j < numtempitems)
                {
                        // found server - add exisiting results
                        memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
                        found = qtrue;
                }
                else
                {
                        // add new server
                        Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
                        g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
                }

                g_numfavoriteservers++;
        }

        g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;

        if (!found)
        {
                // no results were found, reset server list
                // list will be automatically refreshed when selected
                g_numfavoriteservers = 0;
        }
}


/*
=================
ArenaServers_StopRefresh
=================
*/
static void ArenaServers_StopRefresh( void )
{
        if (!g_arenaservers.refreshservers)
                // not currently refreshing
                return;

        g_arenaservers.refreshservers = qfalse;

        if (g_servertype == UIAS_FAVORITES)
        {
                // nonresponsive favorites must be shown
                ArenaServers_InsertFavorites();
        }

        // final tally
        if (g_arenaservers.numqueriedservers >= 0)
        {
                g_arenaservers.currentping       = *g_arenaservers.numservers;
                g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
        }
       
        // sort
        qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);

        ArenaServers_UpdateMenu();
}


/*
=================
ArenaServers_DoRefresh
=================
*/
static void ArenaServers_DoRefresh( void )
{
        int             i;
        int             j;
        int             time;
        int             maxPing;
        char    adrstr[MAX_ADDRESSLENGTH];
        char    info[MAX_INFO_STRING];

        if (uis.realtime < g_arenaservers.refreshtime)
        {
          if (g_servertype != UIAS_FAVORITES) {
                        if (g_servertype == UIAS_LOCAL) {
                                if (!trap_LAN_GetServerCount(AS_LOCAL)) {
                                        return;
                                }
                        }
                        if (trap_LAN_GetServerCount(ArenaServers_SourceForLAN()) < 0) {
                          // still waiting for response
                          return;
                        }
          }
        }

        if (uis.realtime < g_arenaservers.nextpingtime)
        {
                // wait for time trigger
                return;
        }

        // trigger at 10Hz intervals
        g_arenaservers.nextpingtime = uis.realtime + 50;

        // process ping results
        maxPing = ArenaServers_MaxPing();
        for (i=0; i<MAX_PINGREQUESTS; i++)
        {
                trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
                if (!adrstr[0])
                {
                        // ignore empty or pending pings
                        continue;
                }

                // find ping result in our local list
                for (j=0; j<MAX_PINGREQUESTS; j++)
                        if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
                                break;

                if (j < MAX_PINGREQUESTS)
                {
                        // found it
                        if (!time)
                        {
                                time = uis.realtime - g_arenaservers.pinglist[j].start;
                                if (time < maxPing)
                                {
                                        // still waiting
                                        continue;
                                }
                        }


                        if (time > maxPing)
                        {
                                // stale it out
                                info[0] = '\0';
                                time    = maxPing;
                        }
                        else
                        {
                                trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
                        }

                        // insert ping results
                        ArenaServers_Insert( adrstr, info, time );

                        // clear this query from internal list
                        g_arenaservers.pinglist[j].adrstr[0] = '\0';
                }

                // clear this query from external list
                trap_LAN_ClearPing( i );
        }

        // get results of servers query
        // counts can increase as servers respond
        if (g_servertype == UIAS_FAVORITES) {
          g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
        } else {
          g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(ArenaServers_SourceForLAN());
        }

//      if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
//              g_arenaservers.numqueriedservers = g_arenaservers.maxservers;

        // send ping requests in reasonable bursts
        // iterate ping through all found servers
        for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
        {
                if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
                {
                        // ping queue is full
                        break;
                }

                // find empty slot
                for (j=0; j<MAX_PINGREQUESTS; j++)
                        if (!g_arenaservers.pinglist[j].adrstr[0])
                                break;

                if (j >= MAX_PINGREQUESTS)
                        // no empty slots available yet - wait for timeout
                        break;

                // get an address to ping

                if (g_servertype == UIAS_FAVORITES) {
                  strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );              
                } else {
                  trap_LAN_GetServerAddressString(ArenaServers_SourceForLAN(), g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
                }

                strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
                g_arenaservers.pinglist[j].start = uis.realtime;

                trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr )  );
               
                // advance to next server
                g_arenaservers.currentping++;
        }

        if (!trap_LAN_GetPingQueueCount())
        {
                // all pings completed
                ArenaServers_StopRefresh();
                return;
        }

        // update the user interface with ping status
        ArenaServers_UpdateMenu();
}


/*
=================
ArenaServers_StartRefresh
=================
*/
static void ArenaServers_StartRefresh( void )
{
        int             i, gametype;
        char    myargs[32], protocol[32];

        memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );

        for (i=0; i<MAX_PINGREQUESTS; i++)
        {
                g_arenaservers.pinglist[i].adrstr[0] = '\0';
                trap_LAN_ClearPing( i );
        }

        g_arenaservers.refreshservers    = qtrue;
        g_arenaservers.currentping       = 0;
        g_arenaservers.nextpingtime      = 0;
        *g_arenaservers.numservers       = 0;
        g_arenaservers.numqueriedservers = 0;

        // allow max 5 seconds for responses
        g_arenaservers.refreshtime = uis.realtime + 5000;

        // place menu in zeroed state
        ArenaServers_UpdateMenu();

        if( g_servertype == UIAS_LOCAL ) {
                trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
                return;
        }

        if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) {
                gametype = ArenaServers_GametypeForGames(g_arenaservers.gametype.curvalue);

                // Add requested gametype to args for dpmaster
                if (gametype != -1) {
                        Com_sprintf( myargs, sizeof (myargs), " gametype=%i", gametype );
                } else {
                        myargs[0] = '\0';
                }

                if (g_emptyservers) {
                        strcat(myargs, " empty");
                }

                if (g_fullservers) {
                        strcat(myargs, " full");
                }

                protocol[0] = '\0';
                trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) );
                if (strlen(protocol)) {
                        trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - UIAS_GLOBAL0, protocol, myargs ));
                }
                else {
                        trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - UIAS_GLOBAL0, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
                }
        }
}


/*
=================
ArenaServers_SaveChanges
=================
*/
void ArenaServers_SaveChanges( void )
{
        int     i;


        for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
                trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );

        for (; i<MAX_FAVORITESERVERS; i++)
                trap_Cvar_Set( va("server%d",i+1), "" );
}


/*
=================
ArenaServers_Sort
=================
*/
void ArenaServers_Sort( int type ) {
        if( g_sortkey == type ) {
                return;
        }

        g_sortkey = type;
        qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
}


/*
=================
ArenaServers_SetType
=================
*/
int ArenaServers_SetType( int type )
{
	if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
	{
		char masterstr[2], cvarname[sizeof("sv_master1")];
		int direction;
		
		if (type == g_servertype || type == ((g_servertype+1) % UIAS_NUM_SOURCES)) {
			direction = 1;
		} else {
			direction = -1;
		}

		while(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
		{
			Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type - UIAS_GLOBAL0);
			trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr));
			if(*masterstr)
				break;
			
			type += direction;
		}
	}

	g_servertype = type;

	switch( type ) {
	default:
	case UIAS_LOCAL:
		g_arenaservers.save.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.serverlist = g_localserverlist;
		g_arenaservers.numservers = &g_numlocalservers;
		g_arenaservers.maxservers = MAX_LOCALSERVERS;
		break;

	case UIAS_GLOBAL0:
	case UIAS_GLOBAL1:
	case UIAS_GLOBAL2:
	case UIAS_GLOBAL3:
	case UIAS_GLOBAL4:
	case UIAS_GLOBAL5:
		g_arenaservers.save.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.serverlist = g_globalserverlist[type-UIAS_GLOBAL0];
		g_arenaservers.numservers = &g_numglobalservers[type-UIAS_GLOBAL0];
		g_arenaservers.maxservers = MAX_GLOBALSERVERS;
		break;

	case UIAS_FAVORITES:
		g_arenaservers.save.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
		g_arenaservers.serverlist = g_favoriteserverlist;
		g_arenaservers.numservers = &g_numfavoriteservers;
		g_arenaservers.maxservers = MAX_FAVORITESERVERS;
		break;
	}

        if( !*g_arenaservers.numservers ) {
                ArenaServers_StartRefresh();
        }
        else {
                // avoid slow operation, use existing results
                g_arenaservers.currentping       = *g_arenaservers.numservers;
                g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
                ArenaServers_UpdateMenu();
                strcpy(g_arenaservers.status.string,"hit refresh to update");
        }

        return type;
}


/*
=================
ArenaServers_Event
=================
*/
static void ArenaServers_Event( void* ptr, int event ) {
        int             id;

        id = ((menucommon_s*)ptr)->id;

        if( event != QM_ACTIVATED && id != ID_LIST ) {
                return;
        }

        switch( id ) {
        case ID_MASTER:
                g_arenaservers.master.curvalue = ArenaServers_SetType( g_arenaservers.master.curvalue );
                trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue );
                break;

        case ID_GAMETYPE:
                trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
                g_gametype = g_arenaservers.gametype.curvalue;
                ArenaServers_UpdateMenu();
                break;

        case ID_SORTKEY:
                trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
                ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
                ArenaServers_UpdateMenu();
                break;

        case ID_SHOW_FULL:
                trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
                g_fullservers = g_arenaservers.showfull.curvalue;
                ArenaServers_UpdateMenu();
                break;

        case ID_SHOW_EMPTY:
                trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
                g_emptyservers = g_arenaservers.showempty.curvalue;
                ArenaServers_UpdateMenu();
                break;
                
        case ID_ONLY_HUMANS:
                trap_Cvar_SetValue( "ui_browserOnlyHumans", g_arenaservers.onlyhumans.curvalue );
                g_onlyhumans = g_arenaservers.onlyhumans.curvalue;
                ArenaServers_UpdateMenu();
                break;

        case ID_LIST:
                if( event == QM_GOTFOCUS ) {
                        ArenaServers_UpdatePicture();
                }
                break;

// STONELANCE - removed
/*
        case ID_SCROLL_UP:
                ScrollList_Key( &g_arenaservers.list, K_UPARROW );
                break;

        case ID_SCROLL_DOWN:
                ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
                break;
*/
// END

        case ID_BACK:
                ArenaServers_StopRefresh();
                ArenaServers_SaveChanges();
                UI_PopMenu();
                break;

        case ID_REFRESH:
                ArenaServers_StartRefresh();
                break;

        case ID_SPECIFY:
                UI_SpecifyServerMenu();
                break;

        case ID_CREATE:
                UI_StartServerMenu( qtrue );
                break;

        case ID_CONNECT:
                ArenaServers_Go();
                break;

        case ID_REMOVE:
                ArenaServers_Remove();
                ArenaServers_UpdateMenu();
                break;
                
        case ID_SAVE:
                ArenaServers_AddToFavorites();
                ArenaServers_SaveChanges();
                break;

        }
}


/*
=================
ArenaServers_MenuDraw
=================
*/
static void ArenaServers_MenuDraw( void )
{
        if (g_arenaservers.refreshservers)
                ArenaServers_DoRefresh();

        Menu_Draw( &g_arenaservers.menu );
}


/*
=================
ArenaServers_MenuKey
=================
*/
static sfxHandle_t ArenaServers_MenuKey( int key ) {
        if( key == K_SPACE  && g_arenaservers.refreshservers ) {
                ArenaServers_StopRefresh();    
                return menu_move_sound;
        }


        if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == UIAS_FAVORITES ) &&
                ( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
                ArenaServers_Remove();
                ArenaServers_UpdateMenu();
                return menu_move_sound;
        }

        if( key == K_MOUSE2 || key == K_ESCAPE ) {
                ArenaServers_StopRefresh();
                ArenaServers_SaveChanges();
        }


        return Menu_DefaultKey( &g_arenaservers.menu, key );
}


/*
=================
ArenaServers_MenuInit
=================
*/
static void ArenaServers_MenuInit( void ) {
        int                     i;
        int                     y;
        static char     statusbuffer[MAX_STATUSLENGTH];
        static char leaguebuffer[MAX_LEAGUELENGTH];

        // zero set all our globals
        memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );

        ArenaServers_Cache();

        g_arenaservers.menu.fullscreen = qtrue;
        g_arenaservers.menu.wrapAround = qtrue;
        g_arenaservers.menu.draw       = ArenaServers_MenuDraw;
        g_arenaservers.menu.key        = ArenaServers_MenuKey;

        g_arenaservers.banner.generic.type  = MTYPE_BTEXT;
        g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
        g_arenaservers.banner.generic.x     = 320;
        g_arenaservers.banner.generic.y     = 16;
        g_arenaservers.banner.string            = "ARENA SERVERS";
        g_arenaservers.banner.style         = UI_CENTER;
// STONELANCE
//      g_arenaservers.banner.color         = color_white;
        g_arenaservers.banner.color         = text_color_normal;
// END

       
        g_arenaservers.grlogo.generic.type  = MTYPE_BITMAP;
        g_arenaservers.grlogo.generic.name  = GLOBALRANKINGS_LOGO;
        g_arenaservers.grlogo.generic.flags = QMF_INACTIVE|QMF_HIDDEN;
        g_arenaservers.grlogo.generic.x         = 530;
        g_arenaservers.grlogo.generic.y         = 40;
        g_arenaservers.grlogo.width                     = 32;
        g_arenaservers.grlogo.height            = 64;
       
       
        g_arenaservers.league.generic.type              = MTYPE_TEXT;
        g_arenaservers.league.generic.flags             = QMF_HIDDEN;
        g_arenaservers.league.generic.x                 = g_arenaservers.grlogo.generic.x +
                                                                                          (g_arenaservers.grlogo.width / 2);
        g_arenaservers.league.generic.y                 = g_arenaservers.grlogo.generic.y +
                                                                                          g_arenaservers.grlogo.height + 2;
        g_arenaservers.league.string                    = leaguebuffer;
        g_arenaservers.league.style                             = UI_CENTER|UI_SMALLFONT;
        g_arenaservers.league.color                             = menu_text_color;
       

        g_arenaservers.practice.generic.type    = MTYPE_TEXT;
        g_arenaservers.practice.generic.flags   = QMF_HIDDEN;
        g_arenaservers.practice.generic.x               = g_arenaservers.grlogo.generic.x +
                                                                                          (g_arenaservers.grlogo.width / 2);
        g_arenaservers.practice.generic.y               = g_arenaservers.grlogo.generic.y + 6;
        g_arenaservers.practice.string                  = "practice";
        g_arenaservers.practice.style                   = UI_CENTER|UI_SMALLFONT;
        g_arenaservers.practice.color                   = menu_text_color;
       
       
        y = 80;
        g_arenaservers.master.generic.type                      = MTYPE_SPINCONTROL;
        g_arenaservers.master.generic.name                      = "Servers:";
        g_arenaservers.master.generic.flags                     = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.master.generic.callback          = ArenaServers_Event;
        g_arenaservers.master.generic.id                        = ID_MASTER;
// STONELANCE
//      g_arenaservers.master.generic.x                         = 320;
        g_arenaservers.master.generic.x                         = 330;
// END
        g_arenaservers.master.generic.y                         = y;
        g_arenaservers.master.itemnames                         = master_items;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.gametype.generic.type            = MTYPE_SPINCONTROL;
        g_arenaservers.gametype.generic.name            = "Game Type:";
        g_arenaservers.gametype.generic.flags           = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.gametype.generic.callback        = ArenaServers_Event;
        g_arenaservers.gametype.generic.id                      = ID_GAMETYPE;
// STONELANCE
//      g_arenaservers.gametype.generic.x                       = 320;
        g_arenaservers.gametype.generic.x                       = 330;
// END
        g_arenaservers.gametype.generic.y                       = y;
        g_arenaservers.gametype.itemnames                       = servertype_items;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.sortkey.generic.type                     = MTYPE_SPINCONTROL;
        g_arenaservers.sortkey.generic.name                     = "Sort By:";
        g_arenaservers.sortkey.generic.flags            = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.sortkey.generic.callback         = ArenaServers_Event;
        g_arenaservers.sortkey.generic.id                       = ID_SORTKEY;
// STONELANCE
//      g_arenaservers.sortkey.generic.x                        = 320;
        g_arenaservers.sortkey.generic.x                        = 330;
// END
        g_arenaservers.sortkey.generic.y                        = y;
        g_arenaservers.sortkey.itemnames                        = sortkey_items;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.showfull.generic.type            = MTYPE_RADIOBUTTON;
        g_arenaservers.showfull.generic.name            = "Show Full:";
        g_arenaservers.showfull.generic.flags           = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.showfull.generic.callback        = ArenaServers_Event;
        g_arenaservers.showfull.generic.id                      = ID_SHOW_FULL;
// STONELANCE
//      g_arenaservers.showfull.generic.x                       = 320;
        g_arenaservers.showfull.generic.x                       = 330;
// END
        g_arenaservers.showfull.generic.y                       = y;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.showempty.generic.type           = MTYPE_RADIOBUTTON;
        g_arenaservers.showempty.generic.name           = "Show Empty:";
        g_arenaservers.showempty.generic.flags          = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.showempty.generic.callback       = ArenaServers_Event;
        g_arenaservers.showempty.generic.id                     = ID_SHOW_EMPTY;
// STONELANCE
//      g_arenaservers.showempty.generic.x                      = 320;
        g_arenaservers.showempty.generic.x                      = 330;
// END
        g_arenaservers.showempty.generic.y                      = y;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.onlyhumans.generic.type          = MTYPE_RADIOBUTTON;
        g_arenaservers.onlyhumans.generic.name          = "Only humans:";
        g_arenaservers.onlyhumans.generic.flags         = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
        g_arenaservers.onlyhumans.generic.callback      = ArenaServers_Event;
        g_arenaservers.onlyhumans.generic.id                    = ID_ONLY_HUMANS;
        g_arenaservers.onlyhumans.generic.x                     = 330;
        g_arenaservers.onlyhumans.generic.y                     = y;

// STONELANCE
//      y += 3 * SMALLCHAR_HEIGHT;
//      g_arenaservers.list.generic.type                        = MTYPE_SCROLLLIST;
//      g_arenaservers.list.generic.flags                       = QMF_HIGHLIGHT_IF_FOCUS;
        y += 2 * SMALLCHAR_HEIGHT;
        g_arenaservers.list.generic.type                        = MTYPE_LISTBOX;
        g_arenaservers.list.scrollbarAlignment          = SB_RIGHT;
        g_arenaservers.list.generic.flags                       = QMF_CENTER_JUSTIFY|QMF_HIGHLIGHT_IF_FOCUS;
// END
        g_arenaservers.list.generic.id                          = ID_LIST;
        g_arenaservers.list.generic.callback            = ArenaServers_Event;
// STONELANCE
//      g_arenaservers.list.generic.x                           = 72;
        g_arenaservers.list.generic.x                           = 320;
// END
        g_arenaservers.list.generic.y                           = y;
        g_arenaservers.list.width                                       = MAX_LISTBOXWIDTH;
        g_arenaservers.list.height                                      = 11;
        g_arenaservers.list.itemnames                           = (const char **)g_arenaservers.items;
        for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
                g_arenaservers.items[i] = g_arenaservers.table[i].buff;
        }

        g_arenaservers.mappic.generic.type                      = MTYPE_BITMAP;
        g_arenaservers.mappic.generic.flags                     = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
        g_arenaservers.mappic.generic.x                         = 72;
        g_arenaservers.mappic.generic.y                         = 80;
        g_arenaservers.mappic.width                                     = 128;
        g_arenaservers.mappic.height                            = 96;
        g_arenaservers.mappic.errorpic                          = ART_UNKNOWNMAP;

// STONELANCE
/*
        g_arenaservers.arrows.generic.type                      = MTYPE_BITMAP;
        g_arenaservers.arrows.generic.name                      = ART_ARROWS0;
        g_arenaservers.arrows.generic.flags                     = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
        g_arenaservers.arrows.generic.callback          = ArenaServers_Event;
        g_arenaservers.arrows.generic.x                         = 512+48;
        g_arenaservers.arrows.generic.y                         = 240-64+16;
        g_arenaservers.arrows.width                                     = 64;
        g_arenaservers.arrows.height                            = 128;

        g_arenaservers.up.generic.type                          = MTYPE_BITMAP;
        g_arenaservers.up.generic.flags                         = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
        g_arenaservers.up.generic.callback                      = ArenaServers_Event;
        g_arenaservers.up.generic.id                            = ID_SCROLL_UP;
        g_arenaservers.up.generic.x                                     = 512+48;
        g_arenaservers.up.generic.y                                     = 240-64+16;
        g_arenaservers.up.width                                         = 64;
        g_arenaservers.up.height                                        = 64;
        g_arenaservers.up.focuspic                                      = ART_ARROWS_UP;


        g_arenaservers.down.generic.type                        = MTYPE_BITMAP;
        g_arenaservers.down.generic.flags                       = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
        g_arenaservers.down.generic.callback            = ArenaServers_Event;
        g_arenaservers.down.generic.id                          = ID_SCROLL_DOWN;
        g_arenaservers.down.generic.x                           = 512+48;
        g_arenaservers.down.generic.y                           = 240+16;
        g_arenaservers.down.width                                       = 64;
        g_arenaservers.down.height                                      = 64;
        g_arenaservers.down.focuspic                            = ART_ARROWS_DOWN;
*/
// END

        y = 376;
        g_arenaservers.status.generic.type              = MTYPE_TEXT;
        g_arenaservers.status.generic.x                 = 320;
        g_arenaservers.status.generic.y                 = y;
        g_arenaservers.status.string                    = statusbuffer;
        g_arenaservers.status.style                             = UI_CENTER|UI_SMALLFONT;
        g_arenaservers.status.color                             = menu_text_color;

        y += SMALLCHAR_HEIGHT;
        g_arenaservers.statusbar.generic.type   = MTYPE_TEXT;
        g_arenaservers.statusbar.generic.x          = 320;
        g_arenaservers.statusbar.generic.y          = y;
        g_arenaservers.statusbar.string         = "";
        g_arenaservers.statusbar.style          = UI_CENTER|UI_SMALLFONT;
        g_arenaservers.statusbar.color          = text_color_normal;

// STONELANCE
/*
        g_arenaservers.remove.generic.type              = MTYPE_BITMAP;
        g_arenaservers.remove.generic.name              = ART_REMOVE0;
        g_arenaservers.remove.generic.flags             = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.remove.generic.callback  = ArenaServers_Event;
        g_arenaservers.remove.generic.id                = ID_REMOVE;
        g_arenaservers.remove.generic.x                 = 450;
        g_arenaservers.remove.generic.y                 = 86;
        g_arenaservers.remove.width                             = 96;
        g_arenaservers.remove.height                    = 48;
        g_arenaservers.remove.focuspic                  = ART_REMOVE1;

        g_arenaservers.back.generic.type                = MTYPE_BITMAP;
        g_arenaservers.back.generic.name                = ART_BACK0;
        g_arenaservers.back.generic.flags               = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.back.generic.callback    = ArenaServers_Event;
        g_arenaservers.back.generic.id                  = ID_BACK;
        g_arenaservers.back.generic.x                   = 0;
        g_arenaservers.back.generic.y                   = 480-64;
        g_arenaservers.back.width                               = 128;
        g_arenaservers.back.height                              = 64;
        g_arenaservers.back.focuspic                    = ART_BACK1;

        g_arenaservers.specify.generic.type         = MTYPE_BITMAP;
        g_arenaservers.specify.generic.name             = ART_SPECIFY0;
        g_arenaservers.specify.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.specify.generic.callback = ArenaServers_Event;
        g_arenaservers.specify.generic.id           = ID_SPECIFY;
        g_arenaservers.specify.generic.x                = 128;
        g_arenaservers.specify.generic.y                = 480-64;
        g_arenaservers.specify.width                = 128;
        g_arenaservers.specify.height               = 64;
        g_arenaservers.specify.focuspic         = ART_SPECIFY1;

        g_arenaservers.refresh.generic.type             = MTYPE_BITMAP;
        g_arenaservers.refresh.generic.name             = ART_REFRESH0;
        g_arenaservers.refresh.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.refresh.generic.callback = ArenaServers_Event;
        g_arenaservers.refresh.generic.id               = ID_REFRESH;
        g_arenaservers.refresh.generic.x                = 256;
        g_arenaservers.refresh.generic.y                = 480-64;
        g_arenaservers.refresh.width                    = 128;
        g_arenaservers.refresh.height                   = 64;
        g_arenaservers.refresh.focuspic                 = ART_REFRESH1;

        g_arenaservers.create.generic.type              = MTYPE_BITMAP;
        g_arenaservers.create.generic.name              = ART_CREATE0;
        g_arenaservers.create.generic.flags             = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.create.generic.callback  = ArenaServers_Event;
        g_arenaservers.create.generic.id                = ID_CREATE;
        g_arenaservers.create.generic.x                 = 384;
        g_arenaservers.create.generic.y                 = 480-64;
        g_arenaservers.create.width                             = 128;
        g_arenaservers.create.height                    = 64;
        g_arenaservers.create.focuspic                  = ART_CREATE1;

        g_arenaservers.go.generic.type                  = MTYPE_BITMAP;
        g_arenaservers.go.generic.name                  = ART_CONNECT0;
        g_arenaservers.go.generic.flags                 = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.go.generic.callback              = ArenaServers_Event;
        g_arenaservers.go.generic.id                    = ID_CONNECT;
        g_arenaservers.go.generic.x                             = 640;
        g_arenaservers.go.generic.y                             = 480-64;
        g_arenaservers.go.width                                 = 128;
        g_arenaservers.go.height                                = 64;
        g_arenaservers.go.focuspic                              = ART_CONNECT1;
*/
        g_arenaservers.remove.generic.type              = MTYPE_PTEXT;
        g_arenaservers.remove.generic.flags             = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.remove.generic.callback  = ArenaServers_Event;
        g_arenaservers.remove.generic.id                = ID_REMOVE;
        g_arenaservers.remove.generic.x                 = 480;
        g_arenaservers.remove.generic.y                 = 86;
        g_arenaservers.remove.string                    = "<DELETE>";
        g_arenaservers.remove.color                             = text_color_normal;
        g_arenaservers.remove.style                             = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.save.generic.type                = MTYPE_PTEXT;
        g_arenaservers.save.generic.flags               = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.save.generic.callback            = ArenaServers_Event;
        g_arenaservers.save.generic.id                  = ID_SAVE;
        g_arenaservers.save.generic.x                   = 480;
        g_arenaservers.save.generic.y                   = 87;
        g_arenaservers.save.string                      = "<SAVE>";
        g_arenaservers.save.color                             = text_color_normal;
        g_arenaservers.save.style                             = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.back.generic.type                = MTYPE_PTEXT;
        g_arenaservers.back.generic.flags               = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.back.generic.x                   = 20;
        g_arenaservers.back.generic.y                   = 480 - 50;
        g_arenaservers.back.generic.id                  = ID_BACK;
        g_arenaservers.back.generic.callback    = ArenaServers_Event;
        g_arenaservers.back.string                              = "< BACK";
        g_arenaservers.back.color                               = text_color_normal;
        g_arenaservers.back.style                               = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.specify.generic.type         = MTYPE_PTEXT;
        g_arenaservers.specify.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.specify.generic.x                = 160 - 32;
        g_arenaservers.specify.generic.y                = 480-50;
        g_arenaservers.specify.generic.id           = ID_SPECIFY;
        g_arenaservers.specify.generic.callback = ArenaServers_Event;
        g_arenaservers.specify.string                   = "<SPECIFY>";
        g_arenaservers.specify.color                    = text_color_normal;
        g_arenaservers.specify.style                    = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.refresh.generic.type             = MTYPE_PTEXT;
        g_arenaservers.refresh.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.refresh.generic.x                = 320 - 52;
        g_arenaservers.refresh.generic.y                = 480 - 50;
        g_arenaservers.refresh.generic.id               = ID_REFRESH;
        g_arenaservers.refresh.generic.callback = ArenaServers_Event;
        g_arenaservers.refresh.string                   = "<REFRESH>";
        g_arenaservers.refresh.color                    = text_color_normal;
        g_arenaservers.refresh.style                    = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.create.generic.type              = MTYPE_PTEXT;
        g_arenaservers.create.generic.flags             = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.create.generic.x                 = 460 - 56;
        g_arenaservers.create.generic.y                 = 480 - 50;
        g_arenaservers.create.generic.id                = ID_CREATE;
        g_arenaservers.create.generic.callback  = ArenaServers_Event;
        g_arenaservers.create.string                    = "<CREATE>";
        g_arenaservers.create.color                             = text_color_normal;
        g_arenaservers.create.style                             = UI_LEFT | UI_SMALLFONT;

        g_arenaservers.go.generic.type                  = MTYPE_PTEXT;
        g_arenaservers.go.generic.flags                 = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
        g_arenaservers.go.generic.x                             = 640 - 20;
        g_arenaservers.go.generic.y                             = 480 - 50;
        g_arenaservers.go.generic.id                    = ID_CONNECT;
        g_arenaservers.go.generic.callback              = ArenaServers_Event;
        g_arenaservers.go.string                                = "CONNECT >";
        g_arenaservers.go.color                                 = text_color_normal;
        g_arenaservers.go.style                                 = UI_RIGHT | UI_SMALLFONT;
// END

        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );

        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.grlogo );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.league );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.practice );

        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.onlyhumans );

        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar );
// STONELANCE
/*
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down );
*/
// END

        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.save );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
        Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );

        ArenaServers_LoadFavorites();

        g_arenaservers.master.curvalue = g_servertype = Com_Clamp( 0, UIAS_NUM_SOURCES-1, ui_browserMaster.integer );

        g_gametype = Com_Clamp( 0, GAMES_NUM_GAMES-1, ui_browserGameType.integer );
        g_arenaservers.gametype.curvalue = g_gametype;

        g_sortkey = Com_Clamp( 0, SORT_NUM_SORTS-1, ui_browserSortKey.integer );
        g_arenaservers.sortkey.curvalue = g_sortkey;

        g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
        g_arenaservers.showfull.curvalue = g_fullservers;

        g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
        g_arenaservers.showempty.curvalue = g_emptyservers;

        g_arenaservers.onlyhumans.curvalue = Com_Clamp( 0, 1, ui_browserOnlyHumans.integer );
        g_onlyhumans = ui_browserOnlyHumans.integer;

        // force to initial state and refresh
	g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype);

        trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
}


/*
=================
ArenaServers_Cache
=================
*/
void ArenaServers_Cache( void ) {
// STOENLANCE
/*
        trap_R_RegisterShaderNoMip( ART_BACK0 );
        trap_R_RegisterShaderNoMip( ART_BACK1 );
        trap_R_RegisterShaderNoMip( ART_CREATE0 );
        trap_R_RegisterShaderNoMip( ART_CREATE1 );
        trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
        trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
        trap_R_RegisterShaderNoMip( ART_REFRESH0 );
        trap_R_RegisterShaderNoMip( ART_REFRESH1 );
        trap_R_RegisterShaderNoMip( ART_CONNECT0 );
        trap_R_RegisterShaderNoMip( ART_CONNECT1 );
        trap_R_RegisterShaderNoMip( ART_ARROWS0  );
        trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
        trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
*/
// END
        trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );

        trap_R_RegisterShaderNoMip( GLOBALRANKINGS_LOGO );
}


/*
=================
UI_ArenaServersMenu
=================
*/
void UI_ArenaServersMenu( void ) {
        ArenaServers_MenuInit();
        UI_PushMenu( &g_arenaservers.menu );
}