示例#1
0
qboolean Q_stricmpext( const char *pattern, const char *text )
{
    char	c;

    while(( c = *pattern++ ) != '\0' )
    {
        switch( c )
        {
        case '?':
            if( *text++ == '\0' )
                return false;
            break;
        case '\\':
            if( Q_tolower( *pattern++ ) != Q_tolower( *text++ ))
                return false;
            break;
        case '*':
            return Q_starcmp( pattern, text );
        default:
            if( Q_tolower( c ) != Q_tolower( *text++ ))
                return false;
        }
    }
    return true;
}
示例#2
0
/*
=====================================
Cmd_GetCdCommands

Prints or complete CD command name
=====================================
*/
qboolean Cmd_GetCdCommands( const char *s, char *completedname, int length )
{
	int i, numcdcommands;
	string	cdcommands[8];
	string	matchbuf;

	const char *cd_command[] =
	{
	"info",
	"loop",
	"off",
	"on",
	"pause",
	"play",
	"resume",
	"stop",
	};

	// compare CD command list with current keyword
	for( i = 0, numcdcommands = 0; i < 8; i++ )
	{
		if(( *s == '*' ) || !Q_strnicmp( cd_command[i], s, Q_strlen( s )))
			Q_strcpy( cdcommands[numcdcommands++], cd_command[i] );
	}

	if( !numcdcommands ) return false;
	Q_strncpy( matchbuf, cdcommands[0], MAX_STRING );
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( numcdcommands == 1 ) return true;

	for( i = 0; i < numcdcommands; i++ )
	{
		Q_strncpy( matchbuf, cdcommands[i], MAX_STRING );
		Msg( "%16s\n", matchbuf );
	}

	Msg( "\n^3 %i commands found.\n", numcdcommands );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}
	return true;
}
示例#3
0
/*
=====================================
Cmd_GetTexturemodes

Prints or complete sound filename
=====================================
*/
qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length )
{
	int	i, numtexturemodes;
	string	texturemodes[6];	// keep an actual ( sizeof( gl_texturemode) / sizeof( gl_texturemode[0] ))
	string	matchbuf;

	const char *gl_texturemode[] =
	{
	"GL_LINEAR",
	"GL_LINEAR_MIPMAP_LINEAR",
	"GL_LINEAR_MIPMAP_NEAREST",
	"GL_NEAREST",
	"GL_NEAREST_MIPMAP_LINEAR",
	"GL_NEAREST_MIPMAP_NEAREST",
	};

	// compare gamelist with current keyword
	for( i = 0, numtexturemodes = 0; i < 6; i++ )
	{
		if(( *s == '*' ) || !Q_strnicmp( gl_texturemode[i], s, Q_strlen( s )))
			Q_strcpy( texturemodes[numtexturemodes++], gl_texturemode[i] ); 
	}

	if( !numtexturemodes ) return false;
	Q_strncpy( matchbuf, gl_texturemode[0], MAX_STRING ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( numtexturemodes == 1 ) return true;

	for( i = 0; i < numtexturemodes; i++ )
	{
		Q_strncpy( matchbuf, texturemodes[i], MAX_STRING ); 
		Msg( "%16s\n", matchbuf );
	}

	Msg( "\n^3 %i filters found.\n", numtexturemodes );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}
	return true;
}
示例#4
0
文件: common.cpp 项目: emileb/XashXT
/*
=================
COM_HashKey

returns hash key for string
=================
*/
unsigned int COM_HashKey( const char *string, unsigned int hashSize )
{
	unsigned int	hashKey = 0;

	for( int i = 0; string[i]; i++ )
		hashKey = (hashKey + i) * 37 + Q_tolower( string[i] );

	return (hashKey % hashSize);
}
示例#5
0
文件: q_shared.c 项目: Bad-ptr/q2pro
int Q_strcasecmp( const char *s1, const char *s2 ) {
    int        c1, c2;

    do {
        c1 = *s1++;
        c2 = *s2++;

        if( c1 != c2 ) {
            c1 = Q_tolower( c1 );
            c2 = Q_tolower( c2 );
            if( c1 < c2 )
                return -1;
            if( c1 > c2 )
                return 1;        /* strings not equal */
        }
    } while( c1 );

    return 0;        /* strings are equal */
}
示例#6
0
/*
=====================================
Cmd_GetSoundList

Prints or complete sound filename
=====================================
*/
qboolean Cmd_GetSoundList( const char *s, char *completedname, int length )
{
	search_t		*t;
	string		matchbuf;
	int		i, numsounds;
	const char	*snddir = "sound/"; // constant

	t = FS_Search( va( "%s%s*.*", snddir, s ), true, false );
	if( !t ) return false;

	Q_strncpy( matchbuf, t->filenames[0] + Q_strlen( snddir ), MAX_STRING ); 
	FS_StripExtension( matchbuf ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for(i = 0, numsounds = 0; i < t->numfilenames; i++)
	{
		const char *ext = FS_FileExtension( t->filenames[i] ); 

		if( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mp3" ));
		else continue;

		Q_strncpy( matchbuf, t->filenames[i] + Q_strlen(snddir), MAX_STRING ); 
		FS_StripExtension( matchbuf );
		Msg( "%16s\n", matchbuf );
		numsounds++;
	}

	Msg( "\n^3 %i sounds found.\n", numsounds );
	Mem_Free( t );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}

	return true;
}
示例#7
0
uint32 Com_HashGeneric (const String &name, const sint32 hashSize)
{
	uint32 hashValue = 0;
	for (uint32 i = 0; name[i]; i++)
	{
		sint32 ch = Q_tolower(name[i]);
		hashValue = hashValue * 33 + ch;
	}

	return (hashValue + (hashValue >> 5)) & (hashSize-1);
}
示例#8
0
/*
================
Com_HashGeneric
hashSize MUST be a power of two!

From EGL, used to make table lookups
a bit faster.
================
*/
uint32 Com_HashGeneric (const char *name, const sint32 hashSize)
{
	uint32 hashValue = 0;
	for ( ; *name; )
	{
		sint32 ch = Q_tolower(*(name++));
		hashValue = hashValue * 33 + ch;
	}

	return (hashValue + (hashValue >> 5)) & (hashSize-1);
}
示例#9
0
/*
=====================================
Cmd_GetGameList

Prints or complete gamedir name
=====================================
*/
qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
{
	int	i, numgamedirs;
	string	gamedirs[MAX_MODS];
	string	matchbuf;

	// stand-alone games doesn't have cmd "game"
	if( !Cmd_Exists( "game" ))
		return false;

	// compare gamelist with current keyword
	for( i = 0, numgamedirs = 0; i < SI.numgames; i++ )
	{
		if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, Q_strlen( s )))
			Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder ); 
	}

	if( !numgamedirs ) return false;
	Q_strncpy( matchbuf, gamedirs[0], MAX_STRING ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( numgamedirs == 1 ) return true;

	for( i = 0; i < numgamedirs; i++ )
	{
		Q_strncpy( matchbuf, gamedirs[i], MAX_STRING ); 
		Msg( "%16s\n", matchbuf );
	}

	Msg( "\n^3 %i games found.\n", numgamedirs );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}
	return true;
}
示例#10
0
/*
=====================================
Cmd_GetMusicList

Prints or complete background track filename
=====================================
*/
qboolean Cmd_GetMusicList( const char *s, char *completedname, int length )
{
	search_t		*t;
	string		matchbuf;
	int		i, numtracks;

	t = FS_Search( va( "media/%s*.*", s ), true, false );
	if( !t ) return false;

	FS_FileBase( t->filenames[0], matchbuf ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for(i = 0, numtracks = 0; i < t->numfilenames; i++)
	{
		const char *ext = FS_FileExtension( t->filenames[i] ); 

		if( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mp3" ));
		else continue;

		FS_FileBase( t->filenames[i], matchbuf );
		Msg( "%16s\n", matchbuf );
		numtracks++;
	}

	Msg( "\n^3 %i soundtracks found.\n", numtracks );
	Mem_Free(t);

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}
	return true;
}
示例#11
0
文件: q_shared.c 项目: Bad-ptr/q2pro
int Q_strncasecmp( const char *s1, const char *s2, size_t n ) {
    int        c1, c2;

    do {
        c1 = *s1++;
        c2 = *s2++;

        if( !n-- )
            return 0;        /* strings are equal until end point */

        if( c1 != c2 ) {
            c1 = Q_tolower( c1 );
            c2 = Q_tolower( c2 );
            if( c1 < c2 )
                return -1;
            if( c1 > c2 )
                return 1;        /* strings not equal */
        }
    } while( c1 );

    return 0;        /* strings are equal */
}
示例#12
0
/*
=====================================
Cmd_GetConfigList

Prints or complete .cfg filename
=====================================
*/
qboolean Cmd_GetConfigList( const char *s, char *completedname, int length )
{
	search_t		*t;
	string		matchbuf;
	int		i, numconfigs;

	t = FS_Search( va( "%s*.cfg", s ), true, false );
	if( !t ) return false;

	Q_strncpy( matchbuf, t->filenames[0], 256 );
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for( i = 0, numconfigs = 0; i < t->numfilenames; i++ )
	{
		const char *ext = FS_FileExtension( t->filenames[i] );

		if( Q_stricmp( ext, "cfg" )) continue;
		Q_strncpy( matchbuf, t->filenames[i], 256 );
		Msg( "%16s\n", matchbuf );
		numconfigs++;
	}

	Msg( "\n^3 %i configs found.\n", numconfigs );
	Mem_Free( t );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}

	return true;
}
示例#13
0
/*
=====================================
Cmd_GetItemsList

Prints or complete item classname (weapons only)
=====================================
*/
qboolean Cmd_GetItemsList( const char *s, char *completedname, int length )
{
	search_t		*t;
	string		matchbuf;
	int		i, numitems;

	if( !clgame.itemspath[0] ) return false; // not in game yet
	t = FS_Search( va( "%s/%s*.txt", clgame.itemspath, s ), true, false );
	if( !t ) return false;

	FS_FileBase( t->filenames[0], matchbuf ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for(i = 0, numitems = 0; i < t->numfilenames; i++)
	{
		const char *ext = FS_FileExtension( t->filenames[i] ); 

		if( Q_stricmp( ext, "txt" )) continue;
		FS_FileBase( t->filenames[i], matchbuf );
		Msg( "%16s\n", matchbuf );
		numitems++;
	}

	Msg( "\n^3 %i items found.\n", numitems );
	Mem_Free( t );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}
	return true;
}
示例#14
0
/*
=====================================
Cmd_GetSavesList

Prints or complete savegame filename
=====================================
*/
qboolean Cmd_GetSavesList( const char *s, char *completedname, int length )
{
	search_t		*t;
	string		matchbuf;
	int		i, numsaves;

	t = FS_Search( va( "save/%s*.sav", s ), true, true );	// lookup only in gamedir
	if( !t ) return false;

	FS_FileBase( t->filenames[0], matchbuf ); 
	if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for( i = 0, numsaves = 0; i < t->numfilenames; i++ )
	{
		const char *ext = FS_FileExtension( t->filenames[i] ); 

		if( Q_stricmp( ext, "sav" )) continue;
		FS_FileBase( t->filenames[i], matchbuf );
		Msg( "%16s\n", matchbuf );
		numsaves++;
	}

	Msg( "\n^3 %i saves found.\n", numsaves );
	Mem_Free( t );

	// cut shortestMatch to the amount common with s
	if( completedname && length )
	{
		for( i = 0; matchbuf[i]; i++ )
		{
			if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
				completedname[i] = 0;
		}
	}

	return true;
}
示例#15
0
文件: stringlib.cpp 项目: FWGS/XashXT
char *Q_stristr( const char *string, const char *string2 )
{
	int	c, len;

	if( !string || !string2 ) return NULL;

	c = Q_tolower( *string2 );
	len = Q_strlen( string2 );

	while( string )
	{
		for( ; *string && Q_tolower( *string ) != c; string++ );

		if( *string )
		{
			if( !Q_strnicmp( string, string2, len ))
				break;
			string++;
		}
		else return NULL;
	}
	return (char *)string;
}
示例#16
0
文件: keys.c 项目: jayschwa/q2pro
/*
===================
Key_StringToKeynum

Returns a key number to be used to index keybindings[] by looking at
the given string.  Single ascii characters return themselves, while
the K_* names are matched up.
===================
*/
int Key_StringToKeynum(const char *str)
{
    const keyname_t *kn;

    if (!str || !str[0])
        return -1;
    if (!str[1])
        return Q_tolower(str[0]);

    for (kn = keynames; kn->name; kn++) {
        if (!Q_stricmp(str, kn->name))
            return kn->keynum;
    }
    return -1;
}
示例#17
0
static qboolean Q_starcmp( const char *pattern, const char *text )
{
    char		c, c1;
    const char	*p = pattern, *t = text;

    while(( c = *p++ ) == '?' || c == '*' )
    {
        if( c == '?' && *t++ == '\0' )
            return false;
    }

    if( c == '\0' ) return true;

    for( c1 = (( c == '\\' ) ? *p : c ); ; )
    {
        if( Q_tolower( *t ) == c1 && Q_stricmpext( p - 1, t ))
            return true;
        if( *t++ == '\0' ) return false;
    }
}
示例#18
0
/*
=============
COM_LoadFileForMe

=============
*/
byte* COM_LoadFileForMe( const char *filename, int *pLength )
{
	string	name;
	int	i;

	if( !filename || !*filename )
	{
		if( pLength ) *pLength = 0;
		return NULL;
	}

	// replace all backward slashes
	for( i = 0; i < Q_strlen( filename ); i++ )
	{
		if( filename[i] == '\\' ) name[i] = '/';
		else name[i] = Q_tolower( filename[i] );
	}
	name[i] = '\0';

	return FS_LoadFile( name, pLength, false );
}
示例#19
0
文件: prompt.c 项目: Jenco420/q2pro
/*
====================
Prompt_CompleteCommand
====================
*/
void Prompt_CompleteCommand(commandPrompt_t *prompt, qboolean backslash)
{
    inputField_t *inputLine = &prompt->inputLine;
    char *text, *partial, *s;
    int i, argc, currentArg, argnum;
    size_t size, len, pos;
    char *first, *last;
    genctx_t ctx;
    char *matches[MAX_MATCHES], *sortedMatches[MAX_MATCHES];
    int numCommands, numCvars, numAliases;

    text = inputLine->text;
    size = inputLine->maxChars + 1;
    pos = inputLine->cursorPos;

    // prepend backslash if missing
    if (backslash) {
        if (inputLine->text[0] != '\\' && inputLine->text[0] != '/') {
            memmove(inputLine->text + 1, inputLine->text, size - 1);
            inputLine->text[0] = '\\';
        }
        text++;
        size--;
        pos--;
    }

    // parse the input line into tokens
    Cmd_TokenizeString(text, qfalse);

    argc = Cmd_Argc();

    // determine absolute argument number to be completed
    currentArg = Cmd_FindArgForOffset(pos);
    if (currentArg == argc - 1 && Cmd_WhiteSpaceTail()) {
        // start completing new argument if command line has trailing whitespace
        currentArg++;
    }

    // determine relative argument number to be completed
    argnum = 0;
    for (i = 0; i < currentArg; i++) {
        s = Cmd_Argv(i);
        argnum++;
        if (*s == ';') {
            // semicolon starts a new command
            argnum = 0;
        }
    }

    // get the partial argument string to be completed
    partial = Cmd_Argv(currentArg);
    if (*partial == ';') {
        // semicolon starts a new command
        currentArg++;
        partial = Cmd_Argv(currentArg);
        argnum = 0;
    }

    // generate matches
    memset(&ctx, 0, sizeof(ctx));
    ctx.partial = partial;
    ctx.length = strlen(partial);
    ctx.argnum = currentArg;
    ctx.matches = matches;
    ctx.size = MAX_MATCHES;

    if (argnum) {
        // complete a command/cvar argument
        Com_Generic_c(&ctx, argnum);
        numCommands = numCvars = numAliases = 0;
    } else {
        // complete a command/cvar/alias name
        Cmd_Command_g(&ctx);
        numCommands = ctx.count;

        Cvar_Variable_g(&ctx);
        numCvars = ctx.count - numCommands;

        Cmd_Alias_g(&ctx);
        numAliases = ctx.count - numCvars - numCommands;
    }

    if (!ctx.count) {
        pos = strlen(inputLine->text);
        prompt->tooMany = qfalse;
        goto finish2; // nothing found
    }

    pos = Cmd_ArgOffset(currentArg);
    text += pos;
    size -= pos;

    // append whitespace since Cmd_TokenizeString eats it
    if (currentArg == argc && Cmd_WhiteSpaceTail()) {
        *text++ = ' ';
        pos++;
        size--;
    }

    if (ctx.count == 1) {
        // we have finished completion!
        s = Cmd_RawArgsFrom(currentArg + 1);
        if (needs_quotes(matches[0])) {
            pos += Q_concat(text, size, "\"", matches[0], "\" ", s, NULL);
        } else {
            pos += Q_concat(text, size, matches[0], " ", s, NULL);
        }
        pos++;
        prompt->tooMany = qfalse;
        goto finish1;
    }

    if (ctx.count > com_completion_treshold->integer && !prompt->tooMany) {
        prompt->printf("Press TAB again to display all %d possibilities.\n", ctx.count);
        pos = strlen(inputLine->text);
        prompt->tooMany = qtrue;
        goto finish1;
    }

    prompt->tooMany = qfalse;

    // sort matches alphabethically
    for (i = 0; i < ctx.count; i++) {
        sortedMatches[i] = matches[i];
    }
    qsort(sortedMatches, ctx.count, sizeof(sortedMatches[0]),
          ctx.ignorecase ? SortStricmp : SortStrcmp);

    // copy matching part
    first = sortedMatches[0];
    last = sortedMatches[ctx.count - 1];
    len = 0;
    do {
        if (*first != *last) {
            if (!ctx.ignorecase || Q_tolower(*first) != Q_tolower(*last)) {
                break;
            }
        }
        text[len++] = *first;
        if (len == size - 1) {
            break;
        }

        first++;
        last++;
    } while (*first);

    text[len] = 0;
    pos += len;
    size -= len;

    // copy trailing arguments
    if (currentArg + 1 < argc) {
        s = Cmd_RawArgsFrom(currentArg + 1);
        pos += Q_concat(text + len, size, " ", s, NULL);
    }

    pos++;

    prompt->printf("]\\%s\n", Cmd_ArgsFrom(0));
    if (argnum) {
        goto multi;
    }

    switch (com_completion_mode->integer) {
    case 0:
        // print in solid list
        for (i = 0; i < ctx.count; i++) {
            prompt->printf("%s\n", sortedMatches[i]);
        }
        break;
    case 1:
multi:
        // print in multiple columns
        Prompt_ShowMatches(prompt, sortedMatches, 0, ctx.count);
        break;
    case 2:
    default:
        // resort matches by type and print in multiple columns
        Prompt_ShowIndividualMatches(prompt, matches, numCommands, numAliases, numCvars);
        break;
    }

finish1:
    // free matches
    for (i = 0; i < ctx.count; i++) {
        Z_Free(matches[i]);
    }

finish2:
    // move cursor
    if (pos >= inputLine->maxChars) {
        pos = inputLine->maxChars - 1;
    }
    inputLine->cursorPos = pos;
}
示例#20
0
文件: s_vox.c 项目: n00ner/xash3d
//-----------------------------------------------------------------------------
// Purpose: Take a NULL terminated sentence, and parse any commands contained in
//			{}.  The string is rewritten in place with those commands removed.
//
// Input  : *pSentenceData - sentence data to be modified in place
//			sentenceIndex - global sentence table index for any data that is
//							parsed out
//-----------------------------------------------------------------------------
void VOX_ParseLineCommands( char *pSentenceData, int sentenceIndex )
{
    char	tempBuffer[512];
    char	*pNext, *pStart;
    int	length, tempBufferPos = 0;

    if( !pSentenceData )
        return;

    pStart = pSentenceData;

    while( *pSentenceData )
    {
        pNext = ScanForwardUntil( pSentenceData, '{' );

        // find length of "good" portion of the string (not a {} command)
        length = pNext - pSentenceData;
        if( tempBufferPos + length > sizeof( tempBuffer ))
        {
            MsgDev( D_ERROR, "sentence too long!\n" );
            return;
        }

        // Copy good string to temp buffer
        Q_memcpy( tempBuffer + tempBufferPos, pSentenceData, length );

        // move the copy position
        tempBufferPos += length;

        pSentenceData = pNext;

        // skip ahead of the opening brace
        if( *pSentenceData ) pSentenceData++;

        // skip whitespace
        while( *pSentenceData && *pSentenceData <= 32 )
            pSentenceData++;

        // simple comparison of string commands:
        switch( Q_tolower( *pSentenceData ))
        {
        case 'l':
            // all commands starting with the letter 'l' here
            if( !Q_strnicmp( pSentenceData, "len", 3 ))
            {
                g_Sentences[sentenceIndex].length = Q_atof( pSentenceData + 3 );
            }
            break;
        case 0:
        default:
            break;
        }

        pSentenceData = ScanForwardUntil( pSentenceData, '}' );

        // skip the closing brace
        if( *pSentenceData ) pSentenceData++;

        // skip trailing whitespace
        while( *pSentenceData && *pSentenceData <= 32 )
            pSentenceData++;
    }

    if( tempBufferPos < sizeof( tempBuffer ))
    {
        // terminate cleaned up copy
        tempBuffer[tempBufferPos] = 0;

        // copy it over the original data
        Q_strcpy( pStart, tempBuffer );
    }
}
示例#21
0
/*
=====================================
Cmd_GetMapList

Prints or complete map filename
=====================================
*/
qboolean Cmd_GetMapList( const char *s, char *completedname, int length )
{
	search_t		*t;
	file_t		*f;
	string		message;
	string		matchbuf;
	byte		buf[MAX_SYSPATH]; // 1 kb
	int		i, nummaps;

	t = FS_Search( va( "maps/%s*.bsp", s ), true, con_gamemaps->integer );
	if( !t ) return false;

	FS_FileBase( t->filenames[0], matchbuf ); 
	Q_strncpy( completedname, matchbuf, length );
	if( t->numfilenames == 1 ) return true;

	for( i = 0, nummaps = 0; i < t->numfilenames; i++ )
	{
		char		entfilename[CS_SIZE];
		int		ver = -1, mapver = -1, lumpofs = 0, lumplen = 0;
		const char	*ext = FS_FileExtension( t->filenames[i] ); 
		char		*ents = NULL, *pfile;
		qboolean		paranoia = false;
		qboolean		gearbox = false;
			
		if( Q_stricmp( ext, "bsp" )) continue;
		Q_strncpy( message, "^1error^7", sizeof( message ));
		f = FS_Open( t->filenames[i], "rb", con_gamemaps->integer );
	
		if( f )
		{
			dheader_t	*header;
			dextrahdr_t	*hdrext;

			Q_memset( buf, 0, sizeof( buf ));
			FS_Read( f, buf, sizeof( buf ));
			header = (dheader_t *)buf;
			ver = header->version;
                              
			switch( ver )
			{
			case Q1BSP_VERSION:
			case HLBSP_VERSION:
			case XTBSP_VERSION:
				if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && !(header->lumps[LUMP_ENTITIES].filelen % sizeof(dplane_t)))
				{
					lumpofs = header->lumps[LUMP_PLANES].fileofs;
					lumplen = header->lumps[LUMP_PLANES].filelen;
					gearbox = true;
				}
				else
				{
					lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
					lumplen = header->lumps[LUMP_ENTITIES].filelen;
					gearbox = false;
				}
				break;
			}

			if( ver == XTBSP_VERSION )
				hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader31_t ));
			else hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader_t ));

			if( hdrext->id == IDEXTRAHEADER && hdrext->version == EXTRA_VERSION )
				paranoia = true;

			Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
			FS_StripExtension( entfilename );
			FS_DefaultExtension( entfilename, ".ent" );
			ents = FS_LoadFile( entfilename, NULL, true );

			if( !ents && lumplen >= 10 )
			{
				FS_Seek( f, lumpofs, SEEK_SET );
				ents = (char *)Mem_Alloc( host.mempool, lumplen + 1 );
				FS_Read( f, ents, lumplen );
			}

			if( ents )
			{
				// if there are entities to parse, a missing message key just
				// means there is no title, so clear the message string now
				char	token[2048];

				message[0] = 0;
				pfile = ents;

				while(( pfile = COM_ParseFile( pfile, token )) != NULL )
				{
					if( !Q_strcmp( token, "{" )) continue;
					else if(!Q_strcmp( token, "}" )) break;
					else if(!Q_strcmp( token, "message" ))
					{
						// get the message contents
						pfile = COM_ParseFile( pfile, message );
					}
					else if(!Q_strcmp( token, "mapversion" ))
					{
						// get the message contents
						pfile = COM_ParseFile( pfile, token );
						mapver = Q_atoi( token );
					}
				}
				Mem_Free( ents );
			}
		}

		if( f ) FS_Close(f);
		FS_FileBase( t->filenames[i], matchbuf );

		switch( ver )
		{
		case Q1BSP_VERSION:
			if( mapver == 220 ) Q_strncpy( buf, "Half-Life Alpha", sizeof( buf ));
			else Q_strncpy( buf, "Quake", sizeof( buf ));
			break;
		case HLBSP_VERSION:
			if( gearbox ) Q_strncpy( buf, "Blue-Shift", sizeof( buf ));
			else if( paranoia ) Q_strncpy( buf, "Paranoia 2", sizeof( buf ));
			else Q_strncpy( buf, "Half-Life", sizeof( buf ));
			break;
		case XTBSP_VERSION:
			if( paranoia ) Q_strncpy( buf, "Paranoia 2", sizeof( buf ));
			else Q_strncpy( buf, "Xash3D", sizeof( buf ));
			break;
		default:
			Q_strncpy( buf, "??", sizeof( buf ));
			break;
		}

		Msg( "%16s (%s) ^3%s^7\n", matchbuf, buf, message );
		nummaps++;
	}

	Msg( "\n^3 %i maps found.\n", nummaps );
	Mem_Free( t );

	// cut shortestMatch to the amount common with s
	for( i = 0; matchbuf[i]; i++ )
	{
		if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
			completedname[i] = 0;
	}
	return true;
}