/*
Copyright (C) 1997-2001 Id Software, Inc.

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

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// menu_main.c -- the multiplayer menu 
// Copyright (C) 2001-2003 pat@aftermoon.net for modif flanked by <serverping>

#include <ctype.h>
#ifdef _WIN32
#include <io.h>
#endif
#include "client.h"
#include "../client/qmenu.h"


static char *menu_move_sound	= "misc/menu2.wav";
/*
=======================================================================
STATIC FUNCTIONS
=======================================================================
*/
static float ClampCvar( float min, float max, float value )
{
	if ( value < min ) return min;
	if ( value > max ) return max;
	return value;
}

static void FreeFileList( char **list, int n )
{
	int i;

	for ( i = 0; i < n; i++ )
	{
		if ( list && list[i] )
		{
			free( list[i] );
			list[i] = 0;
		}
	}
	free( list );
}

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

MULTIPLAYER MENU

=======================================================================
*/
void DownloadOptionsFunc( void *self );
static menuframework_s	s_multiplayer_menu;
static menuaction_s		s_join_network_server_action;
static menuaction_s		s_start_network_server_action;
static menuaction_s		s_player_setup_action;
static menuaction_s		s_download_options_action;
static menuaction_s		s_backmain_action;

static void Multiplayer_MenuDraw (void)
{
	M_Banner( "m_banner_multiplayer" );

	Menu_AdjustCursor( &s_multiplayer_menu, 1 );
	Menu_Draw( &s_multiplayer_menu );
}

static void PlayerSetupFunc( void *unused )
{
	M_Menu_PlayerConfig_f();
}

static void JoinNetworkServerFunc( void *unused )
{
	M_Menu_JoinServer_f();
}

static void StartNetworkServerFunc( void *unused )
{
	M_Menu_StartServer_f ();
}

void Multiplayer_MenuInit( void )
{
	s_multiplayer_menu.x = viddef.width * 0.50 - scaledVideo(8*MENU_FONT_SIZE); //64
	s_multiplayer_menu.nitems = 0;

	s_join_network_server_action.generic.type	= MTYPE_ACTION;
	s_join_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_join_network_server_action.generic.x		= 0;
	s_join_network_server_action.generic.y		= 0;
	s_join_network_server_action.generic.name	= " join network server";
	s_join_network_server_action.generic.callback = JoinNetworkServerFunc;

	s_start_network_server_action.generic.type	= MTYPE_ACTION;
	s_start_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_start_network_server_action.generic.x		= 0;
	s_start_network_server_action.generic.y		= 2*MENU_FONT_SIZE;	// was 10
	s_start_network_server_action.generic.name	= " start network server";
	s_start_network_server_action.generic.callback = StartNetworkServerFunc;

	s_player_setup_action.generic.type	= MTYPE_ACTION;
	s_player_setup_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_player_setup_action.generic.x		= 0;
	s_player_setup_action.generic.y		= 4*MENU_FONT_SIZE;	// was 20
	s_player_setup_action.generic.name	= " player setup";
	s_player_setup_action.generic.callback = PlayerSetupFunc;

	s_download_options_action.generic.type = MTYPE_ACTION;
	s_download_options_action.generic.flags = QMF_LEFT_JUSTIFY;
	s_download_options_action.generic.x	= 0;
	s_download_options_action.generic.y	= 6*MENU_FONT_SIZE;
	s_download_options_action.generic.name	= " download options";
	s_download_options_action.generic.callback = DownloadOptionsFunc;

	s_backmain_action.generic.type	= MTYPE_ACTION;
	s_backmain_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_backmain_action.generic.x		= 0;
	s_backmain_action.generic.y		= 9*MENU_FONT_SIZE;
	s_backmain_action.generic.name	= " back to main";
	s_backmain_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_join_network_server_action );
	Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_start_network_server_action );
	Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_player_setup_action );
	Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_download_options_action );
	Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_backmain_action );

	Menu_SetStatusBar( &s_multiplayer_menu, NULL );

	Menu_Center( &s_multiplayer_menu );
}

const char *Multiplayer_MenuKey( int key )
{
	return Default_MenuKey( &s_multiplayer_menu, key );
}

void M_Menu_Multiplayer_f( void )
{
	Multiplayer_MenuInit();
	M_PushMenu( Multiplayer_MenuDraw, Multiplayer_MenuKey );
}

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

JOIN SERVER MENU

=============================================================================
*/
#define MAX_LOCAL_SERVERS 12

static menuframework_s	s_joinserver_menu;
static menuseparator_s	s_joinserver_server_title;

//Knightmare- client compatibility option
static menulist_s		s_joinserver_compatibility_box;
static menuseparator_s	s_joinserver_compat_title;

static menuaction_s		s_joinserver_search_action;
static menuaction_s		s_joinserver_address_book_action;
static menuaction_s		s_joinserver_server_actions[MAX_LOCAL_SERVERS];
static menuaction_s		s_joinserver_back_action;

int		m_num_servers;
#define	NO_SERVER_STRING	"<no server>"

// user readable information
static char local_server_names[MAX_LOCAL_SERVERS][80];

// network address
static netadr_t local_server_netadr[MAX_LOCAL_SERVERS];


void SearchLocalGames( void );

//Knightmare- client compatibility option
static void ClientCompatibilityFunc( void *unused )
{
	Cvar_SetValue( "cl_servertrick", s_joinserver_compatibility_box.curvalue );
}


#if 1
//<serverping> Added code for compute ping time of server broadcasted
// The server is displayed like : 
// "protocol ping hostname mapname nb players/max players"
// "udp 100ms Pat  q2dm1 2/8"

int      global_udp_server_time;
int      global_ipx_server_time;
int      global_adr_server_time[16];
netadr_t global_adr_server_netadr[16];

void M_AddToServerList (netadr_t adr, char *info)
{
	int		i;
    int     iPing;
    char    *pszProtocol;

	if (m_num_servers == MAX_LOCAL_SERVERS)
		return;
	while ( *info == ' ' )
		info++;

	// Ignore if duplicated
	for (i=0 ; i<m_num_servers ; i++)
        if ( strncmp(info, &local_server_names[i][11], sizeof(local_server_names[0])-10)==0 )
			return;

    iPing = 0 ;
	for (i=0 ; i<MAX_LOCAL_SERVERS ; i++)
    {
        if (    memcmp(&adr.ip, &global_adr_server_netadr[i].ip, sizeof(adr.ip))==0 
             && adr.port == global_adr_server_netadr[i].port )
        {
            // bookmark server
            iPing = Sys_Milliseconds() - global_adr_server_time[i] ;
            pszProtocol = "bkm" ;
            break;
        }
    }
    if ( i == MAX_LOCAL_SERVERS )
    {
        if ( adr.ip[0] > 0 )    // udp server
        {
            iPing = Sys_Milliseconds() - global_udp_server_time ;
            pszProtocol = "UDP" ;
        }
        else                    // ipx server
        {
            iPing = Sys_Milliseconds() - global_ipx_server_time ;
            pszProtocol = "IPX" ;
        }
    }

    Com_sprintf( local_server_names[m_num_servers], 
                 sizeof(local_server_names[0]), 
                 "%s %4dms %s", pszProtocol, iPing, info ) ;

    local_server_netadr[m_num_servers] = adr;

    m_num_servers++;
}
// </serverping>
#else
void M_AddToServerList (netadr_t adr, char *info)
{
	int		i;

	if (m_num_servers == MAX_LOCAL_SERVERS)
		return;
	while ( *info == ' ' )
		info++;

	// ignore if duplicated
	for (i=0 ; i<m_num_servers ; i++)
		if (!strcmp(info, local_server_names[i]))
			return;

	local_server_netadr[m_num_servers] = adr;
	strncpy (local_server_names[m_num_servers], info, sizeof(local_server_names[0])-1);
	m_num_servers++;
}
#endif

void JoinServerFunc( void *self )
{
	char	buffer[128];
	int		index;

	index = ( menuaction_s * ) self - s_joinserver_server_actions;

	if ( Q_stricmp( local_server_names[index], NO_SERVER_STRING ) == 0 )
		return;

	if (index >= m_num_servers)
		return;

	Com_sprintf (buffer, sizeof(buffer), "connect %s\n", NET_AdrToString (local_server_netadr[index]));
	Cbuf_AddText (buffer);
	M_ForceMenuOff ();
	cls.disable_screen = 1; // Knightmare- show loading screen
}

void AddressBookFunc( void *self )
{
	M_Menu_AddressBook_f();
}

//Knightmare- init client compatibility menu option
static void JoinserverSetMenuItemValues( void )
{
	Cvar_SetValue( "cl_servertrick", ClampCvar( 0, 1, Cvar_VariableValue("cl_servertrick") ) );
	s_joinserver_compatibility_box.curvalue		= Cvar_VariableValue("cl_servertrick");
}

void NullCursorDraw( void *self )
{
}

void SearchLocalGames( void )
{
	int		i;

	m_num_servers = 0;
	for (i=0 ; i<MAX_LOCAL_SERVERS ; i++)
		strcpy (local_server_names[i], NO_SERVER_STRING);

	M_DrawTextBox( 8, 120 - 48, 36, 3 );
	M_Print( 28, 120 - 48 + MENU_FONT_SIZE,  "Searching for local servers, this" );
	M_Print( 28, 120 - 48 + MENU_FONT_SIZE*2, "could take up to a minute, so" );
	M_Print( 28, 120 - 48 + MENU_FONT_SIZE*3, "please be patient." );

	// the text box won't show up unless we do a buffer swap
	GLimp_EndFrame();

	// send out info packets
	CL_PingServers_f();
}

void SearchLocalGamesFunc( void *self )
{
	SearchLocalGames();
}


void JoinServer_MenuInit( void )
{
	int i;
	int y = 30;

	static const char *compatibility_names[] =
	{
		"version 35 (KMQuake2)",
		"version 34 (stock Quake2)",
		0
	};

	JoinserverSetMenuItemValues(); // init item values

	s_joinserver_menu.x = viddef.width * 0.50 - scaledVideo(120);
	s_joinserver_menu.nitems = 0;

	// Knightmare- init client compatibility menu option
	s_joinserver_compat_title.generic.type = MTYPE_SEPARATOR;
	s_joinserver_compat_title.generic.name = "client protocol compatibility";
	s_joinserver_compat_title.generic.x    = 200;
	s_joinserver_compat_title.generic.y	   = y; //30

	s_joinserver_compatibility_box.generic.type = MTYPE_SPINCONTROL;
	s_joinserver_compatibility_box.generic.name	= "";
	s_joinserver_compatibility_box.generic.x	= -32;
	s_joinserver_compatibility_box.generic.y	= y += (MENU_FONT_SIZE+2); //40
	s_joinserver_compatibility_box.generic.cursor_offset = -24;
	s_joinserver_compatibility_box.generic.callback = ClientCompatibilityFunc;
	s_joinserver_compatibility_box.generic.statusbar = "set to version 34 to ping non-KMQuake2 servers";
	s_joinserver_compatibility_box.itemnames = compatibility_names;
	// end Knightmare

	s_joinserver_address_book_action.generic.type	= MTYPE_ACTION;
	s_joinserver_address_book_action.generic.name	= "address book";
	s_joinserver_address_book_action.generic.flags	= QMF_LEFT_JUSTIFY;
	s_joinserver_address_book_action.generic.x		= 0;
	s_joinserver_address_book_action.generic.y		= y += 2*(MENU_FONT_SIZE+2); //60
	s_joinserver_address_book_action.generic.callback = AddressBookFunc;

	s_joinserver_search_action.generic.type = MTYPE_ACTION;
	s_joinserver_search_action.generic.name	= "refresh server list";
	s_joinserver_search_action.generic.flags	= QMF_LEFT_JUSTIFY;
	s_joinserver_search_action.generic.x	= 0;
	s_joinserver_search_action.generic.y	= y += (MENU_FONT_SIZE+2); //70
	s_joinserver_search_action.generic.callback = SearchLocalGamesFunc;
	s_joinserver_search_action.generic.statusbar = "search for servers";

	s_joinserver_server_title.generic.type = MTYPE_SEPARATOR;
	s_joinserver_server_title.generic.name = "connect to...";
	s_joinserver_server_title.generic.x    = 80;
	s_joinserver_server_title.generic.y	   = y += 2*(MENU_FONT_SIZE+2); //90

	y += (MENU_FONT_SIZE+2); //100
	for ( i = 0; i < MAX_LOCAL_SERVERS; i++ )
	{
		s_joinserver_server_actions[i].generic.type	= MTYPE_ACTION;
		strcpy (local_server_names[i], NO_SERVER_STRING);
		s_joinserver_server_actions[i].generic.name	= local_server_names[i];
		s_joinserver_server_actions[i].generic.flags	= QMF_LEFT_JUSTIFY;
		s_joinserver_server_actions[i].generic.x		= 0;
		s_joinserver_server_actions[i].generic.y		= y + i*(MENU_FONT_SIZE+2); //100 + i*10;
		s_joinserver_server_actions[i].generic.callback = JoinServerFunc;
		s_joinserver_server_actions[i].generic.statusbar = "press ENTER to connect";
	}

	s_joinserver_back_action.generic.type = MTYPE_ACTION;
	s_joinserver_back_action.generic.name	= "back to multiplayer";
	s_joinserver_back_action.generic.flags	= QMF_LEFT_JUSTIFY;
	s_joinserver_back_action.generic.x	= 0;
	s_joinserver_back_action.generic.y	= y += (MAX_LOCAL_SERVERS+2)*(MENU_FONT_SIZE+2); //70
	s_joinserver_back_action.generic.callback = M_BackMenu;

	// Knightmare- init client compatibility menu option
	Menu_AddItem( &s_joinserver_menu, &s_joinserver_compat_title );
	Menu_AddItem( &s_joinserver_menu, &s_joinserver_compatibility_box );

	Menu_AddItem( &s_joinserver_menu, &s_joinserver_address_book_action );
	Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_title );
	Menu_AddItem( &s_joinserver_menu, &s_joinserver_search_action );

	for ( i = 0; i < MAX_LOCAL_SERVERS; i++ )
		Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_actions[i] );

	Menu_AddItem( &s_joinserver_menu, &s_joinserver_back_action );

	Menu_Center( &s_joinserver_menu );

	// skip over compatibility title
	if (s_joinserver_menu.cursor == 0)
		s_joinserver_menu.cursor = 1;

	SearchLocalGames();
}

void JoinServer_MenuDraw(void)
{
	M_Banner( "m_banner_join_server" );
	Menu_Draw( &s_joinserver_menu );
}


const char *JoinServer_MenuKey( int key )
{
	return Default_MenuKey( &s_joinserver_menu, key );
}

void M_Menu_JoinServer_f (void)
{
	JoinServer_MenuInit();
	M_PushMenu( JoinServer_MenuDraw, JoinServer_MenuKey );
}


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

START SERVER MENU

=============================================================================
*/
static menuframework_s s_startserver_menu;
static char **mapnames;
static int	  nummaps;

#define M_UNSET 0
#define	M_MISSING 1
#define	M_FOUND 2
static	int *svr_mapshotvalid; // levelshot truth table

static menuaction_s	s_startserver_start_action;
static menuaction_s	s_startserver_dmoptions_action;
static menufield_s	s_timelimit_field;
static menufield_s	s_fraglimit_field;
static menufield_s	s_maxclients_field;
static menufield_s	s_hostname_field;
static menulist_s	s_startmap_list;
static menulist_s	s_rules_box;
static menuaction_s	s_startserver_back_action;

void DMOptionsFunc( void *self )
{
	if (s_rules_box.curvalue == 1)
		return;
	M_Menu_DMOptions_f();
}

void RulesChangeFunc ( void *self )
{
	if (s_rules_box.curvalue == 0) 	// DM
	{
		s_maxclients_field.generic.statusbar = NULL;
		s_startserver_dmoptions_action.generic.statusbar = NULL;
	}
	else if(s_rules_box.curvalue == 1)		// coop				// PGM
	{
		s_maxclients_field.generic.statusbar = "4 maximum for cooperative";
		if (atoi(s_maxclients_field.buffer) > 4)
			strcpy( s_maxclients_field.buffer, "4" );
		s_startserver_dmoptions_action.generic.statusbar = "N/A for cooperative";
	}
	// ROGUE GAMES
	//Knightmare 12/23/2001
	else if (roguepath() && s_rules_box.curvalue == 2) // tag	
	{
		s_maxclients_field.generic.statusbar = NULL;
		s_startserver_dmoptions_action.generic.statusbar = NULL;
	}
	else if ( (roguepath() && s_rules_box.curvalue == 3) // CTF
		|| (!roguepath() && s_rules_box.curvalue == 2) )
	{
		s_maxclients_field.generic.statusbar = NULL;
		if (atoi(s_maxclients_field.buffer) <= 12) // set default of 8
			strcpy( s_maxclients_field.buffer, "12" );
		s_startserver_dmoptions_action.generic.statusbar = NULL;
	}
	else if ( (roguepath() && s_rules_box.curvalue == 4) // 3Team CTF
		|| (!roguepath() && s_rules_box.curvalue == 3) )
	{
		s_maxclients_field.generic.statusbar = NULL;
		if (atoi(s_maxclients_field.buffer) <= 18) // set default of 8
			strcpy( s_maxclients_field.buffer, "18" );
		s_startserver_dmoptions_action.generic.statusbar = NULL;
	}
}

void StartServerActionFunc( void *self )
{
	char	startmap[1024];
	int		timelimit;
	int		fraglimit;
	int		maxclients;
	char	*spot;

	strcpy( startmap, strchr( mapnames[s_startmap_list.curvalue], '\n' ) + 1 );

	maxclients  = atoi( s_maxclients_field.buffer );
	timelimit	= atoi( s_timelimit_field.buffer );
	fraglimit	= atoi( s_fraglimit_field.buffer );

	Cvar_SetValue( "maxclients", ClampCvar( 0, maxclients, maxclients ) );
	Cvar_SetValue ("timelimit", ClampCvar( 0, timelimit, timelimit ) );
	Cvar_SetValue ("fraglimit", ClampCvar( 0, fraglimit, fraglimit ) );
	Cvar_Set("hostname", s_hostname_field.buffer );

	// Knightmare- redid this
	if (roguepath()) {
		Cvar_SetValue ("deathmatch", s_rules_box.curvalue != 1);
		Cvar_SetValue ("coop", s_rules_box.curvalue == 1);
		Cvar_SetValue ("gamerules", s_rules_box.curvalue == 2 );
		Cvar_SetValue ("ctf", s_rules_box.curvalue >= 3 );
		Cvar_SetValue ("ttctf", s_rules_box.curvalue == 4 );
	} else {
		Cvar_SetValue ("deathmatch", s_rules_box.curvalue != 1);
		Cvar_SetValue ("coop", s_rules_box.curvalue == 1);
		Cvar_SetValue ("gamerules", 0 );
		Cvar_SetValue ("ctf", s_rules_box.curvalue >= 2 );
		Cvar_SetValue ("ttctf", s_rules_box.curvalue == 3 );
	}

	spot = NULL;
	if (s_rules_box.curvalue == 1)		// PGM
	{
 		if(Q_stricmp(startmap, "bunk1") == 0)
  			spot = "start";
 		else if(Q_stricmp(startmap, "mintro") == 0)
  			spot = "start";
 		else if(Q_stricmp(startmap, "fact1") == 0)
  			spot = "start";
 		else if(Q_stricmp(startmap, "power1") == 0)
  			spot = "pstart";
 		else if(Q_stricmp(startmap, "biggun") == 0)
  			spot = "bstart";
 		else if(Q_stricmp(startmap, "hangar1") == 0)
  			spot = "unitstart";
 		else if(Q_stricmp(startmap, "city1") == 0)
  			spot = "unitstart";
 		else if(Q_stricmp(startmap, "boss1") == 0)
			spot = "bosstart";
	}

	if (spot)
	{
		if (Com_ServerState())
			Cbuf_AddText ("disconnect\n");
		Cbuf_AddText (va("gamemap \"*%s$%s\"\n", startmap, spot));
	}
	else
	{
		Cbuf_AddText (va("map %s\n", startmap));
	}

	M_ForceMenuOff ();
}

void StartServer_MenuInit( void )
{
	static const char *dm_coop_names[] =
	{
		"deathmatch",
		"cooperative",
		"CTF",
		"3Team CTF",
		0
	};
//=======
//PGM
	static const char *dm_coop_names_rogue[] =
	{
		"deathmatch",
		"cooperative",
		"tag",
		"CTF",
		"3Team CTF",
//		"deathball",
		0
	};
//PGM
//=======
	char *buffer;
	char  mapsname[1024];
	char *s;
	int length;
	int i;
	int y = 0;
	FILE *fp;

	/*
	** load the list of map names
	*/
	Com_sprintf( mapsname, sizeof( mapsname ), "%s/maps.lst", FS_Gamedir() );
	if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
	{
		if ( ( length = FS_LoadFile( "maps.lst", ( void ** ) &buffer ) ) == -1 )
			Com_Error( ERR_DROP, "couldn't find maps.lst\n" );
	}
	else
	{
#ifdef _WIN32
		length = filelength( fileno( fp  ) );
#else
		fseek(fp, 0, SEEK_END);
		length = ftell(fp);
		fseek(fp, 0, SEEK_SET);
#endif
		buffer = malloc( length );
		fread( buffer, length, 1, fp );
	}

	s = buffer;

	i = 0;
	while ( i < length )
	{
		if ( s[i] == '\r' )
			nummaps++;
		i++;
	}

	if ( nummaps == 0 )
		Com_Error( ERR_DROP, "no maps in maps.lst\n" );

	mapnames = malloc( sizeof( char * ) * ( nummaps + 1 ) );
	memset( mapnames, 0, sizeof( char * ) * ( nummaps + 1 ) );

	// levelshot truth table
	svr_mapshotvalid = malloc( sizeof( int ) * ( nummaps + 1 ) );
	memset( svr_mapshotvalid, 0, sizeof( int ) * ( nummaps + 1 ) );

	s = buffer;

	for ( i = 0; i < nummaps; i++ )
	{
		char  shortname[MAX_TOKEN_CHARS];
		char  longname[MAX_TOKEN_CHARS];
		char  scratch[200];
		//char mapshotname [MAX_QPATH];
		int		j, l;

		strcpy( shortname, COM_Parse( &s ) );

		l = strlen(shortname);
		for (j=0 ; j<l ; j++)
			shortname[j] = toupper(shortname[j]);
		strcpy( longname, COM_Parse( &s ) );
		Com_sprintf( scratch, sizeof( scratch ), "%s\n%s", longname, shortname );

		mapnames[i] = malloc( strlen( scratch ) + 1 );
		strcpy( mapnames[i], scratch );
	}
	mapnames[nummaps] = 0;

	// register null levelshot
	if (svr_mapshotvalid[nummaps] == M_UNSET) {	
		if (R_DrawFindPic("/gfx/noscreen.pcx"))
			svr_mapshotvalid[nummaps] = M_FOUND;
		else
			svr_mapshotvalid[nummaps] = M_MISSING;
	}

	if ( fp != 0 )
	{
		fp = 0;
		free( buffer );
	}
	else
	{
		FS_FreeFile( buffer );
	}

	/*
	** initialize the menu stuff
	*/
	s_startserver_menu.x = viddef.width / 2 - scaledVideo(140);
	//s_startserver_menu.x = viddef.width * 0.50;
	s_startserver_menu.nitems = 0;

	s_startmap_list.generic.type = MTYPE_SPINCONTROL;
	s_startmap_list.generic.x	= 0;
	s_startmap_list.generic.y	= y; //0
	s_startmap_list.generic.name	= "initial map";
	s_startmap_list.itemnames = (const char **)mapnames;

	s_rules_box.generic.type = MTYPE_SPINCONTROL;
	s_rules_box.generic.x	= 0;
	s_rules_box.generic.y	= y += 2*(MENU_FONT_SIZE+2); //20
	s_rules_box.generic.name	= "rules";
//PGM - rogue games only available with rogue DLL.
	if (roguepath())
		s_rules_box.itemnames = dm_coop_names_rogue;
	else
		s_rules_box.itemnames = dm_coop_names;
//PGM
	if (roguepath()) {
		if (Cvar_VariableValue("ttctf"))
			s_rules_box.curvalue = 4;
		else if (Cvar_VariableValue("ctf"))
			s_rules_box.curvalue = 3;
		else if (Cvar_VariableValue("gamerules"))
			s_rules_box.curvalue = 2;
		else if (Cvar_VariableValue("coop"))
			s_rules_box.curvalue = 1;
		else
			s_rules_box.curvalue = 0;
	} else {
		if (Cvar_VariableValue("ttctf"))
			s_rules_box.curvalue = 3;
		else if (Cvar_VariableValue("ctf"))
			s_rules_box.curvalue = 2;
		else if (Cvar_VariableValue("coop"))
			s_rules_box.curvalue = 1;
		else
			s_rules_box.curvalue = 0;
	}
	s_rules_box.generic.callback = RulesChangeFunc;

	s_timelimit_field.generic.type = MTYPE_FIELD;
	s_timelimit_field.generic.name = "time limit";
	s_timelimit_field.generic.flags = QMF_NUMBERSONLY;
	s_timelimit_field.generic.x	= 0;
	s_timelimit_field.generic.y	= y += 2*MENU_FONT_SIZE; //36
	s_timelimit_field.generic.statusbar = "0 = no limit";
	s_timelimit_field.length = 4;
	s_timelimit_field.visible_length = 4;
	strcpy( s_timelimit_field.buffer, Cvar_VariableString("timelimit") );
	s_timelimit_field.cursor = strlen( s_timelimit_field.buffer );

	s_fraglimit_field.generic.type = MTYPE_FIELD;
	s_fraglimit_field.generic.name = "frag limit";
	s_fraglimit_field.generic.flags = QMF_NUMBERSONLY;
	s_fraglimit_field.generic.x	= 0;
	s_fraglimit_field.generic.y	= y += 2.25*MENU_FONT_SIZE; //54
	s_fraglimit_field.generic.statusbar = "0 = no limit";
	s_fraglimit_field.length = 4;
	s_fraglimit_field.visible_length = 4;
	strcpy( s_fraglimit_field.buffer, Cvar_VariableString("fraglimit") );
	s_fraglimit_field.cursor = strlen( s_fraglimit_field.buffer );

	/*
	** maxclients determines the maximum number of players that can join
	** the game.  If maxclients is only "1" then we should default the menu
	** option to 8 players, otherwise use whatever its current value is. 
	** Clamping will be done when the server is actually started.
	*/
	s_maxclients_field.generic.type = MTYPE_FIELD;
	s_maxclients_field.generic.name = "max players";
	s_maxclients_field.generic.flags = QMF_NUMBERSONLY;
	s_maxclients_field.generic.x	= 0;
	s_maxclients_field.generic.y	= y += 2.25*MENU_FONT_SIZE; //72
	s_maxclients_field.generic.statusbar = NULL;
	s_maxclients_field.length = 3;
	s_maxclients_field.visible_length = 3;
	if ( Cvar_VariableValue( "maxclients" ) == 1 )
		strcpy( s_maxclients_field.buffer, "8" );
	else 
		strcpy( s_maxclients_field.buffer, Cvar_VariableString("maxclients") );
	s_maxclients_field.cursor = strlen( s_maxclients_field.buffer );

	s_hostname_field.generic.type = MTYPE_FIELD;
	s_hostname_field.generic.name = "hostname";
	s_hostname_field.generic.flags = 0;
	s_hostname_field.generic.x	= 0;
	s_hostname_field.generic.y	= y += 2.25*MENU_FONT_SIZE; //90
	s_hostname_field.generic.statusbar = NULL;
	s_hostname_field.length = 12;
	s_hostname_field.visible_length = 12;
	strcpy( s_hostname_field.buffer, Cvar_VariableString("hostname") );
	s_hostname_field.cursor = strlen( s_hostname_field.buffer );

	s_startserver_dmoptions_action.generic.type = MTYPE_ACTION;
	s_startserver_dmoptions_action.generic.name	= " deathmatch flags";
	s_startserver_dmoptions_action.generic.flags= QMF_LEFT_JUSTIFY;
	s_startserver_dmoptions_action.generic.x	= 24;
	s_startserver_dmoptions_action.generic.y	= y += 2.25*MENU_FONT_SIZE; //108
	s_startserver_dmoptions_action.generic.statusbar = NULL;
	s_startserver_dmoptions_action.generic.callback = DMOptionsFunc;

	s_startserver_start_action.generic.type = MTYPE_ACTION;
	s_startserver_start_action.generic.name	= " begin";
	s_startserver_start_action.generic.flags= QMF_LEFT_JUSTIFY;
	s_startserver_start_action.generic.x	= 24;
	s_startserver_start_action.generic.y	= y += 2*(MENU_FONT_SIZE+2); //128
	s_startserver_start_action.generic.callback = StartServerActionFunc;

	s_startserver_back_action.generic.type = MTYPE_ACTION;
	s_startserver_back_action.generic.name	= " back to multiplayer";
	s_startserver_back_action.generic.flags= QMF_LEFT_JUSTIFY;
	s_startserver_back_action.generic.x	= 24;
	s_startserver_back_action.generic.y	= y += 3*(MENU_FONT_SIZE+2); //128
	s_startserver_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_startserver_menu, &s_startmap_list );
	Menu_AddItem( &s_startserver_menu, &s_rules_box );
	Menu_AddItem( &s_startserver_menu, &s_timelimit_field );
	Menu_AddItem( &s_startserver_menu, &s_fraglimit_field );
	Menu_AddItem( &s_startserver_menu, &s_maxclients_field );
	Menu_AddItem( &s_startserver_menu, &s_hostname_field );
	Menu_AddItem( &s_startserver_menu, &s_startserver_dmoptions_action );
	Menu_AddItem( &s_startserver_menu, &s_startserver_start_action );
	Menu_AddItem( &s_startserver_menu, &s_startserver_back_action );

	Menu_Center( &s_startserver_menu );

	// call this now to set proper inital state
	RulesChangeFunc ( NULL );
}

void DrawStartSeverLevelshot (void)
{	
	char startmap[MAX_QPATH];
	char mapshotname [MAX_QPATH];
	int i = s_startmap_list.curvalue;
	strcpy( startmap, strchr( mapnames[i], '\n' ) + 1 );

	R_DrawFill (
		viddef.width/2 + scaledVideo(44),
		viddef.height/2 - scaledVideo(60),
		scaledVideo(244), scaledVideo(184), 3);


	if ( svr_mapshotvalid[i] == M_UNSET) { // init levelshot
		Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", startmap);
		if (R_DrawFindPic(mapshotname))
			svr_mapshotvalid[i] = M_FOUND;
		else
			svr_mapshotvalid[i] = M_MISSING;
	}
	if ( svr_mapshotvalid[i] == M_FOUND) {
		Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", startmap);

		R_DrawStretchPic (
			viddef.width/2 + scaledVideo(46),
			viddef.height/2 - scaledVideo(58),
			scaledVideo(240), scaledVideo(180), mapshotname, 1.0);
	}
	else if (svr_mapshotvalid[nummaps] == M_FOUND)
		R_DrawStretchPic (
			viddef.width/2 + scaledVideo(46),
			viddef.height/2 - scaledVideo(58),
			scaledVideo(240), scaledVideo(180), "/gfx/noscreen.pcx", 1.0);
	else
		R_DrawFill (
			viddef.width/2 + scaledVideo(46),
			viddef.height/2 - scaledVideo(58),
			scaledVideo(240), scaledVideo(180), 0);
}

void StartServer_MenuDraw(void)
{
	M_Banner( "m_banner_start_server" ); // Knightmare added
	Menu_Draw( &s_startserver_menu );
	DrawStartSeverLevelshot (); // added levelshots
}

const char *StartServer_MenuKey( int key )
{
	if ( key == K_ESCAPE )
	{
		if ( mapnames )
		{
			int i;

			for ( i = 0; i < nummaps; i++ )
				free( mapnames[i] );
			free( mapnames );
		}
		mapnames = 0;
		nummaps = 0;
	}

	return Default_MenuKey( &s_startserver_menu, key );
}

void M_Menu_StartServer_f (void)
{
	StartServer_MenuInit();
	M_PushMenu( StartServer_MenuDraw, StartServer_MenuKey );
}

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

DMOPTIONS BOOK MENU

=============================================================================
*/
static char dmoptions_statusbar[128];

static menuframework_s s_dmoptions_menu;

static menulist_s	s_friendlyfire_box;
static menulist_s	s_falls_box;
static menulist_s	s_weapons_stay_box;
static menulist_s	s_instant_powerups_box;
static menulist_s	s_powerups_box;
static menulist_s	s_health_box;
static menulist_s	s_spawn_farthest_box;
static menulist_s	s_teamplay_box;
static menulist_s	s_samelevel_box;
static menulist_s	s_force_respawn_box;
static menulist_s	s_armor_box;
static menulist_s	s_allow_exit_box;
static menulist_s	s_infinite_ammo_box;
static menulist_s	s_fixed_fov_box;
static menulist_s	s_quad_drop_box;

//Xatrix
static menulist_s	s_quadfire_drop_box;

//ROGUE
static menulist_s	s_no_mines_box;
static menulist_s	s_no_nukes_box;
static menulist_s	s_stack_double_box;
static menulist_s	s_no_spheres_box;

// CTF
static menulist_s	s_ctf_forceteam_box;
static menulist_s	s_ctf_armor_protect_box;
static menulist_s	s_ctf_notechs_box;

static menuaction_s	s_dmoptions_back_action;


#define DF_CTF_FORCEJOIN	131072	
#define DF_ARMOR_PROTECT	262144
#define DF_CTF_NO_TECH      524288

qboolean CTF_menumode (void)
{
	if ( (roguepath() && s_rules_box.curvalue >= 3)
		|| (!roguepath() && s_rules_box.curvalue >= 2) )
		return true;
	return false;
}

static void DMFlagCallback( void *self )
{
	menulist_s *f = ( menulist_s * ) self;
	int flags;
	int bit = 0;

	flags = Cvar_VariableValue( "dmflags" );

	if ( f == &s_friendlyfire_box )
	{
		if ( f->curvalue )
			flags &= ~DF_NO_FRIENDLY_FIRE;
		else
			flags |= DF_NO_FRIENDLY_FIRE;
		goto setvalue;
	}
	else if ( f == &s_falls_box )
	{
		if ( f->curvalue )
			flags &= ~DF_NO_FALLING;
		else
			flags |= DF_NO_FALLING;
		goto setvalue;
	}
	else if ( f == &s_weapons_stay_box ) 
	{
		bit = DF_WEAPONS_STAY;
	}
	else if ( f == &s_instant_powerups_box )
	{
		bit = DF_INSTANT_ITEMS;
	}
	else if ( f == &s_allow_exit_box )
	{
		bit = DF_ALLOW_EXIT;
	}
	else if ( f == &s_powerups_box )
	{
		if ( f->curvalue )
			flags &= ~DF_NO_ITEMS;
		else
			flags |= DF_NO_ITEMS;
		goto setvalue;
	}
	else if ( f == &s_health_box )
	{
		if ( f->curvalue )
			flags &= ~DF_NO_HEALTH;
		else
			flags |= DF_NO_HEALTH;
		goto setvalue;
	}
	else if ( f == &s_spawn_farthest_box )
	{
		bit = DF_SPAWN_FARTHEST;
	}
	else if ( f == &s_teamplay_box )
	{
		if ( f->curvalue == 1 )
		{
			flags |=  DF_SKINTEAMS;
			flags &= ~DF_MODELTEAMS;
		}
		else if ( f->curvalue == 2 )
		{
			flags |=  DF_MODELTEAMS;
			flags &= ~DF_SKINTEAMS;
		}
		else
		{
			flags &= ~( DF_MODELTEAMS | DF_SKINTEAMS );
		}

		goto setvalue;
	}
	else if ( f == &s_samelevel_box )
	{
		bit = DF_SAME_LEVEL;
	}
	else if ( f == &s_force_respawn_box )
	{
		bit = DF_FORCE_RESPAWN;
	}
	else if ( f == &s_armor_box )
	{
		if ( f->curvalue )
			flags &= ~DF_NO_ARMOR;
		else
			flags |= DF_NO_ARMOR;
		goto setvalue;
	}
	else if ( f == &s_infinite_ammo_box )
	{
		bit = DF_INFINITE_AMMO;
	}
	else if ( f == &s_fixed_fov_box )
	{
		bit = DF_FIXED_FOV;
	}
	else if ( f == &s_quad_drop_box )
	{
		bit = DF_QUAD_DROP;
	}
	// Knightmare added
	else if (modType("xatrix"))
	{
		if ( f == &s_quadfire_drop_box)
		{
			bit = DF_QUADFIRE_DROP;
		}
	}
//=======
//ROGUE
	else if (roguepath())
	{
		if ( f == &s_no_mines_box)
		{
			bit = DF_NO_MINES;
		}
		else if ( f == &s_no_nukes_box)
		{
			bit = DF_NO_NUKES;
		}
		else if ( f == &s_stack_double_box)
		{
			bit = DF_NO_STACK_DOUBLE;
		}
		else if ( f == &s_no_spheres_box)
		{
			bit = DF_NO_SPHERES;
		}
	}
//ROGUE
//=======
	// Knightmare added-  CTF flags
	else if ( CTF_menumode() )
	{
		if ( f == &s_ctf_forceteam_box)
		{
			bit = DF_CTF_FORCEJOIN;
		}
		else if ( f == &s_ctf_armor_protect_box)
		{
			bit = DF_ARMOR_PROTECT;
		}
		else if ( f == &s_ctf_notechs_box)
		{
			bit = DF_CTF_NO_TECH;
		}
	}

	if ( f )
	{
		if ( f->curvalue == 0 )
			flags &= ~bit;
		else
			flags |= bit;
	}

setvalue:
	Cvar_SetValue ("dmflags", flags);

	Com_sprintf( dmoptions_statusbar, sizeof( dmoptions_statusbar ), "dmflags = %d", flags );

}

void DMOptions_MenuInit( void )
{
	static const char *yes_no_names[] =
	{
		"no", "yes", 0
	};
	static const char *teamplay_names[] = 
	{
		"disabled", "by skin", "by model", 0
	};
	int dmflags = Cvar_VariableValue( "dmflags" );
	int y = 0;

	s_dmoptions_menu.x = viddef.width * 0.50;
	s_dmoptions_menu.nitems = 0;

	s_falls_box.generic.type = MTYPE_SPINCONTROL;
	s_falls_box.generic.x	= 0;
	s_falls_box.generic.y	= y; // 0
	s_falls_box.generic.name	= "falling damage";
	s_falls_box.generic.callback = DMFlagCallback;
	s_falls_box.itemnames = yes_no_names;
	s_falls_box.curvalue = ( dmflags & DF_NO_FALLING ) == 0;

	s_weapons_stay_box.generic.type = MTYPE_SPINCONTROL;
	s_weapons_stay_box.generic.x	= 0;
	s_weapons_stay_box.generic.y	= y += (MENU_FONT_SIZE+2); //10
	s_weapons_stay_box.generic.name	= "weapons stay";
	s_weapons_stay_box.generic.callback = DMFlagCallback;
	s_weapons_stay_box.itemnames = yes_no_names;
	s_weapons_stay_box.curvalue = ( dmflags & DF_WEAPONS_STAY ) != 0;

	s_instant_powerups_box.generic.type = MTYPE_SPINCONTROL;
	s_instant_powerups_box.generic.x	= 0;
	s_instant_powerups_box.generic.y	= y += (MENU_FONT_SIZE+2); //20
	s_instant_powerups_box.generic.name	= "instant powerups";
	s_instant_powerups_box.generic.callback = DMFlagCallback;
	s_instant_powerups_box.itemnames = yes_no_names;
	s_instant_powerups_box.curvalue = ( dmflags & DF_INSTANT_ITEMS ) != 0;

	s_powerups_box.generic.type = MTYPE_SPINCONTROL;
	s_powerups_box.generic.x	= 0;
	s_powerups_box.generic.y	= y += (MENU_FONT_SIZE+2); //30
	s_powerups_box.generic.name	= "allow powerups";
	s_powerups_box.generic.callback = DMFlagCallback;
	s_powerups_box.itemnames = yes_no_names;
	s_powerups_box.curvalue = ( dmflags & DF_NO_ITEMS ) == 0;

	s_health_box.generic.type = MTYPE_SPINCONTROL;
	s_health_box.generic.x	= 0;
	s_health_box.generic.y	= y += (MENU_FONT_SIZE+2); //40
	s_health_box.generic.callback = DMFlagCallback;
	s_health_box.generic.name	= "allow health";
	s_health_box.itemnames = yes_no_names;
	s_health_box.curvalue = ( dmflags & DF_NO_HEALTH ) == 0;

	s_armor_box.generic.type = MTYPE_SPINCONTROL;
	s_armor_box.generic.x	= 0;
	s_armor_box.generic.y	= y += (MENU_FONT_SIZE+2); //50
	s_armor_box.generic.name	= "allow armor";
	s_armor_box.generic.callback = DMFlagCallback;
	s_armor_box.itemnames = yes_no_names;
	s_armor_box.curvalue = ( dmflags & DF_NO_ARMOR ) == 0;

	s_spawn_farthest_box.generic.type = MTYPE_SPINCONTROL;
	s_spawn_farthest_box.generic.x	= 0;
	s_spawn_farthest_box.generic.y	= y += (MENU_FONT_SIZE+2); //60
	s_spawn_farthest_box.generic.name	= "spawn farthest";
	s_spawn_farthest_box.generic.callback = DMFlagCallback;
	s_spawn_farthest_box.itemnames = yes_no_names;
	s_spawn_farthest_box.curvalue = ( dmflags & DF_SPAWN_FARTHEST ) != 0;

	s_samelevel_box.generic.type = MTYPE_SPINCONTROL;
	s_samelevel_box.generic.x	= 0;
	s_samelevel_box.generic.y	= y += (MENU_FONT_SIZE+2); //70
	s_samelevel_box.generic.name	= "same map";
	s_samelevel_box.generic.callback = DMFlagCallback;
	s_samelevel_box.itemnames = yes_no_names;
	s_samelevel_box.curvalue = ( dmflags & DF_SAME_LEVEL ) != 0;

	s_force_respawn_box.generic.type = MTYPE_SPINCONTROL;
	s_force_respawn_box.generic.x	= 0;
	s_force_respawn_box.generic.y	= y += (MENU_FONT_SIZE+2); //80
	s_force_respawn_box.generic.name	= "force respawn";
	s_force_respawn_box.generic.callback = DMFlagCallback;
	s_force_respawn_box.itemnames = yes_no_names;
	s_force_respawn_box.curvalue = ( dmflags & DF_FORCE_RESPAWN ) != 0;

	s_teamplay_box.generic.type = MTYPE_SPINCONTROL;
	s_teamplay_box.generic.x	= 0;
	s_teamplay_box.generic.y	= y += (MENU_FONT_SIZE+2); //90
	s_teamplay_box.generic.name	= "teamplay";
	s_teamplay_box.generic.callback = DMFlagCallback;
	s_teamplay_box.itemnames = teamplay_names;

	s_allow_exit_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_exit_box.generic.x	= 0;
	s_allow_exit_box.generic.y	= y += (MENU_FONT_SIZE+2); //100
	s_allow_exit_box.generic.name	= "allow exit";
	s_allow_exit_box.generic.callback = DMFlagCallback;
	s_allow_exit_box.itemnames = yes_no_names;
	s_allow_exit_box.curvalue = ( dmflags & DF_ALLOW_EXIT ) != 0;

	s_infinite_ammo_box.generic.type = MTYPE_SPINCONTROL;
	s_infinite_ammo_box.generic.x	= 0;
	s_infinite_ammo_box.generic.y	= y += (MENU_FONT_SIZE+2); //110
	s_infinite_ammo_box.generic.name	= "infinite ammo";
	s_infinite_ammo_box.generic.callback = DMFlagCallback;
	s_infinite_ammo_box.itemnames = yes_no_names;
	s_infinite_ammo_box.curvalue = ( dmflags & DF_INFINITE_AMMO ) != 0;

	s_fixed_fov_box.generic.type = MTYPE_SPINCONTROL;
	s_fixed_fov_box.generic.x	= 0;
	s_fixed_fov_box.generic.y	= y += (MENU_FONT_SIZE+2); //120
	s_fixed_fov_box.generic.name	= "fixed FOV";
	s_fixed_fov_box.generic.callback = DMFlagCallback;
	s_fixed_fov_box.itemnames = yes_no_names;
	s_fixed_fov_box.curvalue = ( dmflags & DF_FIXED_FOV ) != 0;

	s_quad_drop_box.generic.type = MTYPE_SPINCONTROL;
	s_quad_drop_box.generic.x	= 0;
	s_quad_drop_box.generic.y	= y += (MENU_FONT_SIZE+2); //130
	s_quad_drop_box.generic.name	= "quad drop";
	s_quad_drop_box.generic.callback = DMFlagCallback;
	s_quad_drop_box.itemnames = yes_no_names;
	s_quad_drop_box.curvalue = ( dmflags & DF_QUAD_DROP ) != 0;

	s_friendlyfire_box.generic.type = MTYPE_SPINCONTROL;
	s_friendlyfire_box.generic.x	= 0;
	s_friendlyfire_box.generic.y	= y += (MENU_FONT_SIZE+2); //140
	s_friendlyfire_box.generic.name	= "friendly fire";
	s_friendlyfire_box.generic.callback = DMFlagCallback;
	s_friendlyfire_box.itemnames = yes_no_names;
	s_friendlyfire_box.curvalue = ( dmflags & DF_NO_FRIENDLY_FIRE ) == 0;

	// Knightmare added
	if (modType("xatrix"))
	{
		s_quadfire_drop_box.generic.type = MTYPE_SPINCONTROL;
		s_quadfire_drop_box.generic.x	= 0;
		s_quadfire_drop_box.generic.y	= y += (MENU_FONT_SIZE+2); //150
		s_quadfire_drop_box.generic.name	= "dualfire drop";
		s_quadfire_drop_box.generic.callback = DMFlagCallback;
		s_quadfire_drop_box.itemnames = yes_no_names;
		s_quadfire_drop_box.curvalue = ( dmflags & DF_QUADFIRE_DROP ) != 0;
	}
//============
//ROGUE
	//Knightmare 12/23/2001
	else if (roguepath())
	{
		s_no_mines_box.generic.type = MTYPE_SPINCONTROL;
		s_no_mines_box.generic.x	= 0;
		s_no_mines_box.generic.y	= y += (MENU_FONT_SIZE+2); //150
		s_no_mines_box.generic.name	= "remove mines";
		s_no_mines_box.generic.callback = DMFlagCallback;
		s_no_mines_box.itemnames = yes_no_names;
		s_no_mines_box.curvalue = ( dmflags & DF_NO_MINES ) != 0;

		s_no_nukes_box.generic.type = MTYPE_SPINCONTROL;
		s_no_nukes_box.generic.x	= 0;
		s_no_nukes_box.generic.y	= y += (MENU_FONT_SIZE+2); //160
		s_no_nukes_box.generic.name	= "remove nukes";
		s_no_nukes_box.generic.callback = DMFlagCallback;
		s_no_nukes_box.itemnames = yes_no_names;
		s_no_nukes_box.curvalue = ( dmflags & DF_NO_NUKES ) != 0;

		s_stack_double_box.generic.type = MTYPE_SPINCONTROL;
		s_stack_double_box.generic.x	= 0;
		s_stack_double_box.generic.y	= y += (MENU_FONT_SIZE+2); //170
		s_stack_double_box.generic.name	= "2x/4x stacking off";
		s_stack_double_box.generic.callback = DMFlagCallback;
		s_stack_double_box.itemnames = yes_no_names;
		s_stack_double_box.curvalue = ( dmflags & DF_NO_STACK_DOUBLE ) != 0;

		s_no_spheres_box.generic.type = MTYPE_SPINCONTROL;
		s_no_spheres_box.generic.x	= 0;
		s_no_spheres_box.generic.y	= y += (MENU_FONT_SIZE+2); //180
		s_no_spheres_box.generic.name	= "remove spheres";
		s_no_spheres_box.generic.callback = DMFlagCallback;
		s_no_spheres_box.itemnames = yes_no_names;
		s_no_spheres_box.curvalue = ( dmflags & DF_NO_SPHERES ) != 0;

	}
//ROGUE
//============
	// Knightmare added
	else if (CTF_menumode())
	{
		s_ctf_forceteam_box.generic.type = MTYPE_SPINCONTROL;
		s_ctf_forceteam_box.generic.x	= 0;
		s_ctf_forceteam_box.generic.y	= y += (MENU_FONT_SIZE+2); //150
		s_ctf_forceteam_box.generic.name	= "force team join";
		s_ctf_forceteam_box.generic.callback = DMFlagCallback;
		s_ctf_forceteam_box.itemnames = yes_no_names;
		s_ctf_forceteam_box.curvalue = ( dmflags & DF_CTF_FORCEJOIN ) != 0;

		s_ctf_armor_protect_box.generic.type = MTYPE_SPINCONTROL;
		s_ctf_armor_protect_box.generic.x	= 0;
		s_ctf_armor_protect_box.generic.y	= y += (MENU_FONT_SIZE+2); //160
		s_ctf_armor_protect_box.generic.name	= "team armor protect";
		s_ctf_armor_protect_box.generic.callback = DMFlagCallback;
		s_ctf_armor_protect_box.itemnames = yes_no_names;
		s_ctf_armor_protect_box.curvalue = ( dmflags & DF_ARMOR_PROTECT ) != 0;

		s_ctf_notechs_box.generic.type = MTYPE_SPINCONTROL;
		s_ctf_notechs_box.generic.x	= 0;
		s_ctf_notechs_box.generic.y	= y += (MENU_FONT_SIZE+2); //170
		s_ctf_notechs_box.generic.name	= "disable techs";
		s_ctf_notechs_box.generic.callback = DMFlagCallback;
		s_ctf_notechs_box.itemnames = yes_no_names;
		s_ctf_notechs_box.curvalue = ( dmflags & DF_CTF_NO_TECH ) != 0;
	}

	s_dmoptions_back_action.generic.type = MTYPE_ACTION;
	s_dmoptions_back_action.generic.flags = QMF_LEFT_JUSTIFY;
	s_dmoptions_back_action.generic.x	= 0;
	s_dmoptions_back_action.generic.y	= y += 3*(MENU_FONT_SIZE+2); //200
	s_dmoptions_back_action.generic.name	= " back";
	s_dmoptions_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_dmoptions_menu, &s_falls_box );
	Menu_AddItem( &s_dmoptions_menu, &s_weapons_stay_box );
	Menu_AddItem( &s_dmoptions_menu, &s_instant_powerups_box );
	Menu_AddItem( &s_dmoptions_menu, &s_powerups_box );
	Menu_AddItem( &s_dmoptions_menu, &s_health_box );
	Menu_AddItem( &s_dmoptions_menu, &s_armor_box );
	Menu_AddItem( &s_dmoptions_menu, &s_spawn_farthest_box );
	Menu_AddItem( &s_dmoptions_menu, &s_samelevel_box );
	Menu_AddItem( &s_dmoptions_menu, &s_force_respawn_box );
	Menu_AddItem( &s_dmoptions_menu, &s_teamplay_box );
	Menu_AddItem( &s_dmoptions_menu, &s_allow_exit_box );
	Menu_AddItem( &s_dmoptions_menu, &s_infinite_ammo_box );
	Menu_AddItem( &s_dmoptions_menu, &s_fixed_fov_box );
	Menu_AddItem( &s_dmoptions_menu, &s_quad_drop_box );
	Menu_AddItem( &s_dmoptions_menu, &s_friendlyfire_box );
	// Xatrix
	if (modType("xatrix"))
		Menu_AddItem( &s_dmoptions_menu, &s_quadfire_drop_box );
//=======
//ROGUE
	else if (roguepath())
	{
		Menu_AddItem( &s_dmoptions_menu, &s_no_mines_box );
		Menu_AddItem( &s_dmoptions_menu, &s_no_nukes_box );
		Menu_AddItem( &s_dmoptions_menu, &s_stack_double_box );
		Menu_AddItem( &s_dmoptions_menu, &s_no_spheres_box );
	}
//ROGUE
//=======
	// CTF
	else if (CTF_menumode())
	{
		Menu_AddItem( &s_dmoptions_menu, &s_ctf_forceteam_box );
		Menu_AddItem( &s_dmoptions_menu, &s_ctf_armor_protect_box );
		Menu_AddItem( &s_dmoptions_menu, &s_ctf_notechs_box );
	}
	Menu_AddItem( &s_dmoptions_menu, &s_dmoptions_back_action );

	Menu_Center( &s_dmoptions_menu );

	// set the original dmflags statusbar
	DMFlagCallback( 0 );
	Menu_SetStatusBar( &s_dmoptions_menu, dmoptions_statusbar );
}

void DMOptions_MenuDraw(void)
{
	M_Banner( "m_banner_start_server" ); // Knightmare added
	Menu_Draw( &s_dmoptions_menu );
}

const char *DMOptions_MenuKey( int key )
{
	return Default_MenuKey( &s_dmoptions_menu, key );
}

void M_Menu_DMOptions_f (void)
{
	DMOptions_MenuInit();
	M_PushMenu( DMOptions_MenuDraw, DMOptions_MenuKey );
}

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

DOWNLOAD OPTIONS BOOK MENU

=============================================================================
*/
static menuframework_s s_downloadoptions_menu;

static menuseparator_s	s_download_title;
static menulist_s	s_allow_download_box;
static menulist_s	s_allow_download_maps_box;
// Knightmare- option to allow downloading 24-bit textures
static menulist_s	s_allow_download_enh_textures_box;

static menulist_s	s_allow_download_models_box;
static menulist_s	s_allow_download_players_box;
static menulist_s	s_allow_download_sounds_box;

static menuaction_s	s_download_back_action;

static void DownloadCallback( void *self )
{
	menulist_s *f = ( menulist_s * ) self;

	if (f == &s_allow_download_box)
	{
		Cvar_SetValue("allow_download", f->curvalue);
	}

	else if (f == &s_allow_download_maps_box)
	{
		Cvar_SetValue("allow_download_maps", f->curvalue);
	}

	// Knightmare- option to allow downloading 24-bit textures
	else if (f == &s_allow_download_enh_textures_box)
	{
		Cvar_SetValue("allow_download_enh_textures", f->curvalue);
	}

	else if (f == &s_allow_download_models_box)
	{
		Cvar_SetValue("allow_download_models", f->curvalue);
	}

	else if (f == &s_allow_download_players_box)
	{
		Cvar_SetValue("allow_download_players", f->curvalue);
	}

	else if (f == &s_allow_download_sounds_box)
	{
		Cvar_SetValue("allow_download_sounds", f->curvalue);
	}
}

void DownloadOptions_MenuInit( void )
{
	static const char *yes_no_names[] =
	{
		"no", "yes", 0
	};
	int y = 0;

	s_downloadoptions_menu.x = viddef.width * 0.50;
	s_downloadoptions_menu.nitems = 0;

	s_download_title.generic.type = MTYPE_SEPARATOR;
	s_download_title.generic.name = "Download Options";
	s_download_title.generic.x    = 48;
	s_download_title.generic.y	 = y; //0

	s_allow_download_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_box.generic.x	= 0;
	s_allow_download_box.generic.y	= y += 2*(MENU_FONT_SIZE+2); //20
	s_allow_download_box.generic.name	= "allow downloading";
	s_allow_download_box.generic.callback = DownloadCallback;
	s_allow_download_box.itemnames = yes_no_names;
	s_allow_download_box.curvalue = (Cvar_VariableValue("allow_download") != 0);

	s_allow_download_maps_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_maps_box.generic.x	= 0;
	s_allow_download_maps_box.generic.y	= y += 2*(MENU_FONT_SIZE+2); //40
	s_allow_download_maps_box.generic.name	= "maps/textures";
	s_allow_download_maps_box.generic.callback = DownloadCallback;
	s_allow_download_maps_box.itemnames = yes_no_names;
	s_allow_download_maps_box.curvalue = (Cvar_VariableValue("allow_download_maps") != 0);

	// Knightmare- option to allow downloading 24-bit textures
	s_allow_download_enh_textures_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_enh_textures_box.generic.x	= 0;
	s_allow_download_enh_textures_box.generic.y	= y += (MENU_FONT_SIZE+2); //50
	s_allow_download_enh_textures_box.generic.name	= "24-bit textures";
	s_allow_download_enh_textures_box.generic.callback = DownloadCallback;
	s_allow_download_enh_textures_box.generic.statusbar = "enable to allow downloading of JPG and TGA textures";
	s_allow_download_enh_textures_box.itemnames = yes_no_names;
	s_allow_download_enh_textures_box.curvalue = (Cvar_VariableValue("allow_download_enh_textures") != 0);

	s_allow_download_players_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_players_box.generic.x	= 0;
	s_allow_download_players_box.generic.y	= y += (MENU_FONT_SIZE+2); //60
	s_allow_download_players_box.generic.name	= "player models/skins";
	s_allow_download_players_box.generic.callback = DownloadCallback;
	s_allow_download_players_box.itemnames = yes_no_names;
	s_allow_download_players_box.curvalue = (Cvar_VariableValue("allow_download_players") != 0);

	s_allow_download_models_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_models_box.generic.x	= 0;
	s_allow_download_models_box.generic.y	= y += (MENU_FONT_SIZE+2); //70
	s_allow_download_models_box.generic.name	= "models";
	s_allow_download_models_box.generic.callback = DownloadCallback;
	s_allow_download_models_box.itemnames = yes_no_names;
	s_allow_download_models_box.curvalue = (Cvar_VariableValue("allow_download_models") != 0);

	s_allow_download_sounds_box.generic.type = MTYPE_SPINCONTROL;
	s_allow_download_sounds_box.generic.x	= 0;
	s_allow_download_sounds_box.generic.y	= y += (MENU_FONT_SIZE+2); //80
	s_allow_download_sounds_box.generic.name	= "sounds";
	s_allow_download_sounds_box.generic.callback = DownloadCallback;
	s_allow_download_sounds_box.itemnames = yes_no_names;
	s_allow_download_sounds_box.curvalue = (Cvar_VariableValue("allow_download_sounds") != 0);

	s_download_back_action.generic.type = MTYPE_ACTION;
	s_download_back_action.generic.flags = QMF_LEFT_JUSTIFY;
	s_download_back_action.generic.x	= 0;
	s_download_back_action.generic.y	= y += 3*(MENU_FONT_SIZE+2); //110
	s_download_back_action.generic.name	= " back";
	s_download_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_downloadoptions_menu, &s_download_title );
	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_box );
	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_maps_box );

	// Knightmare- option to allow downloading 24-bit textures
	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_enh_textures_box );

	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_players_box );
	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_models_box );
	Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_sounds_box );

	Menu_AddItem( &s_downloadoptions_menu, &s_download_back_action );

	Menu_Center( &s_downloadoptions_menu );

	// skip over title
	if (s_downloadoptions_menu.cursor == 0)
		s_downloadoptions_menu.cursor = 1;
}

void DownloadOptions_MenuDraw(void)
{
	Menu_Draw( &s_downloadoptions_menu );
}

const char *DownloadOptions_MenuKey( int key )
{
	return Default_MenuKey( &s_downloadoptions_menu, key );
}

void M_Menu_DownloadOptions_f (void)
{
	DownloadOptions_MenuInit();
	M_PushMenu( DownloadOptions_MenuDraw, DownloadOptions_MenuKey );
}
/*
=============================================================================

ADDRESS BOOK MENU

=============================================================================
*/
#define NUM_ADDRESSBOOK_ENTRIES 9

static menuframework_s	s_addressbook_menu;
static menufield_s		s_addressbook_fields[NUM_ADDRESSBOOK_ENTRIES];
static menuaction_s		s_addressbook_back_action;


void AddressBook_MenuInit( void )
{
	int i;

	s_addressbook_menu.x = viddef.width / 2 - scaledVideo(MENU_FONT_SIZE*17.75);	// was 142
	s_addressbook_menu.y = viddef.height / 2 - scaledVideo(MENU_FONT_SIZE*7.25);	// was 58
	s_addressbook_menu.nitems = 0;

	for ( i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++ )
	{
		cvar_t *adr;
		char buffer[20];

		Com_sprintf( buffer, sizeof( buffer ), "adr%d", i );

		adr = Cvar_Get( buffer, "", CVAR_ARCHIVE );

		s_addressbook_fields[i].generic.type = MTYPE_FIELD;
		s_addressbook_fields[i].generic.name = 0;
		s_addressbook_fields[i].generic.callback = 0;
		s_addressbook_fields[i].generic.x		= 0;
		s_addressbook_fields[i].generic.y		= i * 2.25*(MENU_FONT_SIZE+2) + 0; //18
		s_addressbook_fields[i].generic.localdata[0] = i;
		s_addressbook_fields[i].cursor			= 0;
		s_addressbook_fields[i].length			= 60;
		s_addressbook_fields[i].visible_length	= 30;

		strcpy( s_addressbook_fields[i].buffer, adr->string );

		Menu_AddItem( &s_addressbook_menu, &s_addressbook_fields[i] );
	}

	s_addressbook_back_action.generic.type = MTYPE_ACTION;
	s_addressbook_back_action.generic.flags = QMF_LEFT_JUSTIFY;
	s_addressbook_back_action.generic.x	= 24;
	s_addressbook_back_action.generic.y	= (NUM_ADDRESSBOOK_ENTRIES*2.25+0.5)*(MENU_FONT_SIZE+2);
	s_addressbook_back_action.generic.name	= " back";
	s_addressbook_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_addressbook_menu, &s_addressbook_back_action );
}

const char *AddressBook_MenuKey( int key )
{
	if ( key == K_ESCAPE )
	{
		int index;
		char buffer[20];

		for ( index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++ )
		{
			Com_sprintf( buffer, sizeof( buffer ), "adr%d", index );
			Cvar_Set( buffer, s_addressbook_fields[index].buffer );
		}
	}
	return Default_MenuKey( &s_addressbook_menu, key );
}

void AddressBook_MenuDraw(void)
{
	M_Banner( "m_banner_addressbook" );
	Menu_Draw( &s_addressbook_menu );
}

void M_Menu_AddressBook_f(void)
{
	AddressBook_MenuInit();
	M_PushMenu( AddressBook_MenuDraw, AddressBook_MenuKey );
}

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

PLAYER CONFIG MENU

=============================================================================
*/
static menuframework_s	s_player_config_menu;
static menufield_s		s_player_name_field;
static menulist_s		s_player_model_box;
static menulist_s		s_player_skin_box;
static menulist_s		s_player_handedness_box;
static menulist_s		s_player_rate_box;
static menuseparator_s	s_player_skin_title;
static menuseparator_s	s_player_model_title;
static menuseparator_s	s_player_hand_title;
static menuseparator_s	s_player_rate_title;
//static menuaction_s		s_player_download_action;
static menuaction_s		s_player_back_action;

// Knightmare- save skins and models here so as to not have to re-register every frame
struct model_s *playermodel;
struct model_s *weaponmodel;
struct image_s *playerskin;
char *currentweaponmodel;
// end Knightmare

#define MAX_DISPLAYNAME 16
#define MAX_PLAYERMODELS 1024

typedef struct
{
	int		nskins;
	char	**skindisplaynames;
	char	displayname[MAX_DISPLAYNAME];
	char	directory[MAX_QPATH];
} playermodelinfo_s;

static playermodelinfo_s s_pmi[MAX_PLAYERMODELS];
static char *s_pmnames[MAX_PLAYERMODELS];
static int s_numplayermodels;

static int rate_tbl[] = { 2500, 3200, 5000, 10000, 25000, 0 };
static const char *rate_names[] = { "28.8 Modem", "33.6 Modem", "Single ISDN",
	"Dual ISDN/Cable", "T1/LAN", "User defined", 0 };

void DownloadOptionsFunc( void *self )
{
	M_Menu_DownloadOptions_f();
}

static void HandednessCallback( void *unused )
{
	Cvar_SetValue( "hand", s_player_handedness_box.curvalue );
}

static void RateCallback( void *unused )
{
	if (s_player_rate_box.curvalue != sizeof(rate_tbl) / sizeof(*rate_tbl) - 1)
		Cvar_SetValue( "rate", rate_tbl[s_player_rate_box.curvalue] );
}

static void ModelCallback( void *unused )
{
	char scratch[MAX_QPATH];

	s_player_skin_box.itemnames = (const char **)s_pmi[s_player_model_box.curvalue].skindisplaynames;
	s_player_skin_box.curvalue = 0;

	// Knightmare- only register model and skin on starup or when changed
	Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory );
	playermodel = R_RegisterModel( scratch );

	Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
	playerskin = R_RegisterSkin( scratch );

	// show current weapon model (if any)
	if (currentweaponmodel && strlen(currentweaponmodel)) {
		Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s", s_pmi[s_player_model_box.curvalue].directory, currentweaponmodel );
		weaponmodel = R_RegisterModel( scratch );
		if (!weaponmodel) {
			Com_sprintf( scratch, sizeof( scratch ), "players/%s/weapon.md2", s_pmi[s_player_model_box.curvalue].directory );
			weaponmodel = R_RegisterModel( scratch );
		}
	}
	else {
		Com_sprintf( scratch, sizeof( scratch ), "players/%s/weapon.md2", s_pmi[s_player_model_box.curvalue].directory );
		weaponmodel = R_RegisterModel( scratch );
	}
	// end Knightmare
}

// Knightmare- only register skin on starup and when changed
static void SkinCallback( void *unused )
{
	char scratch[MAX_QPATH];

	Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
	playerskin = R_RegisterSkin( scratch );
}


// Knightmare- Psychospaz's menu support for TGA and JPG skins
//static qboolean IconOfSkinExists( char *skin, char **pcxfiles, int npcxfiles )
static qboolean IconOfSkinExists( char *skin, char **files, int nfiles, char *suffix )
{
	int i;
	char scratch[1024];

	strcpy( scratch, skin );
	*strrchr( scratch, '.' ) = 0;
	//strcat( scratch, "_i.pcx" );
	strcat( scratch, suffix );

	for ( i = 0; i < nfiles; i++ )
	{
		if ( strcmp( files[i], scratch ) == 0 )
			return true;
	}

	return false;
}


// Knightmare- added Psychospaz's menus support for JPG and TGA skins
static qboolean PlayerConfig_ScanDirectories (void)
{
	char findname[1024];
	char scratch[1024];
	int ndirs = 0, npms = 0;
	char **dirnames;
	char *path = NULL;
	int i;

	extern char **FS_ListFiles( char *, int *, unsigned, unsigned );

	s_numplayermodels = 0;

	// Knightmare- loop back to here if there were no valid player models found in the selected path
	do
	{
		/*
		** get a list of directories
		*/
		do 
		{
			path = FS_NextPath( path );
			Com_sprintf( findname, sizeof(findname), "%s/players/*.*", path );

			if ( ( dirnames = FS_ListFiles( findname, &ndirs, SFF_SUBDIR, 0 ) ) != 0)
				break;
		} while ( path );

		if ( !dirnames )
			return false;

		/*
		** go through the subdirectories
		*/
		npms = ndirs;
		if ( npms > MAX_PLAYERMODELS )
			npms = MAX_PLAYERMODELS;

		for ( i = 0; i < npms; i++ )
		{
			int k, s;
			char	*a, *b, *c;
			char	**skinnames;
			char	**imagenames;
			int		nimagefiles;
			int		nskins = 0;

			if ( dirnames[i] == 0 )
				continue;

			// verify the existence of tris.md2
			strcpy( scratch, dirnames[i] );
			strcat( scratch, "/tris.md2" );
			if ( !Sys_FindFirst( scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM ) )
			{
				free( dirnames[i] );
				dirnames[i] = 0;
				Sys_FindClose();
				continue;
			}
			Sys_FindClose();

			// verify the existence of at least one skin
			//strcpy( scratch, dirnames[i] );
			//strcat( scratch, "/*.pcx" );
			strcpy( scratch, va("%s%s", dirnames[i], "/*.*" ));

			imagenames = FS_ListFiles( scratch, &nimagefiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM );

			if ( !imagenames )
			{
				free( dirnames[i] );
				dirnames[i] = 0;
				continue;
			}

			// count valid skins, which consist of a skin with a matching "_i" icon
			/*for ( k = 0; k < nimagefiles-1; k++ )
			{
				if ( !strstr( imagenames[k], "_i.pcx" ) )
				{
					if ( IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 ) )
					{
						nskins++;
					}
				}
			}*/
			for ( k = 0; k < nimagefiles-1; k++ )
				if (	!strstr( imagenames[k], "_i.pcx")
					||  !strstr( imagenames[k], "_i.jpg" )
					||  !strstr( imagenames[k], "_i.tga") )
					if (	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.pcx")
						||	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.jpg")
						||	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.tga"))
						nskins++;

			if ( !nskins )
				continue;

			skinnames = malloc( sizeof( char * ) * ( nskins + 1 ) );
			memset( skinnames, 0, sizeof( char * ) * ( nskins + 1 ) );

			// copy the valid skins
			if (nimagefiles)
				for ( s = 0, k = 0; k < nimagefiles-1; k++ )
				{
					char *a, *b, *c;

				//	if ( !strstr( imagenames[k], "_i.pcx" ) )
					if (	!strstr( imagenames[k], "_i.pcx")
						||  !strstr( imagenames[k], "_i.jpg" )
						||  !strstr( imagenames[k], "_i.tga") )
					{
					//	if ( IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1, "_i.pcx"))
						if (	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.pcx")
							||	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.jpg")
							||	IconOfSkinExists( imagenames[k], imagenames, nimagefiles - 1 , "_i.tga"))
						{
							a = strrchr( imagenames[k], '/' );
							b = strrchr( imagenames[k], '\\' );

							if ( a > b )
								c = a;
							else
								c = b;

							strcpy( scratch, c + 1 );

							if ( strrchr( scratch, '.' ) )
								*strrchr( scratch, '.' ) = 0;

							skinnames[s] = strdup( scratch );
							s++;
						}
					}
				}

			// at this point we have a valid player model
			s_pmi[s_numplayermodels].nskins = nskins;
			s_pmi[s_numplayermodels].skindisplaynames = skinnames;

			// make short name for the model
			a = strrchr( dirnames[i], '/' );
			b = strrchr( dirnames[i], '\\' );

			if ( a > b )
				c = a;
			else
				c = b;

			strncpy( s_pmi[s_numplayermodels].displayname, c + 1, MAX_DISPLAYNAME-1 );
			strcpy( s_pmi[s_numplayermodels].directory, c + 1 );

			FreeFileList( imagenames, nimagefiles );

			s_numplayermodels++;
		}
		
		if ( dirnames )
			FreeFileList( dirnames, ndirs );

	// Knightmare- if no valid player models found in path,
	// try next path, if there is one
	} while (s_numplayermodels == 0 && path);

	return true;	//** DMP warning fix
}

static int pmicmpfnc( const void *_a, const void *_b )
{
	const playermodelinfo_s *a = ( const playermodelinfo_s * ) _a;
	const playermodelinfo_s *b = ( const playermodelinfo_s * ) _b;

	/*
	** sort by male, female, then alphabetical
	*/
	if ( strcmp( a->directory, "male" ) == 0 )
		return -1;
	else if ( strcmp( b->directory, "male" ) == 0 )
		return 1;

	if ( strcmp( a->directory, "female" ) == 0 )
		return -1;
	else if ( strcmp( b->directory, "female" ) == 0 )
		return 1;

	return strcmp( a->directory, b->directory );
}


qboolean PlayerConfig_MenuInit( void )
{
	extern cvar_t *name;
//	extern cvar_t *team;
	extern cvar_t *skin;
	char currentdirectory[1024];
	char currentskin[1024];
	char scratch[MAX_QPATH];
	int i = 0;
	int y = 0;

	int currentdirectoryindex = 0;
	int currentskinindex = 0;

	cvar_t *hand = Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE );

	static const char *handedness[] = { "right", "left", "center", 0 };

	PlayerConfig_ScanDirectories();

	if (s_numplayermodels == 0)
		return false;

	if ( hand->value < 0 || hand->value > 2 )
		Cvar_SetValue( "hand", 0 );

	strcpy( currentdirectory, skin->string );

	if ( strchr( currentdirectory, '/' ) )
	{
		strcpy( currentskin, strchr( currentdirectory, '/' ) + 1 );
		*strchr( currentdirectory, '/' ) = 0;
	}
	else if ( strchr( currentdirectory, '\\' ) )
	{
		strcpy( currentskin, strchr( currentdirectory, '\\' ) + 1 );
		*strchr( currentdirectory, '\\' ) = 0;
	}
	else
	{
		strcpy( currentdirectory, "male" );
		strcpy( currentskin, "grunt" );
	}

	qsort( s_pmi, s_numplayermodels, sizeof( s_pmi[0] ), pmicmpfnc );

	memset( s_pmnames, 0, sizeof( s_pmnames ) );
	for ( i = 0; i < s_numplayermodels; i++ )
	{
		s_pmnames[i] = s_pmi[i].displayname;
		if ( Q_stricmp( s_pmi[i].directory, currentdirectory ) == 0 )
		{
			int j;

			currentdirectoryindex = i;

			for ( j = 0; j < s_pmi[i].nskins; j++ )
			{
				if ( Q_stricmp( s_pmi[i].skindisplaynames[j], currentskin ) == 0 )
				{
					currentskinindex = j;
					break;
				}
			}
		}
	}

	if (menu_scale->value && viddef.width >= MENU_STATIC_WIDTH)
	{
		s_player_config_menu.x = viddef.width  / 2 - scaledVideo(210);
		//s_player_config_menu.x = scaledVideo(150);
		s_player_config_menu.y = viddef.height / 2 - scaledVideo(70.0);
		s_player_config_menu.nitems = 0;

		s_player_name_field.generic.type = MTYPE_FIELD;
		s_player_name_field.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_name_field.generic.name = "name";
		s_player_name_field.generic.callback = 0;
		s_player_name_field.generic.x		= -MENU_FONT_SIZE;
		s_player_name_field.generic.y		= y; //0
		s_player_name_field.length	= 20;
		s_player_name_field.visible_length = 20;
		strcpy( s_player_name_field.buffer, name->string );
		s_player_name_field.cursor = strlen( name->string );

		s_player_model_title.generic.type = MTYPE_SEPARATOR;
		s_player_model_title.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_model_title.generic.name = "model";
		s_player_model_title.generic.x    = -MENU_FONT_SIZE;
		s_player_model_title.generic.y	 = y += 3*(MENU_FONT_SIZE+2); //24

		s_player_model_box.generic.type = MTYPE_SPINCONTROL;
		s_player_model_box.generic.x	= -7*MENU_FONT_SIZE;
		s_player_model_box.generic.y	= y += (MENU_FONT_SIZE+2); //32
		s_player_model_box.generic.callback = ModelCallback;
		s_player_model_box.generic.cursor_offset = -6*MENU_FONT_SIZE;
		s_player_model_box.curvalue = currentdirectoryindex;
		s_player_model_box.itemnames = (const char **)s_pmnames;

		s_player_skin_title.generic.type = MTYPE_SEPARATOR;
		s_player_skin_title.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_skin_title.generic.name = "skin";
		s_player_skin_title.generic.x    = -2*MENU_FONT_SIZE;
		s_player_skin_title.generic.y	 = y += 2*(MENU_FONT_SIZE+2); //48

		s_player_skin_box.generic.type = MTYPE_SPINCONTROL;
		s_player_skin_box.generic.x	= -7*MENU_FONT_SIZE;
		s_player_skin_box.generic.y	= y += (MENU_FONT_SIZE+2); //56
		s_player_skin_box.generic.name	= 0;
		s_player_skin_box.generic.callback = SkinCallback; // Knightmare added, was 0
		s_player_skin_box.generic.cursor_offset = -6*MENU_FONT_SIZE;
		s_player_skin_box.curvalue = currentskinindex;
		s_player_skin_box.itemnames = (const char **)s_pmi[currentdirectoryindex].skindisplaynames;

		s_player_hand_title.generic.type = MTYPE_SEPARATOR;
		s_player_hand_title.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_hand_title.generic.name = "handedness";
		s_player_hand_title.generic.x    = 4*MENU_FONT_SIZE;
		s_player_hand_title.generic.y	 = y += 2*(MENU_FONT_SIZE+2); //72

		s_player_handedness_box.generic.type = MTYPE_SPINCONTROL;
		s_player_handedness_box.generic.x	= -7*MENU_FONT_SIZE;
		s_player_handedness_box.generic.y	= y += (MENU_FONT_SIZE+2); //80
		s_player_handedness_box.generic.name	= 0;
		s_player_handedness_box.generic.cursor_offset = -6*MENU_FONT_SIZE;
		s_player_handedness_box.generic.callback = HandednessCallback;
		s_player_handedness_box.curvalue = Cvar_VariableValue( "hand" );
		s_player_handedness_box.itemnames = handedness;

		for (i = 0; i < sizeof(rate_tbl) / sizeof(*rate_tbl) - 1; i++)
			if (Cvar_VariableValue("rate") == rate_tbl[i])
				break;

		s_player_rate_title.generic.type = MTYPE_SEPARATOR;
		s_player_rate_title.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_rate_title.generic.name = "connect speed";
		s_player_rate_title.generic.x    = 7*MENU_FONT_SIZE;
		s_player_rate_title.generic.y	 = y += 2*(MENU_FONT_SIZE+2); //96

		s_player_rate_box.generic.type = MTYPE_SPINCONTROL;
		s_player_rate_box.generic.x	= -7*MENU_FONT_SIZE;
		s_player_rate_box.generic.y	= y += (MENU_FONT_SIZE+2); //104
		s_player_rate_box.generic.name	= 0;
		s_player_rate_box.generic.cursor_offset = -6*MENU_FONT_SIZE;
		s_player_rate_box.generic.callback = RateCallback;
		s_player_rate_box.curvalue = i;
		s_player_rate_box.itemnames = rate_names;
		
		/*
		s_player_download_action.generic.type = MTYPE_ACTION;
		s_player_download_action.generic.name	= "download options";
		s_player_download_action.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_download_action.generic.x	= -5*MENU_FONT_SIZE;
		s_player_download_action.generic.y	= y += 2*(MENU_FONT_SIZE+2); //120
		s_player_download_action.generic.statusbar = NULL;
		s_player_download_action.generic.callback = DownloadOptionsFunc;
		*/

		s_player_back_action.generic.type = MTYPE_ACTION;
		s_player_back_action.generic.name	= "back to multiplayer";
		s_player_back_action.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_back_action.generic.x	= -5*MENU_FONT_SIZE;
		s_player_back_action.generic.y	= y += 2*(MENU_FONT_SIZE+2); //120
		s_player_back_action.generic.statusbar = NULL;
		s_player_back_action.generic.callback = M_BackMenu;
	}
	else
	{
		s_player_config_menu.x = viddef.width / 2 - 95; 
		s_player_config_menu.y = viddef.height / 2 - 97;	
		s_player_config_menu.nitems = 0;

		s_player_name_field.generic.type = MTYPE_FIELD;
		s_player_name_field.generic.name = "name";
		s_player_name_field.generic.callback = 0;
		s_player_name_field.generic.x		= 0;
		s_player_name_field.generic.y		= 0;
		s_player_name_field.length	= 20;
		s_player_name_field.visible_length = 20;
		strcpy( s_player_name_field.buffer, name->string );
		s_player_name_field.cursor = strlen( name->string );

		s_player_model_title.generic.type = MTYPE_SEPARATOR;
		s_player_model_title.generic.name = "model";
		s_player_model_title.generic.x    = -MENU_FONT_SIZE;
		s_player_model_title.generic.y	 = 60;

		s_player_model_box.generic.type = MTYPE_SPINCONTROL;
		s_player_model_box.generic.x	= -56;
		s_player_model_box.generic.y	= 70;
		s_player_model_box.generic.callback = ModelCallback;
		s_player_model_box.generic.cursor_offset = -48;
		s_player_model_box.curvalue = currentdirectoryindex;
		s_player_model_box.itemnames = (const char **)s_pmnames;

		s_player_skin_title.generic.type = MTYPE_SEPARATOR;
		s_player_skin_title.generic.name = "skin";
		s_player_skin_title.generic.x    = -16;
		s_player_skin_title.generic.y	 = 84;

		s_player_skin_box.generic.type = MTYPE_SPINCONTROL;
		s_player_skin_box.generic.x	= -56;
		s_player_skin_box.generic.y	= 94;
		s_player_skin_box.generic.name	= 0;
		s_player_skin_box.generic.callback = SkinCallback; // Knightmare added, was 0
		s_player_skin_box.generic.cursor_offset = -48;
		s_player_skin_box.curvalue = currentskinindex;
		s_player_skin_box.itemnames = (const char **)s_pmi[currentdirectoryindex].skindisplaynames;

		s_player_hand_title.generic.type = MTYPE_SEPARATOR;
		s_player_hand_title.generic.name = "handedness";
		s_player_hand_title.generic.x    = 32;
		s_player_hand_title.generic.y	 = 108;

		s_player_handedness_box.generic.type = MTYPE_SPINCONTROL;
		s_player_handedness_box.generic.x	= -56;
		s_player_handedness_box.generic.y	= 118;
		s_player_handedness_box.generic.name	= 0;
		s_player_handedness_box.generic.cursor_offset = -48;
		s_player_handedness_box.generic.callback = HandednessCallback;
		s_player_handedness_box.curvalue = Cvar_VariableValue( "hand" );
		s_player_handedness_box.itemnames = handedness;

		for (i = 0; i < sizeof(rate_tbl) / sizeof(*rate_tbl) - 1; i++)
			if (Cvar_VariableValue("rate") == rate_tbl[i])
				break;

		s_player_rate_title.generic.type = MTYPE_SEPARATOR;
		s_player_rate_title.generic.name = "connect speed";
		s_player_rate_title.generic.x    = 56;
		s_player_rate_title.generic.y	 = 156;

		s_player_rate_box.generic.type = MTYPE_SPINCONTROL;
		s_player_rate_box.generic.x	= -56;
		s_player_rate_box.generic.y	= 166;
		s_player_rate_box.generic.name	= 0;
		s_player_rate_box.generic.cursor_offset = -48;
		s_player_rate_box.generic.callback = RateCallback;
		s_player_rate_box.curvalue = i;
		s_player_rate_box.itemnames = rate_names;

		/*
		s_player_download_action.generic.type = MTYPE_ACTION;
		s_player_download_action.generic.name	= "download options";
		s_player_download_action.generic.flags= QMF_LEFT_JUSTIFY;
		s_player_download_action.generic.x	= -24;
		s_player_download_action.generic.y	= 186;
		s_player_download_action.generic.statusbar = NULL;
		s_player_download_action.generic.callback = DownloadOptionsFunc;
		*/

		s_player_back_action.generic.type = MTYPE_ACTION;
		s_player_back_action.generic.name	= "back";
		s_player_back_action.generic.flags = QMF_LEFT_JUSTIFY;
		s_player_back_action.generic.x	= -24;
		s_player_back_action.generic.y	= 186;
		s_player_back_action.generic.statusbar = NULL;
		s_player_back_action.generic.callback = M_BackMenu;
	}

	// Knightmare- only register model and skin on starup or when changed
	Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory );
	playermodel = R_RegisterModel( scratch );

	Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
	playerskin = R_RegisterSkin( scratch );

	// show current weapon model (if any)
	if (currentweaponmodel && strlen(currentweaponmodel)) {
		Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s", s_pmi[s_player_model_box.curvalue].directory, currentweaponmodel );
		weaponmodel = R_RegisterModel( scratch );
		if (!weaponmodel) {
			Com_sprintf( scratch, sizeof( scratch ), "players/%s/weapon.md2", s_pmi[s_player_model_box.curvalue].directory );
			weaponmodel = R_RegisterModel( scratch );
		}
	}
	else
	{
		Com_sprintf( scratch, sizeof( scratch ), "players/%s/weapon.md2", s_pmi[s_player_model_box.curvalue].directory );
		weaponmodel = R_RegisterModel( scratch );
	}
	// end Knightmare

	Menu_AddItem( &s_player_config_menu, &s_player_name_field );
	Menu_AddItem( &s_player_config_menu, &s_player_model_title );
	Menu_AddItem( &s_player_config_menu, &s_player_model_box );
	if ( s_player_skin_box.itemnames )
	{
		Menu_AddItem( &s_player_config_menu, &s_player_skin_title );
		Menu_AddItem( &s_player_config_menu, &s_player_skin_box );
	}
	Menu_AddItem( &s_player_config_menu, &s_player_hand_title );
	Menu_AddItem( &s_player_config_menu, &s_player_handedness_box );
	Menu_AddItem( &s_player_config_menu, &s_player_rate_title );
	Menu_AddItem( &s_player_config_menu, &s_player_rate_box );
	//Menu_AddItem( &s_player_config_menu, &s_player_download_action );
	Menu_AddItem( &s_player_config_menu, &s_player_back_action );

	return true;
}

qboolean PlayerConfig_CheckIncerement(int dir, float x, float y)
{
	float min[2], max[2];
	char *sound = NULL;

	min[0] = x;
	max[0] = x + 2*scaledVideo(MENU_FONT_SIZE);
	min[1] = y;
	max[1] = y + 2*scaledVideo(MENU_FONT_SIZE);

	if (cursor.x>=min[0] && cursor.x<=max[0] &&
		cursor.y>=min[1] && cursor.y<=max[1] &&
		!cursor.buttonused[MOUSEBUTTON1] &&
		cursor.buttonclicks[MOUSEBUTTON1]==1)
	{
		if (dir) //dir==1 is left
		{
			if (s_player_skin_box.curvalue>0)
				s_player_skin_box.curvalue--;
		}
		else
		{
			if (s_player_skin_box.curvalue<s_pmi[s_player_model_box.curvalue].nskins)
				s_player_skin_box.curvalue++;
		}

		sound = menu_move_sound;
		cursor.buttonused[MOUSEBUTTON1] = true;
		cursor.buttonclicks[MOUSEBUTTON1] = 0;

		if ( sound )
			S_StartLocalSound( sound );
		SkinCallback (NULL);

		return true;
	}
	return false;
}

void PlayerConfig_MouseClick( void )
{
	//int ystart, xoffset, widest, totalheight, oldhover;
	int i, w, h, count;
	char *sound = NULL;
	buttonmenuobject_t buttons[7];
	float	icon_x = viddef.width  - scaledVideo(325),
			icon_y = viddef.height - scaledVideo(75),
			icon_offset = 0, icon_scale;

	for (i=0;i<7;i++)
		buttons[i].index=-1;

	if (s_pmi[s_player_model_box.curvalue].nskins<7 || s_player_skin_box.curvalue<4)
		i=0;
	else if (s_player_skin_box.curvalue>s_pmi[s_player_model_box.curvalue].nskins-4)
		i=s_pmi[s_player_model_box.curvalue].nskins-7;
	else
		i=s_player_skin_box.curvalue-3;

	if (i>0)
		if (PlayerConfig_CheckIncerement(true, 
				icon_x - scaledVideo(37), // was 45
				icon_y - scaledVideo(30)))
			return;

	for (count=0; count<7;i++,count++)
	{
		if (i<0 || i>=s_pmi[s_player_model_box.curvalue].nskins)
			continue;

		icon_scale = (i==s_player_skin_box.curvalue)? 2: 1;

		R_DrawGetPicSize ( &w, &h, va("/players/%s/%s_i.pcx", 
			s_pmi[s_player_model_box.curvalue].directory,
			s_pmi[s_player_model_box.curvalue].skindisplaynames[i]));
		icon_scale = icon_scale * (32.0/(float)w); // fix for smaller icons
		addPlayerButton (&buttons[count], i, 
			icon_x + icon_offset,
			icon_y - scaledVideo(32),
			scaledVideo(w)*icon_scale, 
			scaledVideo(h)*icon_scale);
		icon_offset += scaledVideo(w)*icon_scale;
	}

	if (s_pmi[s_player_model_box.curvalue].nskins-i>0)
		if (PlayerConfig_CheckIncerement(false,
				icon_x + icon_offset + scaledVideo(5), 
				icon_y - scaledVideo(30)))
			return;

	for (i=0;i<7;i++)
	{
		if (buttons[i].index==-1)
			continue;

		if (cursor.x>=buttons[i].min[0] && cursor.x<=buttons[i].max[0] &&
			cursor.y>=buttons[i].min[1] && cursor.y<=buttons[i].max[1])
		{
			if (!cursor.buttonused[MOUSEBUTTON1] && cursor.buttonclicks[MOUSEBUTTON1]==1)
			{
				s_player_skin_box.curvalue = buttons[i].index;

				sound = menu_move_sound;
				cursor.buttonused[MOUSEBUTTON1] = true;
				cursor.buttonclicks[MOUSEBUTTON1] = 0;

				if ( sound )
					S_StartLocalSound( sound );
				SkinCallback (NULL);

				return;
			}
			break;
		}
	}
}


void PlayerConfig_MenuDraw( void )
{
	extern float CalcFov( float fov_x, float w, float h );
	refdef_t refdef;
	char scratch[MAX_QPATH];
	qboolean newformat = (menu_scale->value && viddef.width >= MENU_STATIC_WIDTH);

	M_Banner( "m_banner_plauer_setup" ); // typo for image name is id's fault

	memset( &refdef, 0, sizeof( refdef ) );

	if (newformat)
	{
		refdef.width = viddef.width;
		refdef.height = viddef.height;
		refdef.x = 0;
		refdef.y = 0;
		refdef.fov_x = 50;
		/*refdef.width = scaledVideo(250);
		refdef.height = scaledVideo(250);
		refdef.x = viddef.width / 2 + scaledVideo(35);
		refdef.y = viddef.height / 2 - scaledVideo(30);*/
	}
	else
	{
		refdef.x = viddef.width / 2;
		refdef.y = viddef.height / 2 - 9*MENU_FONT_SIZE; //was 72
		refdef.width = 144;
		refdef.height = 168;
		refdef.fov_x = 40;
	}
	refdef.fov_y = CalcFov( refdef.fov_x, refdef.width, refdef.height );
	refdef.time = cls.realtime*0.001;
 
	if ( s_pmi[s_player_model_box.curvalue].skindisplaynames )
	{
		int yaw; //was static int
		int /*maxframe = 29,*/ w, h;
		vec3_t modelOrg;
		// Knightmare- Psychopspaz's support for showing weapon model
		entity_t entity[2], *ent;

		refdef.num_entities = 0; // was 2
		refdef.entities = entity;

		yaw = anglemod(cl.time/10);

		VectorSet(modelOrg, 150,
			(hand->value == 1)? 25 : -25, //was 35
			0);

		//Set Up Player Model
		ent = &entity[0];
		{
			memset( &entity[0], 0, sizeof( entity[0] ) );

		// Knightmare- moved registration code to init and change only
		//	Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory );
		//	ent->model = R_RegisterModel( scratch );
			ent->model = playermodel;
		//	Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
		//	ent->skin = R_RegisterSkin( scratch );
			ent->skin = playerskin;

			ent->flags = RF_FULLBRIGHT|RF_NOSHADOW|RF_DEPTHHACK;
			if (hand->value == 1)
				ent->flags |= RF_MIRRORMODEL;
			if (newformat) {
				ent->origin[0] = modelOrg[0];
				ent->origin[1] = modelOrg[1];
				ent->origin[2] = modelOrg[2];
			}
			else {
				ent->origin[0] = 80;
				ent->origin[1] = 0;
				ent->origin[2] = 0;
			}
			VectorCopy( ent->origin, ent->oldorigin );
			ent->frame = 0;
			ent->oldframe = 0;
			ent->backlerp = 0.0;
			ent->angles[1] = yaw;//++;
			//if ( ++yaw > 360 )
			//	yaw -= 360;
			
			if (hand->value == 1)
				ent->angles[1] = 360 - ent->angles[1];

			refdef.num_entities++;
		}
		ent = &entity[1];
		{
			memset( &entity[1], 0, sizeof( entity[1] ) );

		// Knightmare- moved registration code to init and change only
		//	Com_sprintf( scratch, sizeof( scratch ), "players/%s/weapon.md2", s_pmi[s_player_model_box.curvalue].directory );
		//	ent->model = R_RegisterModel( scratch );
			ent->model = weaponmodel;

			//if (!ent->model)
			//	refdef.num_entities = 1;
			if (ent->model)
			{
				ent->skinnum = 0;

				ent->flags = RF_FULLBRIGHT|RF_NOSHADOW|RF_DEPTHHACK;
				if (hand->value == 1)
					ent->flags |= RF_MIRRORMODEL;
				if (newformat) {
					ent->origin[0] = modelOrg[0];
					ent->origin[1] = modelOrg[1];
					ent->origin[2] = modelOrg[2];
				}
				else {
					ent->origin[0] = 80;
					ent->origin[1] = 0;
					ent->origin[2] = 0;
				}
				VectorCopy( ent->origin, ent->oldorigin );
				ent->frame = 0;
				ent->oldframe = 0;
				ent->backlerp = 0.0;
				ent->angles[1] = yaw;
				
				if (hand->value == 1)
					ent->angles[1] = 360 - ent->angles[1];

				refdef.num_entities++;
			}
		}

		refdef.areabits = 0;
		refdef.lightstyles = 0;
		refdef.rdflags = RDF_NOWORLDMODEL;
		// end Knightmare

		Menu_Draw( &s_player_config_menu );

		if (newformat)
#if 1
					//skin selection preview
		{
			float	icon_x = viddef.width  - scaledVideo(325),
					icon_y = viddef.height - scaledVideo(75),
					icon_offset = 0, icon_scale;
			int i, count;
			int color[3];

			TextColor((int)Cvar_VariableValue("alt_text_color"), &color[0], &color[1], &color[2]);

			if (s_pmi[s_player_model_box.curvalue].nskins<7 || s_player_skin_box.curvalue<4)
				i=0;
			else if (s_player_skin_box.curvalue>s_pmi[s_player_model_box.curvalue].nskins-4)
				i=s_pmi[s_player_model_box.curvalue].nskins-7;
			else
				i=s_player_skin_box.curvalue-3;

			if (i>0)
				R_DrawScaledChar( 
					icon_x - scaledVideo(37), //was 45
					icon_y - scaledVideo(30),
					'<' , 3*videoScale(), color[0],color[1],color[2],255,false);
				
			for (count=0; count<7;i++,count++)
			{
				if (i<0 || i>=s_pmi[s_player_model_box.curvalue].nskins)
					continue;

				icon_scale = (i==s_player_skin_box.curvalue)? 2: 1;

				Com_sprintf (scratch, sizeof(scratch), "/players/%s/%s_i.pcx", 
					s_pmi[s_player_model_box.curvalue].directory,
					s_pmi[s_player_model_box.curvalue].skindisplaynames[i] );
				R_DrawGetPicSize ( &w, &h, scratch);
				icon_scale = icon_scale * (32.0/(float)w); // fix for smaller icons
				R_DrawStretchPic ( icon_x + icon_offset, icon_y - scaledVideo(32),
					scaledVideo(w)*icon_scale, scaledVideo(h)*icon_scale, scratch, 1.0);
				icon_offset += scaledVideo(w)*icon_scale;
			}

			if (s_pmi[s_player_model_box.curvalue].nskins-i>0)
				R_DrawScaledChar( 
					icon_x + icon_offset + scaledVideo(5),
					icon_y - scaledVideo(30),
					'>' , 3*videoScale(), color[0],color[1],color[2],255,false);
		}
#else
		{
			//MAKE BORDER AROUND MODEL
			int	bordercolor = 3;

			/*R_DrawFill (refdef.x-5, refdef.y-5, 
				refdef.width + 10, 5, 
				bordercolor);

			R_DrawFill (refdef.x-5, refdef.y-5, 
				5, refdef.height + 10, 
				bordercolor);

			R_DrawFill (refdef.x+refdef.width, refdef.y-5, 
				5, refdef.height+10, 
				bordercolor);

			R_DrawFill (refdef.x-5, refdef.y+refdef.height, 
				refdef.width+10, 5, 
				bordercolor);*/

			Com_sprintf( scratch, sizeof( scratch ), "/players/%s/%s_i.pcx", 
				s_pmi[s_player_model_box.curvalue].directory,
				s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );

			R_DrawGetPicSize ( &w, &h, scratch);
			R_DrawFill ( refdef.x, refdef.y, scaledVideo(w)+5, scaledVideo(h)+5, bordercolor);

			R_DrawStretchPic ( refdef.x, refdef.y,
				scaledVideo(w), scaledVideo(h), scratch, 1.0);
		}
#endif
		else
		{
			M_DrawTextBox( (refdef.x) * (320.0F / viddef.width) - MENU_FONT_SIZE, (viddef.height / 2) * ( 240.0F / viddef.height) - 77, refdef.width / 8, refdef.height / 8 );
			refdef.height += 4;
			R_DrawFill (refdef.x, refdef.y, refdef.width, refdef.height, 5);

			Com_sprintf( scratch, sizeof( scratch ), "/players/%s/%s_i.pcx", 
				s_pmi[s_player_model_box.curvalue].directory,
				s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );

			R_DrawPic( s_player_config_menu.x - 40, refdef.y, scratch );
		}

		R_RenderFrame( &refdef );
	}
}

void PConfigAccept (void)
{
	int i;
	char scratch[1024];

	Cvar_Set( "name", s_player_name_field.buffer );

	Com_sprintf( scratch, sizeof( scratch ), "%s/%s", 
		s_pmi[s_player_model_box.curvalue].directory, 
		s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );

	Cvar_Set( "skin", scratch );

	for ( i = 0; i < s_numplayermodels; i++ )
	{
		int j;

		for ( j = 0; j < s_pmi[i].nskins; j++ )
		{
			if ( s_pmi[i].skindisplaynames[j] )
				free( s_pmi[i].skindisplaynames[j] );
			s_pmi[i].skindisplaynames[j] = 0;
		}
		free( s_pmi[i].skindisplaynames );
		s_pmi[i].skindisplaynames = 0;
		s_pmi[i].nskins = 0;
	}
}

const char *PlayerConfig_MenuKey (int key)
{
	if ( key == K_ESCAPE )
		PConfigAccept();

	return Default_MenuKey( &s_player_config_menu, key );
}


void M_Menu_PlayerConfig_f (void)
{
	if (!PlayerConfig_MenuInit())
	{
		Menu_SetStatusBar( &s_multiplayer_menu, "No valid player models found" );
		return;
	}
	Menu_SetStatusBar( &s_multiplayer_menu, NULL );
	M_PushMenu( PlayerConfig_MenuDraw, PlayerConfig_MenuKey );
}
