//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/cvar.c                                                 $
// $Revision:: 3                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 2/5/02 7:39 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// dynamic variable tracking
//

#include "qcommon.h"
#ifndef DEDICATED_ONLY
#include "console3d.h"
#endif

extern cvar_t *Cvar_FindVar (const char *var_name);

cvar_t	*cvar_vars;		/* Root pointer to the linked list */
cvar_t	*con_show_description; /* FS */
cvar_t	*con_show_dev_flags; /* FS */
void Cvar_Force_f (void); /* FS: Force a NOSET CVAR (within reason) */
void Cvar_Toggle_f (void); /* FS: Toggle a CVAR */
void Cvar_Reset_f (void); /* FS: Reset a CVAR to it's default value */
qboolean Cvar_Never_Reset_Cmds(const char *var_name); /* FS: Tired of copying CVARs everywhere */
static void Cvar_ParseDeveloperFlags (void); /* FS */

/*
============
Cvar_InfoValidate
============
*/
static qboolean Cvar_InfoValidate (const char *s)
{
   if (strstr (s, "\\"))
      return false;
   if (strstr (s, "\""))
      return false;
   if (strstr (s, ";"))
      return false;
   return true;
}

/*
============
Cvar_FindVar
============
*/
cvar_t *Cvar_FindVar (const char *var_name)
{
	cvar_t	*var;

	for (var=cvar_vars ; var ; var=var->next)
		if (!strcmp (var_name, var->name))
			return var;

	return NULL;
}

/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue (const char *var_name)
{
	cvar_t	*var;

	var = Cvar_FindVar (var_name);
	if (!var)
		return 0;
	return Sinatof (var->string);
}


/*
============
Cvar_VariableString
============
*/
const char *Cvar_VariableString (const char *var_name)
{
	cvar_t *var;

	var = Cvar_FindVar (var_name);
	if (!var)
		return "";
	return var->string;
}


/*
============
Cvar_CompleteVariable
============
*/
const char *Cvar_CompleteVariable (const char *partial)
{
	cvar_t		*cvar;
	int			len;

	len = (int)strlen(partial);

	if (!len)
		return NULL;

	// check exact match
	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
		if (!strcmp (partial,cvar->name))
			return cvar->name;

	// check partial match
	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
		if (!strncmp (partial,cvar->name, len))
			return cvar->name;

	return NULL;
}


/*
============
Cvar_Get

If the variable allready exists, the value will not be set
The flags will be or'ed in if the variable exists.
============
*/
cvar_t *Cvar_Get (const char *var_name, const char *var_value, int flags)
{
	cvar_t	*var;

   if (flags & (CVAR_USERINFO | CVAR_SERVERINFO))
   {
      if (!Cvar_InfoValidate (var_name))
      {
         Com_Printf("invalid info cvar name\n");
         return NULL;
      }
   }

   var = Cvar_FindVar (var_name);
	if (var)
	{
		var->flags |= flags;
		// Knightmare- change default value if this is called again
		free(var->defaultValue);
		var->defaultValue = strdup(var_value);
		var->defaultFlags |= flags; /* FS: Ditto */

		return var;
	}

	if (!var_value)
		return NULL;

   if (flags & (CVAR_USERINFO | CVAR_SERVERINFO))
   {
      if (!Cvar_InfoValidate (var_value))
      {
         Com_Printf("invalid info cvar value\n");
         return NULL;
      }
   }

	var = (cvar_t *)Z_Malloc(sizeof(*var));
	var->name = strdup(var_name);
	var->string = strdup(var_value);
	var->modified = true;
	var->value = Sinatof (var->string);
	var->intValue = atoi(var->string); /* FS: So we don't need to cast shit all the time */
	var->defaultValue = strdup(var_value); /* FS: Find out what it was initially */
	var->defaultFlags = flags; /* FS: Default flags for resetcvar */
	var->description = NULL; /* FS: Init it first, d'oh */

	// link the variable in
	var->next = cvar_vars;
	cvar_vars = var;

	var->flags = flags;

	return var;
}

/*
============
Cvar_Set2
============
*/
static cvar_t *Cvar_Set2 (const char *var_name, const char *value, qboolean force)
{
	cvar_t	*var;

	var = Cvar_FindVar (var_name);
	if (!var)
	{	// create it
		return Cvar_Get (var_name, value, 0);
	}

	if (var->flags & (CVAR_USERINFO | CVAR_SERVERINFO))
	{
		if (!Cvar_InfoValidate (value))
		{
			Com_Printf("invalid info cvar value\n");
			return var;
		}
	}

	if (!force)
	{
		if (var->flags & CVAR_NOSET)
		{
			Com_Printf ("%s is write protected.\n", var_name);
			return var;
		}

		if (var->flags & CVAR_LATCH)
		{
			if (var->latched_string)
			{
				if (strcmp(value, var->latched_string) == 0)
					return var;
				free(var->latched_string);
				var->latched_string = 0;
			}
			else
			{
				if (strcmp(value, var->string) == 0)
					return var;
			}
	
			if (Com_ServerState())
			{
				Com_Printf ("%s will be changed for next game.\n", var_name);
				var->latched_string = strdup(value);
			}
			else
			{
				free(var->string);
				var->string = strdup(value);
				var->value = Sinatof (var->string);
				var->intValue = atoi(var->string); /* FS: So we don't need to cast shit all the time */

				if (!strcmp(var->name, "game"))
				{
					char cfgExecString[MAX_QPATH];

					FS_SetGamedir (var->string);
					// 2015 code 
					if (!strcmp(var->string, "2015"))
						Cvar_FullSet ("game2015", "1", CVAR_SERVERINFO|CVAR_NOSET);
					else
						Cvar_FullSet ("game2015", "0", CVAR_SERVERINFO|CVAR_NOSET);
					FS_ExecAutoexec ();

					Com_sprintf(cfgExecString, sizeof(cfgExecString), "exec %s\n", cfg_default->string);
					Cbuf_AddText (cfgExecString); /* FS: If we switch game modes dynamically load that dirs config */
				}
			}
			return var;
		}
	}
	else
	{
		free(var->latched_string);
		var->latched_string = NULL;
	}
	

	if (!strcmp(value, var->string))
		return var;		// not changed

	var->modified = true;

	if (var->flags & CVAR_USERINFO)
		userinfo_modified = true;	// transmit at next oportunity

	free(var->string);	// free the old value string

	var->string = strdup(value);
	var->value = Sinatof (var->string);
	var->intValue = atoi(var->string); /* FS: So we don't need to cast shit all the time */

	return var;
}

/*
============
Cvar_ForceSet
============
*/
cvar_t *Cvar_ForceSet (const char *var_name, const char *value)
{
	return Cvar_Set2 (var_name, value, true);
}

/*
============
Cvar_Set
============
*/
cvar_t *Cvar_Set (const char *var_name, const char *value)
{
	return Cvar_Set2 (var_name, value, false);
}

/*
============
Cvar_FullSet
============
*/
cvar_t *Cvar_FullSet (const char *var_name, const char *value, int flags)
{
	cvar_t	*var;

	var = Cvar_FindVar (var_name);
	if (!var)
	{	// create it
		return Cvar_Get (var_name, value, flags);
	}

	var->modified = true;

	if (var->flags & CVAR_USERINFO)
		userinfo_modified = true;	// transmit at next oportunity

	free (var->string);	// free the old value string

	var->string = strdup(value);
	var->value = Sinatof (var->string);
	var->intValue = atoi(var->string); /* FS: So we don't need to cast shit all the time */
	var->flags = flags;

	return var;
}

/*
============
Cvar_SetValue
============
*/
void Cvar_SetValue (const char *var_name, float value)
{
	char	val[32];

	if (value == (int)value)
		Com_sprintf (val, sizeof(val), "%i",(int)value);
	else
		Com_sprintf (val, sizeof(val), "%f",value);
	Cvar_Set (var_name, val);
}


/*
============
Cvar_GetLatchedVars

Any variables with latched values will now be updated
============
*/
void Cvar_GetLatchedVars (void)
{
	cvar_t	*var;

	for (var = cvar_vars ; var ; var = var->next)
	{
		if (!var->latched_string)
			continue;
		free (var->string);
		var->string = var->latched_string;
		var->latched_string = 0;
		var->value = Sinatof(var->string);
		var->intValue = atoi(var->string); /* FS: So we don't need to cast shit all the time */
      if (!strcmp(var->name, "game"))
      {
         FS_SetGamedir (var->string);
         FS_ExecAutoexec ();
      }
	}
}

/*
============
Cvar_Command

Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command (void)
{
	cvar_t			*v;

// check variables
	v = Cvar_FindVar (Cmd_Argv(0));
	if (!v)
		return false;

// perform a variable print or set
	if (!strcmp(v->name, "developer")) /* FS: Special case for showing enabled flags */
	{
		if(strlen(Cmd_Argv(1)) > 0)
			Cvar_Set(developer->name, Cmd_Argv(1));
		Cvar_ParseDeveloperFlags();
		return true;
	}

	if (Cmd_Argc() == 1)
	{
		if ( (v->flags & CVAR_LATCH) && v->latched_string)
			Com_Printf ("\"%s\" is \"%s\", Default: \"%s\", Latched to: \"%s\"\n", v->name, v->string, v->defaultValue, v->latched_string);
		else
			Com_Printf ("\"%s\" is \"%s\", Default: \"%s\"\n", v->name, v->string, v->defaultValue);

		/* FS: cvar descriptions */
		/* FS: Always show it for con_show_description so we know what it does */
		if (v->description) {
		    if (con_show_description->intValue || v == con_show_description)
			Com_Printf("Description: %s\n", v->description);
		}

		return true;
	}

	Cvar_Set (v->name, Cmd_Argv(1));
	return true;
}

/*
============
Cvar_Negate_f
============
*/
static void Cvar_Negate_f (void)
{
	int c;

	c = Cmd_Argc();
	if (c != 2)
	{
		Com_Printf ("usage: negate <variable>\n");
		return;
	}

	Cvar_SetValue(Cmd_Argv(1),-Cvar_VariableValue(Cmd_Argv(1)));
}


static void Cvar_BitSet_f (void)
{
	int c;
	int bits = 0;
	int bitset = 0;

	c = Cmd_Argc();
	if (c != 3)
	{
		Com_Printf ("usage: bitset <bitnum> <variable>\n");
		return;
	}

	bits = Cvar_VariableValue( Cmd_Argv(2) );
	bitset = (1<<(atoi(Cmd_Argv(1))));
	bits |= bitset;
	Cvar_SetValue(Cmd_Argv(2),bits);
}

static void Cvar_BitClear_f (void)
{
	int c;
	int bits = 0;
	int bitset = 0;

	c = Cmd_Argc();
	if (c != 3)
	{
		Com_Printf ("usage: bitclear <bitnum> <variable>\n");
		return;
	}

	bits = Cvar_VariableValue( Cmd_Argv(2) );
	bitset = (1<<(atoi(Cmd_Argv(1))));
	bits &= ~bitset;
	Cvar_SetValue(Cmd_Argv(2),bits);
}

// 2015 code 
// allows displaying a help text box when an item is changed
static void Cvar_DisplayText_f(void)
{
	int c, i, displaytime;
	char layout[2048];

	c = Cmd_Argc();
	if (c < 4)
	{
		Com_Printf ("usage: displaytext <time> <layout string> <text...>\n");

		for(i = 0; i < c; i++)
			Com_Printf ("       arg %i: %s\n", i, Cmd_Argv(i));

		return;
	}

	// get the text display time
	displaytime = atoi(Cmd_Argv(1));

	// add the format string to the layout string
	sprintf(layout, "%s \"", Cmd_Argv(2));

	// add all the lines of text
	i = 3;
	while(i < c)
	{
		// we're doing multiple lines of text here
		if(i > 3)
			strcat(layout, "\n");

		// tack this line of text only the layout
		strcat(layout, Cmd_Argv(i));

		i++;
	}

	// close off the end of the layout string
	strcat(layout, "\"");

#ifndef DEDICATED_ONLY
	CONSOLE_ScreenPrint(layout, displaytime);
#endif
}

/*
============
Cvar_Set_f

Allows setting and defining of arbitrary cvars from console
============
*/
static void Cvar_Set_f (void)
{
	int		c;
	int		flags;

	c = Cmd_Argc();
	if (c != 3 && c != 4)
	{
		Com_Printf ("usage: set <variable> <value> [u / s]\n");
		return;
	}

	if (c == 4)
	{
		if (!strcmp(Cmd_Argv(3), "u"))
			flags = CVAR_USERINFO;
		else if (!strcmp(Cmd_Argv(3), "s"))
			flags = CVAR_SERVERINFO;
		else
		{
			Com_Printf ("flags can only be 'u' or 's'\n");
			return;
		}
		Cvar_FullSet (Cmd_Argv(1), Cmd_Argv(2), flags);
	}
	else
		Cvar_Set (Cmd_Argv(1), Cmd_Argv(2));
}


/*
============
Cvar_WriteVariables

Appends lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
void Cvar_WriteVariables (char *path)
{
	cvar_t	*var;
	char	buffer[1024];
	FILE	*f;

	f = fopen (path, "a");
	for (var = cvar_vars ; var ; var = var->next)
	{
		if (var->flags & CVAR_ARCHIVE)
		{
			Com_sprintf (buffer, sizeof(buffer), "set %s \"%s\"\n", var->name, var->string);
			fprintf (f, "%s", buffer);
		}
	}
	fclose (f);
}

/*
============
Cvar_List_f

============
*/
static void Cvar_List_f (void)
{
	cvar_t	*var;
	qboolean endReached = false; /* FS: For the filter */
	qboolean endIsAMatch = false; /* FS: For the filter */
	int		i;

	i = 0;

	for (var = cvar_vars ; var ; var = var->next, i++)
	{
		/* FS: Filter it */
		if(Cmd_Argc() > 1)
		{
			if (i == 0)
				Com_Printf("Listing matches for '%s'...\n", Cmd_Argv(1));
			while( !strstr(var->name, Cmd_Argv(1)) && var->next)
			{
				if(var->next)
					var = var->next;
			}

			if(!var->next)
			{
				if(strstr(var->name, Cmd_Argv(1))) /* FS: The last one in the search actually matches, so don't break out */
				{
					endIsAMatch = true;
				}

				endReached = true;
			}
		}

		if(endReached && !endIsAMatch) /* FS: We're at the end, and it's not a match to the filter so bust out. */
		{
			break;
		}

		if (var->flags & CVAR_ARCHIVE)
			Com_Printf ("*");
		else
			Com_Printf (" ");
		if (var->flags & CVAR_USERINFO)
			Com_Printf ("U");
		else
			Com_Printf (" ");
		if (var->flags & CVAR_SERVERINFO)
			Com_Printf ("S");
		else
			Com_Printf (" ");
		if (var->flags & CVAR_NOSET)
			Com_Printf ("-");
		else if (var->flags & CVAR_LATCH)
			Com_Printf ("L");
		else
			Com_Printf (" ");
		if (var->description)
			Com_Printf ("D");
		else
			Com_Printf (" ");

		if ( (var->flags & CVAR_LATCH) && var->latched_string)
			Com_Printf ("\"%s\" is \"%s\", Default: \"%s\", Latched to: \"%s\"\n", var->name, var->string, var->defaultValue, var->latched_string);
		else
			Com_Printf (" %s \"%s\", Default: \"%s\"\n", var->name, var->string, var->defaultValue);
	}
	Com_Printf("Legend: * Archive. U Userinfo. S Serverinfo. - Write Protected. L Latched. D Containts a Help Description.\n"); /* FS: Added a legend */
	Com_Printf ("%i cvars\n", i);
}


qboolean userinfo_modified;


static char	*Cvar_BitInfo (int bit)
{
	static char	info[MAX_INFO_STRING];
	cvar_t	*var;

	info[0] = 0;

	for (var = cvar_vars ; var ; var = var->next)
	{
		if (var->flags & bit)
			Info_SetValueForKey (info, var->name, var->string);
	}
	return info;
}

// returns an info string containing all the CVAR_USERINFO cvars
char	*Cvar_Userinfo (void)
{
	return Cvar_BitInfo (CVAR_USERINFO);
}

// returns an info string containing all the CVAR_SERVERINFO cvars
char	*Cvar_Serverinfo (void)
{
	return Cvar_BitInfo (CVAR_SERVERINFO);
}

/*
============
Cvar_Init

Reads in all archived cvars
============
*/
void Cvar_Init (void)
{
	con_show_description = Cvar_Get("con_show_description", "1", CVAR_ARCHIVE); /* FS */
	con_show_description->description = "Toggle descriptions for CVARs.";
	con_show_dev_flags = Cvar_Get ("con_show_dev_flags", "1", CVAR_ARCHIVE); /* FS */
	con_show_dev_flags->description = "Show toggled developer flags when using the developer CVAR.";

	Cmd_AddCommand ("set", Cvar_Set_f);
	Cmd_AddCommand ("cvarlist", Cvar_List_f);
	Cmd_AddCommand ("negate", Cvar_Negate_f);
	Cmd_AddCommand ("bitset", Cvar_BitSet_f);
	Cmd_AddCommand ("bitclear", Cvar_BitClear_f);
	// 2015 code 
	Cmd_AddCommand ("displaytext", Cvar_DisplayText_f);

	Cmd_AddCommand ("togglecvar", Cvar_Toggle_f); /* FS */
	Cmd_AddCommand ("forcecvar", Cvar_Force_f); /* FS */
	Cmd_AddCommand ("resetcvar", Cvar_Reset_f); /* FS */
}

static cvar_t *Cvar_IsNoset (const char *var_name) /* FS: Make sure this isn't a NOSET CVAR! */
{
	cvar_t	*var;
	
	for (var=cvar_vars ; var ; var=var->next)
	{
		if( var->name )
		{
			if (!strcmp (var_name, var->name))
			{
				if (var->flags & CVAR_NOSET)
				{
					return var;
				}
			}
		}
	}
	return NULL;
}

void Cvar_Toggle_f (void) /* FS: Added */
{
	if(Cmd_Argc() == 2 && Cmd_Argv(1))
	{
		const char *cvar_name = Cmd_Argv(1);
		float cur_value;

		if (cvar_name == NULL || cvar_name[0] == '\0' || Cvar_FindVar(cvar_name) == NULL) /* FS: Check for NULL sillies */
		{
			Com_Printf("%s not found!\n", Cmd_Argv(1));
			return;
		}

		if (Cvar_IsNoset(cvar_name))
		{
			Com_Printf("%s is write protected.\n", cvar_name);
			return;
		}

	    //get the value of the cvar.
		cur_value = Cvar_VariableValue(cvar_name);

		//set it to the opposite value.
		if (cur_value != 0.0f)
			Cvar_ForceSet(cvar_name, "0");
		else
			Cvar_ForceSet(cvar_name, "1");

		Com_Printf("%s set to %0.0f\n", cvar_name, Cvar_VariableValue(cvar_name));
	}
	else
	{
		Com_Printf("USAGE: togglecvar <console variable>\n");
		return;
	}
}

void Cvar_Force_f (void) /* FS: Added */
{
	if(Cmd_Argc() == 3 && Cmd_Argv(1))
	{
		const char *cvar_name = Cmd_Argv(1);
		const char *cvar_value;

		if (cvar_name == NULL || cvar_name[0] == '\0' || Cvar_FindVar(cvar_name) == NULL) /* FS: Check for NULL sillies */
		{
			Com_Printf("%s not found!\n", Cmd_Argv(1));
			return;
		}

		if (Cvar_Never_Reset_Cmds(cvar_name))
		{
			Com_Printf("Error: %s cannot be changed from console!\n", cvar_name);
			return;
		}

		cvar_value = Cmd_Argv(2);

		Cvar_ForceSet(cvar_name, cvar_value);

		Com_Printf("%s set to %s\n", cvar_name, cvar_value);
	}
	else
	{
		Com_Printf("USAGE: forcecvar <console variable> <value>\n");
		return;
	}
}

qboolean Cvar_Never_Reset_Cmds(const char *var_name) /* FS: Tired of copying CVARs everywhere */
{
	if(!strcmp(var_name, "dedicated"))
		return true;
	if(!strcmp(var_name, "port"))
		return true;
	if(!strcmp(var_name, "qport"))
		return true;
	if(!strcmp(var_name, "version"))
		return true;
	if(!strcmp(var_name, "cfg_default"))
		return true;

	return false;
}

void Cvar_Reset_f (void) /* FS: Reset a CVAR to its default value */
{
	int args;
	cvar_t *var;
	const char *var_name;

	args = Cmd_Argc();

	if (args != 2)
	{
		Com_Printf("usage: resetcvar <variable>.  Resets CVARs to their default values\n");
		return;
	}

	var_name = Cmd_Argv(1);

	if (Cvar_Never_Reset_Cmds(var_name))
	{
		Com_Printf("Error: you can not reset this value: %s!\n", var_name);
		return;
	}

	var = Cvar_FindVar(var_name);

	if (!var)
	{
		Com_Printf("Error: %s is not a valid CVAR!\n", var_name);
		return;
	}

	Com_Printf("Resetting %s to default value of %s, Flags Value: %i\n", var_name, var->defaultValue, var->defaultFlags);
	var->flags = var->defaultFlags;
	Cvar_ForceSet(var_name, var->defaultValue);
}

/*
============
Cvar_Destroy

Dispose of all the cvars
============
*/
void Cvar_Destroy(void)
{
	cvar_t *var;

	var=cvar_vars;
	if (var) {
		do {
			cvar_t *next;
			next = var->next;
			free(var->name);
			free(var->string);
			free(var->latched_string);
			Z_Free(var);
			var = next;
		} while (var);
	}
	cvar_vars = 0;
}

static void Cvar_ParseDeveloperFlags (void) /* FS: Special stuff for showing all the dev flags */
{
	extern cvar_t	*developer;

	Com_Printf("\"%s\" is \"%s\", Default: \"%s\"\n", developer->name, developer->string, developer->defaultValue);

	if(developer->intValue > 0)
	{
		unsigned devFlags = 0;

		if(strstr(developer->string, "+"))
		{
			char cvarString[1024];
			char *tokenPtr;
			char seperators[] = "+";
			char *developerCvarToken;

			Com_sprintf(cvarString, sizeof(cvarString), "%s", developer->string);
			developerCvarToken = strtok_r(cvarString, seperators, &tokenPtr);
			while (developerCvarToken != NULL)
			{
				int devFlagsToAdd = atoi(developerCvarToken);

				if(devFlagsToAdd == 1) /* FS: 1 adds everything except verbose */
				{
					devFlags += 16383;
				}
				else
				{
					devFlags += devFlagsToAdd;
				}
				developerCvarToken = strtok_r(NULL, seperators, &tokenPtr);
			}
			Com_sprintf(cvarString, sizeof(cvarString), "%u", devFlags);
			Cvar_ForceSet("developer", cvarString);
		}
		else
		{
			if(developer->intValue == 1)
				devFlags = 16383;
			else
				devFlags = (unsigned) developer->value;
		}

		Com_Printf("Toggled flags:\n");
		if(devFlags & DEVELOPER_MSG_STANDARD)
			Com_Printf(" * Standard messages - 2\n");
		if(devFlags & DEVELOPER_MSG_SOUND)
			Com_Printf(" * Sound messages - 4\n");
		if(devFlags & DEVELOPER_MSG_NET)
			Com_Printf(" * Network messages - 8\n");
		if(devFlags & DEVELOPER_MSG_IO)
			Com_Printf(" * File IO messages - 16\n");
		if(devFlags & DEVELOPER_MSG_GFX)
			Com_Printf(" * Graphics Renderer messages - 32\n");
		if(devFlags & DEVELOPER_MSG_GAME)
			Com_Printf(" * Game DLL messages - 64\n");
		if(devFlags & DEVELOPER_MSG_PMOVE)
			Com_Printf(" * PMove messages - 128\n");
		if(devFlags & DEVELOPER_MSG_SERVER)
			Com_Printf(" * Server messages - 256\n");
		if(devFlags & DEVELOPER_MSG_CLIENT)
			Com_Printf(" * Client messages - 512\n");
		if(devFlags & DEVELOPER_MSG_SAVE)
			Com_Printf(" * Save system messages - 1024\n");
		if(devFlags & DEVELOPER_MSG_SINMDL)
			Com_Printf(" * SiN MDL messages - 2048\n");
		if(devFlags & DEVELOPER_MSG_SINCMDS)
			Com_Printf(" * SiN CMD messages - 4096\n");
		if(devFlags & DEVELOPER_MSG_CONSOLE3D)
			Com_Printf(" * Console3D messages - 8192\n");
		if(devFlags & DEVELOPER_MSG_PMOVE_VERBOSE)
			Com_Printf(" * Verbose PMove messages - 16384\n");
		if(devFlags & DEVELOPER_MSG_CONSOLE_VERBOSE)
			Com_Printf(" * Verbose Console messages - 32768\n");
		if(devFlags & DEVELOPER_MSG_SURFACE_VERBOSE)
			Com_Printf(" * Verbose Surface messages - 65536\n");
		if(devFlags & DEVELOPER_MSG_GENERIC_VERBOSE)
			Com_Printf(" * Verbose Generic messages - 131072\n");
		if(devFlags & DEVELOPER_MSG_GAMESPY)
			Com_Printf(" * Verbose Gamespy messages - 262144\n");
	}
	else
	{
		Com_Printf("Description: %s\n", developer->description);
	}
}

void Cvar_SetDescription (const char *var_name, const char *description) /* FS: Set descriptions for CVARs */
{
	cvar_t	*var = Cvar_FindVar (var_name);
	if (!var) {
		Com_DPrintf(DEVELOPER_MSG_STANDARD, "Error: Can't set description for %s!\n", var_name);
		return;
	}
	if (!description) {
		Com_DPrintf(DEVELOPER_MSG_STANDARD, "NULL description for %s\n", var_name);
	}
	if (var->description) {
		free(var->description);
		var->description = NULL;
	}
	var->description = strdup(description);
}
