/*
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_game.c -- the single player menu and credits

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

/*
=======================================================================
STATIC FUNCTIONS
=======================================================================
*/
/*
static float ClampCvar( float min, float max, float value )
{
	if ( value < min ) return min;
	if ( value > max ) return max;
	return value;
}
*/
/*
=============================================================================

CREDITS MENU

=============================================================================
*/
static char *menu_out_sound		= "misc/menu3.wav";
static char *creditsBuffer;
static int credits_start_time;
// Knigthtmare added- allow credits to scroll past top of screen
static int credits_start_line;
static const char **credits;
static char *creditsIndex[256];
//static char *creditsBuffer;
static const char *idcredits[] =
{
	"^b^s^aQUAKE II BY ID SOFTWARE",
	"",
	"^b^s^aPROGRAMMING",
	"John Carmack",
	"John Cash",
	"Brian Hook",
	"",
	"^b^s^aART",
	"Adrian Carmack",
	"Kevin Cloud",
	"Paul Steed",
	"",
	"^b^s^aLEVEL DESIGN",
	"Tim Willits",
	"American McGee",
	"Christian Antkow",
	"Paul Jaquays",
	"Brandon James",
	"",
	"^b^s^aBIZ",
	"Todd Hollenshead",
	"Barrett (Bear) Alexander",
	"Donna Jackson",
	"",
	"",
	"^b^s^aSPECIAL THANKS",
	"Ben Donges for beta testing",
	"",
	"",
	"",
	"",
	"",
	"",
	"^b^s^aADDITIONAL SUPPORT",
	"",
	"^b^s^aLINUX PORT AND CTF",
	"Dave \"Zoid\" Kirsch",
	"",
	"^b^s^aCINEMATIC SEQUENCES",
	"Ending Cinematic by Blur Studio - ",
	"Venice, CA",
	"",
	"Environment models for Introduction",
	"Cinematic by Karl Dolgener",
	"",
	"Assistance with environment design",
	"by Cliff Iwai",
	"",
	"^b^s^aSOUND EFFECTS AND MUSIC",
	"Sound Design by Soundelux Media Labs.",
	"Music Composed and Produced by",
	"Soundelux Media Labs.  Special thanks",
	"to Bill Brown, Tom Ozanich, Brian",
	"Celano, Jeff Eisner, and The Soundelux",
	"Players.",
	"",
	"\"Level Music\" by Sonic Mayhem",
	"www.sonicmayhem.com",
	"",
	"\"Quake II Theme Song\"",
	"(C) 1997 Rob Zombie. All Rights",
	"Reserved.",
	"",
	"Track 10 (\"Climb\") by Jer Sypult",
	"",
	"Voice of computers by",
	"Carly Staehlin-Taylor",
	"",
	"^b^s^aTHANKS TO ACTIVISION",
	"^b^s^aIN PARTICULAR:",
	"",
	"John Tam",
	"Steve Rosenthal",
	"Marty Stratton",
	"Henk Hartong",
	"",
	"Quake II(tm) (C)1997 Id Software, Inc.",
	"All Rights Reserved.  Distributed by",
	"Activision, Inc. under license.",
	"Quake II(tm), the Id Software name,",
	"the \"Q II\"(tm) logo and id(tm)",
	"logo are trademarks of Id Software,",
	"Inc. Activision(R) is a registered",
	"trademark of Activision, Inc. All",
	"other trademarks and trade names are",
	"properties of their respective owners.",
	0
};

static const char *xatcredits[] =
{
	"^b^s^aQUAKE II MISSION PACK: THE RECKONING",
	"^b^s^aBY",
	"^b^s^aXATRIX ENTERTAINMENT, INC.",
	"",
	"^b^s^aDESIGN AND DIRECTION",
	"Drew Markham",
	"",
	"^b^s^aPRODUCED BY",
	"Greg Goodrich",
	"",
	"^b^s^aPROGRAMMING",
	"Rafael Paiz",
	"",
	"^b^s^aLEVEL DESIGN / ADDITIONAL GAME DESIGN",
	"Alex Mayberry",
	"",
	"^b^s^aLEVEL DESIGN",
	"Mal Blackwell",
	"Dan Koppel",
	"",
	"^b^s^aART DIRECTION",
	"Michael \"Maxx\" Kaufman",
	"",
	"^b^s^aCOMPUTER GRAPHICS SUPERVISOR AND",
	"^b^s^aCHARACTER ANIMATION DIRECTION",
	"Barry Dempsey",
	"",
	"^b^s^aSENIOR ANIMATOR AND MODELER",
	"Jason Hoover",
	"",
	"^b^s^aCHARACTER ANIMATION AND",
	"^b^s^aMOTION CAPTURE SPECIALIST",
	"Amit Doron",
	"",
	"^b^s^aART",
	"Claire Praderie-Markham",
	"Viktor Antonov",
	"Corky Lehmkuhl",
	"",
	"^b^s^aINTRODUCTION ANIMATION",
	"Dominique Drozdz",
	"",
	"^b^s^aADDITIONAL LEVEL DESIGN",
	"Aaron Barber",
	"Rhett Baldwin",
	"",
	"^b^s^a3D CHARACTER ANIMATION TOOLS",
	"Gerry Tyra, SA Technology",
	"",
	"^b^s^aADDITIONAL EDITOR TOOL PROGRAMMING",
	"Robert Duffy",
	"",
	"^b^s^aADDITIONAL PROGRAMMING",
	"Ryan Feltrin",
	"",
	"^b^s^aPRODUCTION COORDINATOR",
	"Victoria Sylvester",
	"",
	"^b^s^aSOUND DESIGN",
	"Gary Bradfield",
	"",
	"^b^s^aMUSIC BY",
	"Sonic Mayhem",
	"",
	"",
	"",
	"^b^s^aSPECIAL THANKS",
	"^b^s^aTO",
	"^b^s^aOUR FRIENDS AT ID SOFTWARE",
	"",
	"John Carmack",
	"John Cash",
	"Brian Hook",
	"Adrian Carmack",
	"Kevin Cloud",
	"Paul Steed",
	"Tim Willits",
	"Christian Antkow",
	"Paul Jaquays",
	"Brandon James",
	"Todd Hollenshead",
	"Barrett (Bear) Alexander",
	"Dave \"Zoid\" Kirsch",
	"Donna Jackson",
	"",
	"",
	"",
	"^b^s^aTHANKS TO ACTIVISION",
	"^b^s^aIN PARTICULAR:",
	"",
	"Marty Stratton",
	"Henk \"The Original Ripper\" Hartong",
	"Kevin Kraff",
	"Jamey Gottlieb",
	"Chris Hepburn",
	"",
	"^b^s^aAND THE GAME TESTERS",
	"",
	"Tim Vanlaw",
	"Doug Jacobs",
	"Steven Rosenthal",
	"David Baker",
	"Chris Campbell",
	"Aaron Casillas",
	"Steve Elwell",
	"Derek Johnstone",
	"Igor Krinitskiy",
	"Samantha Lee",
	"Michael Spann",
	"Chris Toft",
	"Juan Valdes",
	"",
	"^b^s^aTHANKS TO INTERGRAPH COMPUTER SYTEMS",
	"^b^s^aIN PARTICULAR:",
	"",
	"Michael T. Nicolaou",
	"",
	"",
	"Quake II Mission Pack: The Reckoning",
	"(tm) (C)1998 Id Software, Inc. All",
	"Rights Reserved. Developed by Xatrix",
	"Entertainment, Inc. for Id Software,",
	"Inc. Distributed by Activision Inc.",
	"under license. Quake(R) is a",
	"registered trademark of Id Software,",
	"Inc. Quake II Mission Pack: The",
	"Reckoning(tm), Quake II(tm), the Id",
	"Software name, the \"Q II\"(tm) logo",
	"and id(tm) logo are trademarks of Id",
	"Software, Inc. Activision(R) is a",
	"registered trademark of Activision,",
	"Inc. Xatrix(R) is a registered",
	"trademark of Xatrix Entertainment,",
	"Inc. All other trademarks and trade",
	"names are properties of their",
	"respective owners.",
	0
};

static const char *roguecredits[] =
{
	"^b^s^aQUAKE II MISSION PACK 2: GROUND ZERO",
	"^b^s^aBY",
	"^b^s^aROGUE ENTERTAINMENT, INC.",
	"",
	"^b^s^aPRODUCED BY",
	"Jim Molinets",
	"",
	"^b^s^aPROGRAMMING",
	"Peter Mack",
	"Patrick Magruder",
	"",
	"^b^s^aLEVEL DESIGN",
	"Jim Molinets",
	"Cameron Lamprecht",
	"Berenger Fish",
	"Robert Selitto",
	"Steve Tietze",
	"Steve Thoms",
	"",
	"^b^s^aART DIRECTION",
	"Rich Fleider",
	"",
	"^b^s^aART",
	"Rich Fleider",
	"Steve Maines",
	"Won Choi",
	"",
	"^b^s^aANIMATION SEQUENCES",
	"Creat Studios",
	"Steve Maines",
	"",
	"^b^s^aADDITIONAL LEVEL DESIGN",
	"Rich Fleider",
	"Steve Maines",
	"Peter Mack",
	"",
	"^b^s^aSOUND",
	"James Grunke",
	"",
	"^b^s^aGROUND ZERO THEME",
	"^b^s^aAND",
	"^b^s^aMUSIC BY",
	"Sonic Mayhem",
	"",
	"^b^s^aVWEP MODELS",
	"Brent \"Hentai\" Dill",
	"",
	"",
	"",
	"^b^s^aSPECIAL THANKS",
	"^b^s^aTO",
	"^b^s^aOUR FRIENDS AT ID SOFTWARE",
	"",
	"John Carmack",
	"John Cash",
	"Brian Hook",
	"Adrian Carmack",
	"Kevin Cloud",
	"Paul Steed",
	"Tim Willits",
	"Christian Antkow",
	"Paul Jaquays",
	"Brandon James",
	"Todd Hollenshead",
	"Barrett (Bear) Alexander",
	"Katherine Anna Kang",
	"Donna Jackson",
	"Dave \"Zoid\" Kirsch",
	"",
	"",
	"",
	"^b^s^aTHANKS TO ACTIVISION",
	"^b^s^aIN PARTICULAR:",
	"",
	"Marty Stratton",
	"Henk Hartong",
	"Mitch Lasky",
	"Steve Rosenthal",
	"Steve Elwell",
	"",
	"^b^s^aAND THE GAME TESTERS",
	"",
	"The Ranger Clan",
	"Dave \"Zoid\" Kirsch",
	"Nihilistic Software",
	"Robert Duffy",
	"",
	"And Countless Others",
	"",
	"",
	"",
	"Quake II Mission Pack 2: Ground Zero",
	"(tm) (C)1998 Id Software, Inc. All",
	"Rights Reserved. Developed by Rogue",
	"Entertainment, Inc. for Id Software,",
	"Inc. Distributed by Activision Inc.",
	"under license. Quake(R) is a",
	"registered trademark of Id Software,",
	"Inc. Quake II Mission Pack 2: Ground",
	"Zero(tm), Quake II(tm), the Id",
	"Software name, the \"Q II\"(tm) logo",
	"and id(tm) logo are trademarks of Id",
	"Software, Inc. Activision(R) is a",
	"registered trademark of Activision,",
	"Inc. Rogue(R) is a registered",
	"trademark of Rogue Entertainment,",
	"Inc. All other trademarks and trade",
	"names are properties of their",
	"respective owners.",
	0
};

int stringLengthExtra ( const char *string);
void M_Credits_MenuDraw( void )
{
	float alpha /*, time = (cls.realtime - credits_start_time) * 0.05*/;
	int i, y, x, len;


	if ((viddef.height - ((cls.realtime - credits_start_time)/40.0F) + credits_start_line * scaledVideo(MENU_FONT_SIZE+2)) < 0)
	{
		credits_start_line++;
		if (!credits[credits_start_line])
		{
			credits_start_line = 0;
			credits_start_time = cls.realtime;
		}
	}

	/*
	** draw the credits
	*/
	for (i=credits_start_line, y=viddef.height - ((cls.realtime - credits_start_time)/40.0F) + credits_start_line * scaledVideo(MENU_FONT_SIZE+2);
		credits[i] && y < viddef.height; y += scaledVideo(MENU_FONT_SIZE+2), i++) // was 10
	{
		//int j;
		int	stringoffset = 0;
		int bold = false;

		if ( y <= -MENU_FONT_SIZE )
			continue;
		if ( y > viddef.height )
			continue;

		if ( credits[i][0] == '+' )
		{
			bold = true;
			stringoffset = 1;
		}
		else
		{
			bold = false;
			stringoffset = 0;
		}

		if (y > 7*viddef.height/8)
		{
			float y_test, h_test;
			y_test = y - (7.0/8.0)*viddef.height;
			h_test = viddef.height/8;

			alpha = 1 - (y_test/h_test);

			if (alpha>1) alpha=1; if (alpha<0) alpha=0;
		}
		else if (y < viddef.height/8)
		{
			float y_test, h_test;
			y_test = y;
			h_test = viddef.height/8;

			alpha = y_test/h_test;

			if (alpha>1) alpha=1; if (alpha<0) alpha=0;
		}
		else
			alpha = 1;

		len = strlen(credits[i]) - stringLengthExtra(credits[i]);

		x = ( viddef.width - len * scaledVideo(MENU_FONT_SIZE) - stringoffset * scaledVideo(MENU_FONT_SIZE) ) / 2 + ( stringoffset ) * scaledVideo(MENU_FONT_SIZE);
		Menu_DrawString(x, y, credits[i], alpha*255);

		//x = ( viddef.width - len * MENU_FONT_SIZE - stringoffset * MENU_FONT_SIZE ) / 2 + ( stringoffset ) * MENU_FONT_SIZE;
		//DrawString (x, y, credits[i], (int)(alpha*255));
		/*for ( j = 0; credits[i][j+stringoffset]; j++ )
		{
			x = (viddef.width - strlen(credits[i]) * scaledVideo(MENU_FONT_SIZE) - stringoffset * scaledVideo(MENU_FONT_SIZE) ) / 2
				+ (j + stringoffset) * scaledVideo(MENU_FONT_SIZE);
			if ( bold )
				R_DrawChar( x, y, credits[i][j+stringoffset] + 128, alpha*255);
			else
				R_DrawChar( x, y, credits[i][j+stringoffset], alpha*255);
		}*/
	}
	/*if (y < 0)
		credits_start_time = cls.realtime;*/
}

const char *M_Credits_Key( int key )
{
	char *sound = NULL;

	switch (key)
	{
	case K_ESCAPE:
	//case K_MOUSE2
		if (creditsBuffer)
			FS_FreeFile (creditsBuffer);
		M_PopMenu ();
		sound = menu_out_sound;
		break;
	}

	return sound;
}


void M_Menu_Credits_f( void )
{
	int		n;
	int		count;
	char	*p;

	creditsBuffer = NULL;
	count = FS_LoadFile ("credits", (void *)&creditsBuffer);
	if (count != -1)
	{
		p = creditsBuffer;
		for (n = 0; n < 255; n++)
		{
			creditsIndex[n] = p;
			while (*p != '\r' && *p != '\n')
			{
				p++;
				if (--count == 0)
					break;
			}
			if (*p == '\r')
			{
				*p++ = 0;
				if (--count == 0)
					break;
			}
			*p++ = 0;
			if (--count == 0)
				break;
		}
		creditsIndex[++n] = 0;
		credits = (const char **)creditsIndex;
	}
	else
	{	
		if (modType("xatrix"))			// xatrix
			credits = xatcredits;
		else if (modType("rogue"))		// ROGUE
			credits = roguecredits;
		else
			credits = idcredits;	
	}

	credits_start_time = cls.realtime;
	credits_start_line = 0; // Knightmare- allow credits to scroll past top of screen
	M_PushMenu( M_Credits_MenuDraw, M_Credits_Key);
}

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

GAME MENU

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

static int		m_game_cursor;

static menuframework_s	s_game_menu;
static menuaction_s		s_easy_game_action;
static menuaction_s		s_medium_game_action;
static menuaction_s		s_hard_game_action;
static menuaction_s		s_nitemare_game_action;
static menuaction_s		s_load_game_action;
static menuaction_s		s_save_game_action;
static menuaction_s		s_credits_action;
static menuseparator_s	s_blankline;
static menuaction_s		s_game_back_action;

static void StartGame( void )
{
	// disable updates and start the cinematic going
	cl.servercount = -1;
	M_ForceMenuOff ();
	Cvar_SetValue( "deathmatch", 0 );
	Cvar_SetValue( "coop", 0 );
	Cvar_SetValue( "gamerules", 0 );		//PGM

	//Cbuf_AddText ("loading ; killserver ; wait ; newgame\n");
	if (cls.state != ca_disconnected) // don't force loadscreen if disconnected
        Cbuf_AddText ("loading ; killserver ; wait\n");
        Cbuf_AddText ("newgame\n");
	cls.key_dest = key_game;
}

static void EasyGameFunc( void *data )
{
	Cvar_ForceSet( "skill", "0" );
	StartGame();
}

static void MediumGameFunc( void *data )
{
	Cvar_ForceSet( "skill", "1" );
	StartGame();
}

static void HardGameFunc( void *data )
{
	Cvar_ForceSet( "skill", "2" );
	StartGame();
}

static void NitemareGameFunc( void *data )
{
	Cvar_ForceSet( "skill", "3" );
	StartGame();
}

static void LoadGameFunc( void *unused )
{
	M_Menu_LoadGame_f ();
}

static void SaveGameFunc( void *unused )
{
	M_Menu_SaveGame_f();
}

static void CreditsFunc( void *unused )
{
	M_Menu_Credits_f();
}

void Game_MenuInit( void )
{
/*
	static const char *difficulty_names[] =
	{
		"easy",
		"medium",
		"hard",
		0
	};
*/
	int y = 0;

	s_game_menu.x = viddef.width * 0.50 - scaledVideo(3*MENU_FONT_SIZE);
	s_game_menu.nitems = 0;

	s_easy_game_action.generic.type	= MTYPE_ACTION;
	s_easy_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_easy_game_action.generic.x		= 0;
	s_easy_game_action.generic.y		= y; // 0
	s_easy_game_action.generic.name	= " easy";
	s_easy_game_action.generic.callback = EasyGameFunc;

	s_medium_game_action.generic.type	= MTYPE_ACTION;
	s_medium_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_medium_game_action.generic.x		= 0;
	s_medium_game_action.generic.y		= y += (MENU_FONT_SIZE+2); //10
	s_medium_game_action.generic.name	= " medium";
	s_medium_game_action.generic.callback = MediumGameFunc;

	s_hard_game_action.generic.type	= MTYPE_ACTION;
	s_hard_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_hard_game_action.generic.x		= 0;
	s_hard_game_action.generic.y		= y += (MENU_FONT_SIZE+2); //20
	s_hard_game_action.generic.name	= " hard";
	s_hard_game_action.generic.callback = HardGameFunc;

	s_nitemare_game_action.generic.type	= MTYPE_ACTION;
	s_nitemare_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_nitemare_game_action.generic.x		= 0;
	s_nitemare_game_action.generic.y		= y += (MENU_FONT_SIZE+2); //30
	s_nitemare_game_action.generic.name	= " nightmare";
	s_nitemare_game_action.generic.callback = NitemareGameFunc;


	s_blankline.generic.type = MTYPE_SEPARATOR;

	s_load_game_action.generic.type	= MTYPE_ACTION;
	s_load_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_load_game_action.generic.x		= 0;
	s_load_game_action.generic.y		= y += 2*(MENU_FONT_SIZE+2); //50
	s_load_game_action.generic.name	= " load game";
	s_load_game_action.generic.callback = LoadGameFunc;

	s_save_game_action.generic.type	= MTYPE_ACTION;
	s_save_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_save_game_action.generic.x		= 0;
	s_save_game_action.generic.y		= y += (MENU_FONT_SIZE+2); //60
	s_save_game_action.generic.name	= " save game";
	s_save_game_action.generic.callback = SaveGameFunc;

	s_credits_action.generic.type	= MTYPE_ACTION;
	s_credits_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_credits_action.generic.x		= 0;
	s_credits_action.generic.y		= y += (MENU_FONT_SIZE+2); //70
	s_credits_action.generic.name	= " credits";
	s_credits_action.generic.callback = CreditsFunc;

	s_game_back_action.generic.type	= MTYPE_ACTION;
	s_game_back_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_game_back_action.generic.x		= 0;
	s_game_back_action.generic.y		= y += 2*(MENU_FONT_SIZE+2); //90
	s_game_back_action.generic.name	= " back to main";
	s_game_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_game_menu, ( void * ) &s_easy_game_action );
	Menu_AddItem( &s_game_menu, ( void * ) &s_medium_game_action );
	Menu_AddItem( &s_game_menu, ( void * ) &s_hard_game_action );
	Menu_AddItem( &s_game_menu, ( void * ) &s_nitemare_game_action );

	Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );

	Menu_AddItem( &s_game_menu, ( void * ) &s_load_game_action );
	Menu_AddItem( &s_game_menu, ( void * ) &s_save_game_action );

	Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );
	Menu_AddItem( &s_game_menu, ( void * ) &s_credits_action );

	Menu_AddItem( &s_game_menu, ( void * ) &s_game_back_action );

	Menu_Center( &s_game_menu );
}

void Game_MenuDraw( void )
{
	M_Banner( "m_banner_game" );
	Menu_AdjustCursor( &s_game_menu, 1 );
	Menu_Draw( &s_game_menu );
}

const char *Game_MenuKey( int key )
{
	return Default_MenuKey( &s_game_menu, key );
}

void M_Menu_Game_f (void)
{
	Game_MenuInit();
	M_PushMenu( Game_MenuDraw, Game_MenuKey );
	m_game_cursor = 1;
}

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

LOADGAME MENU

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

#define	MAX_SAVEGAMES	21 // was 15

static menuframework_s	s_savegame_menu;

static menuframework_s	s_loadgame_menu;
static menuaction_s		s_loadgame_actions[MAX_SAVEGAMES];
static menuaction_s		s_loadgame_back_action;

static menuframework_s	s_savegame_menu;
static menuaction_s		s_savegame_actions[MAX_SAVEGAMES];
static menuaction_s		s_savegame_back_action;

char		m_savestrings[MAX_SAVEGAMES][32];
qboolean	m_savevalid[MAX_SAVEGAMES];
qboolean	m_saveshotvalid[MAX_SAVEGAMES+1];

char		m_mapname[MAX_QPATH];
qboolean	m_mapshotvalid;

void Create_Savestrings (void)
{
	int		i;
	fileHandle_t	f;
	char	name[MAX_OSPATH];
	char	mapname[MAX_TOKEN_CHARS];
	char	*ch;

	for (i=0 ; i<MAX_SAVEGAMES ; i++)
	{
		Com_sprintf (name, sizeof(name), "save/save%i/server.ssv", i);
		FS_FOpenFile (name, &f, FS_READ);
		if (!f)
		{
			//Com_Printf("Save file %s not found.\n", name);
			strcpy (m_savestrings[i], "<EMPTY>");
			m_savevalid[i] = false;
		}
		else
		{
			FS_Read (m_savestrings[i], sizeof(m_savestrings[i]), f);

			if (i==0) { // grab mapname
				FS_Read (mapname, sizeof(mapname), f);
				if (mapname[0] == '*') // skip * marker
					Com_sprintf (m_mapname, sizeof(m_mapname), mapname+1);
				else
					Com_sprintf (m_mapname, sizeof(m_mapname), mapname);
				if ((ch = strchr (m_mapname, '$')))
					*ch = 0; // terminate string at $ marker
			}
			FS_FCloseFile(f);
			m_savevalid[i] = true;
		}
	}
}

void InitSaveshots (qboolean loadmenu)
{
	int i;
	char shotname [MAX_QPATH];
	char mapshotname [MAX_QPATH];

	for ( i = 1; i < MAX_SAVEGAMES; i++ )
	{	// free previously loaded shots
		Com_sprintf(shotname, sizeof(shotname), "save/save%i/shot.jpg", i);
		R_FreePic (shotname);
		Com_sprintf(shotname, sizeof(shotname), "/save/save%i/shot.jpg", i);
		if (R_DrawFindPic(shotname))
			m_saveshotvalid[i] = true;
		else
			m_saveshotvalid[i] = false;
	}
	if (loadmenu)
	{	// register mapshot for autosave
		if (m_savevalid[0]) {
			Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", m_mapname);
			if (R_DrawFindPic(mapshotname))
				m_mapshotvalid = true;
			else
				m_mapshotvalid = false;
		}
		else
			m_mapshotvalid = false;
	}
	// register null saveshot
	if (R_DrawFindPic("/gfx/noscreen.pcx"))
		m_saveshotvalid[MAX_SAVEGAMES] = true;
	else
		m_saveshotvalid[MAX_SAVEGAMES] = false;
}

void DrawSaveshot (qboolean loadmenu)
{
	char shotname [MAX_QPATH];
	char mapshotname [MAX_QPATH];
	int i;
	if (loadmenu)
		i = s_loadgame_actions[s_loadgame_menu.cursor].generic.localdata[0];
	else
		i = s_savegame_actions[s_savegame_menu.cursor].generic.localdata[0];

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

	if ( m_savevalid[i] && m_saveshotvalid[i] )
	{
		Com_sprintf(shotname, sizeof(shotname), "/save/save%i/shot.jpg", i);

		R_DrawStretchPic (
			viddef.width/2 + scaledVideo(46),
			viddef.height/2 - scaledVideo(58),
			scaledVideo(240), scaledVideo(180), shotname, 1.0);
	}
	else if ( loadmenu && i==0 && m_savevalid[i] && m_mapshotvalid ) // autosave shows mapshot
	{
		Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", m_mapname);

		R_DrawStretchPic (
			viddef.width/2 + scaledVideo(46),
			viddef.height/2 - scaledVideo(58),
			scaledVideo(240), scaledVideo(180), mapshotname, 1.0);
	}
	else if (m_saveshotvalid[MAX_SAVEGAMES])
		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);
}

extern	char *load_saveshot;
char loadshotname[MAX_QPATH];
void LoadGameCallback( void *self )
{
	menuaction_s *a = ( menuaction_s * ) self;

	// set saveshot name here
	if ( m_saveshotvalid[ a->generic.localdata[0] ] ) {
		Com_sprintf(loadshotname, sizeof(loadshotname), "/save/save%i/shot.jpg", a->generic.localdata[0]);
		load_saveshot = loadshotname; }
	else
		load_saveshot = NULL;

	if ( m_savevalid[ a->generic.localdata[0] ] )
		Cbuf_AddText (va("load save%i\n",  a->generic.localdata[0] ) );
	M_ForceMenuOff ();
}

void LoadGame_MenuInit ( void )
{
	int i;

	s_loadgame_menu.x = viddef.width / 2 - scaledVideo(240);
	//s_loadgame_menu.x = viddef.width / 2 - scaledVideo(15*MENU_FONT_SIZE);	// was 120
	s_loadgame_menu.y = viddef.height / 2 - scaledVideo(7.25*MENU_FONT_SIZE); // was 58
	s_loadgame_menu.nitems = 0;

	Create_Savestrings();

	for ( i = 0; i < MAX_SAVEGAMES; i++ )
	{
		s_loadgame_actions[i].generic.name			= m_savestrings[i];
		s_loadgame_actions[i].generic.flags			= QMF_LEFT_JUSTIFY;
		s_loadgame_actions[i].generic.localdata[0]	= i;
		s_loadgame_actions[i].generic.callback		= LoadGameCallback;

		s_loadgame_actions[i].generic.x = 0;
		s_loadgame_actions[i].generic.y = (i) * (MENU_FONT_SIZE+2); //10
		if (i>0)	// separate from autosave
			s_loadgame_actions[i].generic.y += 10;

		s_loadgame_actions[i].generic.type = MTYPE_ACTION;

		Menu_AddItem( &s_loadgame_menu, &s_loadgame_actions[i] );
	}

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

	Menu_AddItem( &s_loadgame_menu, &s_loadgame_back_action );

	InitSaveshots (true); // register saveshots
}



void LoadGame_MenuDraw( void )
{
	M_Banner( "m_banner_load_game" );
//	Menu_AdjustCursor( &s_loadgame_menu, 1 );
	Menu_Draw( &s_loadgame_menu );
	DrawSaveshot (true);
}

const char *LoadGame_MenuKey( int key )
{
	if ( key == K_ESCAPE || key == K_ENTER )
	{
		s_savegame_menu.cursor = s_loadgame_menu.cursor - 1;
		if ( s_savegame_menu.cursor < 0 )
			s_savegame_menu.cursor = 0;
	}
	return Default_MenuKey( &s_loadgame_menu, key );
}

void M_Menu_LoadGame_f (void)
{
	LoadGame_MenuInit();
	M_PushMenu( LoadGame_MenuDraw, LoadGame_MenuKey );
}


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

SAVEGAME MENU

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

void SaveGameCallback( void *self )
{
	menuaction_s *a = ( menuaction_s * ) self;

	Cbuf_AddText (va("save save%i\n", a->generic.localdata[0] ));
	M_ForceMenuOff ();
}

void SaveGame_MenuDraw( void )
{
	M_Banner( "m_banner_save_game" );
	Menu_AdjustCursor( &s_savegame_menu, 1 );
	Menu_Draw( &s_savegame_menu );
	DrawSaveshot (false);
}

void SaveGame_MenuInit( void )
{
	int i;

	s_savegame_menu.x = viddef.width / 2 - scaledVideo(240);
	//s_savegame_menu.x = viddef.width / 2 - scaledVideo(15*MENU_FONT_SIZE);	// was 120
	s_savegame_menu.y = viddef.height / 2 - scaledVideo(7.25*MENU_FONT_SIZE);//was 58
	s_savegame_menu.nitems = 0;

	Create_Savestrings();

	// don't include the autosave slot
	for ( i = 0; i < MAX_SAVEGAMES-1; i++ )
	{
		s_savegame_actions[i].generic.name = m_savestrings[i+1];
		s_savegame_actions[i].generic.localdata[0] = i+1;
		s_savegame_actions[i].generic.flags = QMF_LEFT_JUSTIFY;
		s_savegame_actions[i].generic.callback = SaveGameCallback;

		s_savegame_actions[i].generic.x = 0;
		s_savegame_actions[i].generic.y = ( i ) * (MENU_FONT_SIZE+2); //10;

		s_savegame_actions[i].generic.type = MTYPE_ACTION;

		Menu_AddItem( &s_savegame_menu, &s_savegame_actions[i] );
	}
	s_savegame_back_action.generic.type	= MTYPE_ACTION;
	s_savegame_back_action.generic.flags  = QMF_LEFT_JUSTIFY;
	s_savegame_back_action.generic.x		= 0;
	s_savegame_back_action.generic.y		= (MAX_SAVEGAMES+1)*(MENU_FONT_SIZE+2);
	s_savegame_back_action.generic.name		= " back";
	s_savegame_back_action.generic.callback = M_BackMenu;

	Menu_AddItem( &s_savegame_menu, &s_savegame_back_action );


	InitSaveshots (false);
}

const char *SaveGame_MenuKey( int key )
{
	if ( key == K_ENTER || key == K_ESCAPE )
	{
		s_loadgame_menu.cursor = s_savegame_menu.cursor - 1;
		if ( s_loadgame_menu.cursor < 0 )
			s_loadgame_menu.cursor = 0;
	}
	return Default_MenuKey( &s_savegame_menu, key );
}

void M_Menu_SaveGame_f (void)
{
	if (!Com_ServerState())
		return;		// not playing a game

	SaveGame_MenuInit();
	M_PushMenu( SaveGame_MenuDraw, SaveGame_MenuKey );
	Create_Savestrings ();
}
