This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/con_utils.c

1061 lines
27 KiB
C
Raw Normal View History

2011-05-09 22:00:00 +02:00
/*
con_utils.c - console helpers
Copyright (C) 2008 Uncle Mike
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 3 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.
*/
2008-08-03 22:00:00 +02:00
#include "common.h"
#include "client.h"
2008-11-22 22:00:00 +01:00
#include "const.h"
2010-11-15 22:00:00 +01:00
#include "../cl_dll/kbutton.h"
2008-08-03 22:00:00 +02:00
2010-11-26 22:00:00 +01:00
extern convar_t *con_gamemaps;
2017-01-14 22:00:00 +01:00
typedef struct autocomplete_list_s
{
const char *name;
qboolean (*func)( const char *s, char *name, int length );
} autocomplete_list_t;
2010-08-05 22:00:00 +02:00
#ifdef _DEBUG
2010-10-26 22:00:00 +02:00
void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage )
2010-08-05 22:00:00 +02:00
{
if( fExpr ) return;
if( szMessage != NULL )
2015-12-04 22:00:00 +01:00
MsgDev( at_error, "ASSERT FAILED:\n %s \n(%s@%d)\n%s\n", szExpr, szFile, szLine, szMessage );
2010-08-05 22:00:00 +02:00
else MsgDev( at_error, "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine );
}
#endif // DEBUG
2008-08-03 22:00:00 +02:00
/*
=======================================================================
FILENAME AUTOCOMPLETION
=======================================================================
*/
/*
=====================================
Cmd_GetMapList
Prints or complete map filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetMapList( const char *s, char *completedname, int length )
2008-08-03 22:00:00 +02:00
{
search_t *t;
2009-07-12 22:00:00 +02:00
file_t *f;
2008-08-03 22:00:00 +02:00
string message;
string matchbuf;
byte buf[MAX_SYSPATH]; // 1 kb
int i, nummaps;
2017-02-12 22:00:00 +01:00
t = FS_Search( va( "maps/%s*.bsp", s ), true, con_gamemaps->value );
2008-08-03 22:00:00 +02:00
if( !t ) return false;
2008-11-22 22:00:00 +01:00
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2008-11-22 22:00:00 +01:00
if( t->numfilenames == 1 ) return true;
2008-08-03 22:00:00 +02:00
2008-11-22 22:00:00 +01:00
for( i = 0, nummaps = 0; i < t->numfilenames; i++ )
2008-08-03 22:00:00 +02:00
{
2018-02-05 22:00:00 +01:00
char entfilename[MAX_QPATH];
2008-08-03 22:00:00 +02:00
const char *ext = FS_FileExtension( t->filenames[i] );
2018-02-05 22:00:00 +01:00
int ver = -1, lumpofs = 0, lumplen = 0;
2011-03-02 22:00:00 +01:00
char *ents = NULL, *pfile;
2018-02-05 22:00:00 +01:00
qboolean validmap = false;
2016-11-29 22:00:00 +01:00
int version = 0;
2018-02-05 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
if( Q_stricmp( ext, "bsp" )) continue;
Q_strncpy( message, "^1error^7", sizeof( message ));
2017-02-12 22:00:00 +01:00
f = FS_Open( t->filenames[i], "rb", con_gamemaps->value );
2008-08-03 22:00:00 +02:00
2009-07-12 22:00:00 +02:00
if( f )
2008-08-03 22:00:00 +02:00
{
2015-01-14 22:00:00 +01:00
dheader_t *header;
dextrahdr_t *hdrext;
2009-07-12 22:00:00 +02:00
2016-11-17 22:00:00 +01:00
memset( buf, 0, sizeof( buf ));
2015-01-14 22:00:00 +01:00
FS_Read( f, buf, sizeof( buf ));
header = (dheader_t *)buf;
ver = header->version;
2018-02-05 22:00:00 +01:00
// check all the lumps and some other errors
if( Mod_TestBmodelLumps( t->filenames[i], buf, true ))
2008-08-03 22:00:00 +02:00
{
2018-02-05 22:00:00 +01:00
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
ver = header->version;
2009-07-12 22:00:00 +02:00
}
2018-02-02 22:00:00 +01:00
hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader_t ));
2018-02-05 22:00:00 +01:00
if( hdrext->id == IDEXTRAHEADER ) version = hdrext->version;
2015-01-14 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
2009-07-12 22:00:00 +02:00
FS_StripExtension( entfilename );
FS_DefaultExtension( entfilename, ".ent" );
2011-03-08 22:00:00 +01:00
ents = FS_LoadFile( entfilename, NULL, true );
2009-07-12 22:00:00 +02:00
if( !ents && lumplen >= 10 )
{
FS_Seek( f, lumpofs, SEEK_SET );
2011-03-02 22:00:00 +01:00
ents = (char *)Mem_Alloc( host.mempool, lumplen + 1 );
FS_Read( f, ents, lumplen );
2009-07-12 22:00:00 +02:00
}
if( ents )
{
// if there are entities to parse, a missing message key just
// means there is no title, so clear the message string now
2011-10-09 22:00:00 +02:00
char token[2048];
2009-07-12 22:00:00 +02:00
2018-02-05 22:00:00 +01:00
message[0] = 0; // remove 'error'
2011-03-02 22:00:00 +01:00
pfile = ents;
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
2009-07-12 22:00:00 +02:00
{
2011-03-09 22:00:00 +01:00
if( !Q_strcmp( token, "{" )) continue;
2018-02-05 22:00:00 +01:00
else if( !Q_strcmp( token, "}" )) break;
else if( !Q_strcmp( token, "message" ))
2009-07-12 22:00:00 +02:00
{
// get the message contents
2011-03-02 22:00:00 +01:00
pfile = COM_ParseFile( pfile, message );
2009-07-12 22:00:00 +02:00
}
}
2011-03-02 22:00:00 +01:00
Mem_Free( ents );
2008-08-03 22:00:00 +02:00
}
}
2009-07-12 22:00:00 +02:00
if( f ) FS_Close(f);
2008-11-01 22:00:00 +01:00
FS_FileBase( t->filenames[i], matchbuf );
switch( ver )
2008-08-03 22:00:00 +02:00
{
2010-05-22 22:00:00 +02:00
case Q1BSP_VERSION:
2018-02-05 22:00:00 +01:00
Q_strncpy( buf, "Quake", sizeof( buf ));
2009-07-12 22:00:00 +02:00
break;
2017-07-31 23:00:00 +02:00
case QBSP2_VERSION:
Q_strncpy( buf, "Darkplaces BSP2", sizeof( buf ));
break;
2010-05-22 22:00:00 +02:00
case HLBSP_VERSION:
2018-02-05 22:00:00 +01:00
switch( version )
{
case 1: Q_strncpy( buf, "XashXT old format", sizeof( buf )); break;
case 2: Q_strncpy( buf, "Paranoia 2: Savior", sizeof( buf )); break;
case 4: Q_strncpy( buf, "Half-Life extended", sizeof( buf )); break;
default: Q_strncpy( buf, "Half-Life", sizeof( buf )); break;
}
2009-11-03 22:00:00 +01:00
break;
2011-03-09 22:00:00 +01:00
default: Q_strncpy( buf, "??", sizeof( buf )); break;
2008-08-03 22:00:00 +02:00
}
2013-02-19 21:00:00 +01:00
2009-07-03 22:00:00 +02:00
Msg( "%16s (%s) ^3%s^7\n", matchbuf, buf, message );
2008-08-03 22:00:00 +02:00
nummaps++;
}
2011-04-08 22:00:00 +02:00
2009-07-03 22:00:00 +02:00
Msg( "\n^3 %i maps found.\n", nummaps );
2008-08-03 22:00:00 +02:00
Mem_Free( t );
// cut shortestMatch to the amount common with s
for( i = 0; matchbuf[i]; i++ )
{
2012-12-21 21:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-03 22:00:00 +02:00
completedname[i] = 0;
}
return true;
}
/*
=====================================
Cmd_GetDemoList
Prints or complete demo filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetDemoList( const char *s, char *completedname, int length )
2008-08-03 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, numdems;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "demos/%s*.dem", s ), true, true ); // lookup only in gamedir
2009-07-03 22:00:00 +02:00
if( !t ) return false;
2008-08-03 22:00:00 +02:00
2009-07-03 22:00:00 +02:00
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2009-07-03 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
2008-08-03 22:00:00 +02:00
2009-07-03 22:00:00 +02:00
for( i = 0, numdems = 0; i < t->numfilenames; i++ )
2008-08-03 22:00:00 +02:00
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "dem" ))
continue;
2008-08-03 22:00:00 +02:00
2009-07-03 22:00:00 +02:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
2008-08-03 22:00:00 +02:00
numdems++;
}
2011-04-08 22:00:00 +02:00
2009-07-03 22:00:00 +02:00
Msg( "\n^3 %i demos found.\n", numdems );
Mem_Free( t );
2008-08-03 22:00:00 +02:00
// cut shortestMatch to the amount common with s
2009-07-03 22:00:00 +02:00
if( completedname && length )
2008-08-03 22:00:00 +02:00
{
for( i = 0; matchbuf[i]; i++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-03 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetMovieList
Prints or complete movie filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetMovieList( const char *s, char *completedname, int length )
2008-08-03 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, nummovies;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "media/%s*.avi", s ), true, false );
2010-04-12 22:00:00 +02:00
if( !t ) return false;
2008-08-03 22:00:00 +02:00
2010-09-30 22:00:00 +02:00
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2010-09-30 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
2008-08-03 22:00:00 +02:00
for(i = 0, nummovies = 0; i < t->numfilenames; i++)
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "avi" ))
continue;
2008-08-03 22:00:00 +02:00
2010-09-30 22:00:00 +02:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
2008-08-03 22:00:00 +02:00
nummovies++;
}
2011-04-08 22:00:00 +02:00
2010-09-30 22:00:00 +02:00
Msg( "\n^3 %i movies found.\n", nummovies );
Mem_Free( t );
2008-08-03 22:00:00 +02:00
// cut shortestMatch to the amount common with s
2010-09-30 22:00:00 +02:00
if( completedname && length )
2008-08-03 22:00:00 +02:00
{
for( i = 0; matchbuf[i]; i++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-03 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetMusicList
Prints or complete background track filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetMusicList( const char *s, char *completedname, int length )
2008-08-03 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, numtracks;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "media/%s*.*", s ), true, false );
2010-04-12 22:00:00 +02:00
if( !t ) return false;
2008-08-03 22:00:00 +02:00
2010-09-16 22:00:00 +02:00
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2010-09-16 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
2008-08-03 22:00:00 +02:00
for(i = 0, numtracks = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
2016-08-13 23:00:00 +02:00
if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" ))
continue;
2010-09-16 22:00:00 +02:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
2008-08-03 22:00:00 +02:00
numtracks++;
}
2011-04-08 22:00:00 +02:00
2010-09-16 22:00:00 +02:00
Msg( "\n^3 %i soundtracks found.\n", numtracks );
2008-08-03 22:00:00 +02:00
Mem_Free(t);
// cut shortestMatch to the amount common with s
2010-09-16 22:00:00 +02:00
if( completedname && length )
2008-08-03 22:00:00 +02:00
{
for( i = 0; matchbuf[i]; i++ )
{
2011-04-08 22:00:00 +02:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-03 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2009-09-28 22:00:00 +02:00
/*
=====================================
Cmd_GetSavesList
2010-07-31 22:00:00 +02:00
Prints or complete savegame filename
2009-09-28 22:00:00 +02:00
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetSavesList( const char *s, char *completedname, int length )
2009-09-28 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, numsaves;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "save/%s*.sav", s ), true, true ); // lookup only in gamedir
2009-09-28 22:00:00 +02:00
if( !t ) return false;
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2009-09-28 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
for( i = 0, numsaves = 0; i < t->numfilenames; i++ )
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "sav" ))
continue;
2009-09-28 22:00:00 +02:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
numsaves++;
}
2011-04-08 22:00:00 +02:00
2009-09-28 22:00:00 +02:00
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++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2009-09-28 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2010-07-31 22:00:00 +02:00
/*
=====================================
Cmd_GetConfigList
Prints or complete .cfg filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetConfigList( const char *s, char *completedname, int length )
2010-07-31 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, numconfigs;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "%s*.cfg", s ), true, false );
2010-07-31 22:00:00 +02:00
if( !t ) return false;
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2010-07-31 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
for( i = 0, numconfigs = 0; i < t->numfilenames; i++ )
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "cfg" ))
continue;
2010-07-31 22:00:00 +02:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
numconfigs++;
}
2011-04-08 22:00:00 +02:00
2010-07-31 22:00:00 +02:00
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++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2010-07-31 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2008-08-03 22:00:00 +02:00
/*
=====================================
Cmd_GetSoundList
Prints or complete sound filename
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetSoundList( const char *s, char *completedname, int length )
2008-08-03 22:00:00 +02:00
{
search_t *t;
string matchbuf;
int i, numsounds;
const char *snddir = "sound/"; // constant
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "%s%s*.*", snddir, s ), true, false );
2010-03-22 22:00:00 +01:00
if( !t ) return false;
2008-08-03 22:00:00 +02:00
2011-03-09 22:00:00 +01:00
Q_strncpy( matchbuf, t->filenames[0] + Q_strlen( snddir ), MAX_STRING );
2008-08-03 22:00:00 +02:00
FS_StripExtension( matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2010-03-22 22:00:00 +01:00
if( t->numfilenames == 1 ) return true;
2008-08-03 22:00:00 +02:00
for(i = 0, numsounds = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
2017-01-14 22:00:00 +01:00
if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" ))
continue;
2010-09-16 22:00:00 +02:00
2011-03-09 22:00:00 +01:00
Q_strncpy( matchbuf, t->filenames[i] + Q_strlen(snddir), MAX_STRING );
2008-08-03 22:00:00 +02:00
FS_StripExtension( matchbuf );
2010-03-22 22:00:00 +01:00
Msg( "%16s\n", matchbuf );
2008-08-03 22:00:00 +02:00
numsounds++;
}
2011-04-08 22:00:00 +02:00
2010-03-22 22:00:00 +01:00
Msg( "\n^3 %i sounds found.\n", numsounds );
Mem_Free( t );
2008-08-03 22:00:00 +02:00
// cut shortestMatch to the amount common with s
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
2011-04-08 22:00:00 +02:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-03 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2010-10-28 22:00:00 +02:00
2009-01-23 22:00:00 +01:00
/*
=====================================
Cmd_GetItemsList
2011-04-08 22:00:00 +02:00
Prints or complete item classname (weapons only)
2009-01-23 22:00:00 +01:00
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetItemsList( const char *s, char *completedname, int length )
2009-01-23 22:00:00 +01:00
{
search_t *t;
string matchbuf;
int i, numitems;
2010-07-08 22:00:00 +02:00
if( !clgame.itemspath[0] ) return false; // not in game yet
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "%s/%s*.txt", clgame.itemspath, s ), true, false );
2009-01-23 22:00:00 +01:00
if( !t ) return false;
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2009-01-23 22:00:00 +01:00
if( t->numfilenames == 1 ) return true;
for(i = 0, numitems = 0; i < t->numfilenames; i++)
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "txt" ))
continue;
2009-01-23 22:00:00 +01:00
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
numitems++;
}
2011-04-08 22:00:00 +02:00
Msg( "\n^3 %i items found.\n", numitems );
2009-01-23 22:00:00 +01:00
Mem_Free( t );
// cut shortestMatch to the amount common with s
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
2011-04-08 22:00:00 +02:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2009-01-23 22:00:00 +01:00
completedname[i] = 0;
}
}
return true;
}
2010-10-28 22:00:00 +02:00
/*
=====================================
Cmd_GetCustomList
Prints or complete .HPK filenames
=====================================
*/
qboolean Cmd_GetCustomList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, numitems;
2011-03-08 22:00:00 +01:00
t = FS_Search( va( "%s*.hpk", s ), true, false );
2010-10-28 22:00:00 +02:00
if( !t ) return false;
FS_FileBase( t->filenames[0], matchbuf );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2010-10-28 22:00:00 +02:00
if( t->numfilenames == 1 ) return true;
for(i = 0, numitems = 0; i < t->numfilenames; i++)
{
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "hpk" ))
continue;
2010-10-28 22:00:00 +02:00
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++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2010-10-28 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2009-10-06 22:00:00 +02:00
/*
=====================================
Cmd_GetGameList
Prints or complete gamedir name
=====================================
*/
2010-10-26 22:00:00 +02:00
qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
2008-08-04 22:00:00 +02:00
{
int i, numgamedirs;
2010-07-19 22:00:00 +02:00
string gamedirs[MAX_MODS];
2008-08-04 22:00:00 +02:00
string matchbuf;
2012-12-16 21:00:00 +01:00
// stand-alone games doesn't have cmd "game"
if( !Cmd_Exists( "game" ))
return false;
2008-08-04 22:00:00 +02:00
// compare gamelist with current keyword
2011-03-12 22:00:00 +01:00
for( i = 0, numgamedirs = 0; i < SI.numgames; i++ )
2008-08-04 22:00:00 +02:00
{
2011-03-12 22:00:00 +01:00
if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, Q_strlen( s )))
Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder );
2008-08-04 22:00:00 +02:00
}
if( !numgamedirs ) return false;
2011-03-09 22:00:00 +01:00
Q_strncpy( matchbuf, gamedirs[0], MAX_STRING );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2011-04-08 22:00:00 +02:00
if( numgamedirs == 1 ) return true;
2008-08-04 22:00:00 +02:00
for( i = 0; i < numgamedirs; i++ )
{
2011-03-09 22:00:00 +01:00
Q_strncpy( matchbuf, gamedirs[i], MAX_STRING );
2011-04-08 22:00:00 +02:00
Msg( "%16s\n", matchbuf );
2008-08-04 22:00:00 +02:00
}
2011-04-08 22:00:00 +02:00
Msg( "\n^3 %i games found.\n", numgamedirs );
2008-08-04 22:00:00 +02:00
// cut shortestMatch to the amount common with s
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
2011-04-08 22:00:00 +02:00
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
2008-08-04 22:00:00 +02:00
completedname[i] = 0;
}
}
return true;
}
2016-08-13 23:00:00 +02:00
/*
=====================================
Cmd_GetCDList
Prints or complete CD command name
=====================================
*/
qboolean Cmd_GetCDList( 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 );
2017-01-14 22:00:00 +01:00
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
2016-08-13 23:00:00 +02:00
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;
}
2010-11-17 22:00:00 +01:00
qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
2008-08-03 22:00:00 +02:00
{
2010-11-17 22:00:00 +01:00
byte buf[MAX_SYSPATH];
2009-07-12 22:00:00 +02:00
char *buffer;
string result;
2010-11-17 22:00:00 +01:00
int i, size;
2008-08-03 22:00:00 +02:00
search_t *t;
2009-07-12 22:00:00 +02:00
file_t *f;
2008-08-03 22:00:00 +02:00
2011-03-08 22:00:00 +01:00
if( FS_FileSize( "maps.lst", onlyingamedir ) > 0 && !fRefresh )
2012-02-09 21:00:00 +01:00
{
2014-05-11 22:00:00 +02:00
MsgDev( D_NOTE, "maps.lst is exist: %s\n", onlyingamedir ? "basedir" : "gamedir" );
2008-08-03 22:00:00 +02:00
return true; // exist
2012-02-09 21:00:00 +01:00
}
2008-08-03 22:00:00 +02:00
2011-03-08 22:00:00 +01:00
t = FS_Search( "maps/*.bsp", false, onlyingamedir );
2012-12-21 21:00:00 +01:00
2013-12-01 21:00:00 +01:00
if( !t )
2012-02-09 21:00:00 +01:00
{
2013-12-01 21:00:00 +01:00
if( onlyingamedir )
{
// mod doesn't contain any maps (probably this is a bot)
return Cmd_CheckMapsList_R( fRefresh, false );
}
return false;
2012-02-09 21:00:00 +01:00
}
2008-08-03 22:00:00 +02:00
2010-02-18 22:00:00 +01:00
buffer = Mem_Alloc( host.mempool, t->numfilenames * 2 * sizeof( result ));
2012-12-21 21:00:00 +01:00
2008-08-03 22:00:00 +02:00
for( i = 0; i < t->numfilenames; i++ )
{
2011-03-02 22:00:00 +01:00
char *ents = NULL, *pfile;
2018-02-05 22:00:00 +01:00
int lumpofs = 0, lumplen = 0;
2009-07-12 22:00:00 +02:00
string mapname, message, entfilename;
2008-08-03 22:00:00 +02:00
2017-01-14 22:00:00 +01:00
if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "bsp" ))
continue;
2011-03-08 22:00:00 +01:00
f = FS_Open( t->filenames[i], "rb", onlyingamedir );
2008-08-03 22:00:00 +02:00
FS_FileBase( t->filenames[i], mapname );
2009-07-12 22:00:00 +02:00
if( f )
2008-08-03 22:00:00 +02:00
{
2009-11-03 22:00:00 +01:00
int num_spawnpoints = 0;
dheader_t *header;
2009-07-12 22:00:00 +02:00
2016-11-17 22:00:00 +01:00
memset( buf, 0, MAX_SYSPATH );
2009-07-12 22:00:00 +02:00
FS_Read( f, buf, MAX_SYSPATH );
2018-02-05 22:00:00 +01:00
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( t->filenames[i], buf, true ))
2009-07-12 22:00:00 +02:00
{
2018-02-05 22:00:00 +01:00
FS_Close( f );
continue;
2008-08-03 22:00:00 +02:00
}
2008-11-22 22:00:00 +01:00
2018-02-05 22:00:00 +01:00
// after call Mod_TestBmodelLumps we gurantee what map is valid
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
2011-03-09 22:00:00 +01:00
Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
2009-07-12 22:00:00 +02:00
FS_StripExtension( entfilename );
FS_DefaultExtension( entfilename, ".ent" );
2011-03-08 22:00:00 +01:00
ents = FS_LoadFile( entfilename, NULL, true );
2009-07-12 22:00:00 +02:00
if( !ents && lumplen >= 10 )
{
FS_Seek( f, lumpofs, SEEK_SET );
2018-02-05 22:00:00 +01:00
ents = Z_Malloc( lumplen + 1 );
2011-03-02 22:00:00 +01:00
FS_Read( f, ents, lumplen );
2009-07-12 22:00:00 +02:00
}
2008-08-03 22:00:00 +02:00
2008-11-01 22:00:00 +01:00
if( ents )
2008-08-03 22:00:00 +02:00
{
// if there are entities to parse, a missing message key just
// means there is no title, so clear the message string now
2018-02-05 22:00:00 +01:00
char token[MAX_TOKEN];
2010-10-26 22:00:00 +02:00
qboolean worldspawn = true;
2008-11-01 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
Q_strncpy( message, "No Title", MAX_STRING );
2011-03-02 22:00:00 +01:00
pfile = ents;
2009-07-12 22:00:00 +02:00
2011-03-02 22:00:00 +01:00
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
2008-08-03 22:00:00 +02:00
{
2011-03-02 22:00:00 +01:00
if( token[0] == '}' && worldspawn )
2010-07-31 22:00:00 +02:00
worldspawn = false;
2011-03-09 22:00:00 +01:00
else if( !Q_strcmp( token, "message" ) && worldspawn )
2009-07-12 22:00:00 +02:00
{
// get the message contents
2011-03-02 22:00:00 +01:00
pfile = COM_ParseFile( pfile, message );
2009-07-12 22:00:00 +02:00
}
2011-03-09 22:00:00 +01:00
else if( !Q_strcmp( token, "classname" ))
2008-08-03 22:00:00 +02:00
{
2011-03-02 22:00:00 +01:00
pfile = COM_ParseFile( pfile, token );
2011-03-09 22:00:00 +01:00
if( !Q_strcmp( token, GI->mp_entity ))
2008-08-03 22:00:00 +02:00
num_spawnpoints++;
}
2010-10-21 22:00:00 +02:00
if( num_spawnpoints ) break; // valid map
2008-08-03 22:00:00 +02:00
}
2011-03-02 22:00:00 +01:00
Mem_Free( ents );
2008-08-03 22:00:00 +02:00
}
2009-07-12 22:00:00 +02:00
2010-07-31 22:00:00 +02:00
if( f ) FS_Close( f );
2010-10-21 22:00:00 +02:00
if( num_spawnpoints )
2010-07-31 22:00:00 +02:00
{
// format: mapname "maptitle"\n
2011-03-09 22:00:00 +01:00
Q_sprintf( result, "%s \"%s\"\n", mapname, message );
Q_strcat( buffer, result ); // add new string
2010-07-31 22:00:00 +02:00
}
2008-08-03 22:00:00 +02:00
}
}
2012-02-09 21:00:00 +01:00
if( t ) Mem_Free( t ); // free search result
2011-03-09 22:00:00 +01:00
size = Q_strlen( buffer );
2010-11-17 22:00:00 +01:00
2011-04-08 22:00:00 +02:00
if( !size )
{
if( buffer ) Mem_Free( buffer );
2012-02-09 21:00:00 +01:00
if( onlyingamedir )
return Cmd_CheckMapsList_R( fRefresh, false );
return false;
2011-04-08 22:00:00 +02:00
}
2008-08-03 22:00:00 +02:00
// write generated maps.lst
2011-03-09 22:00:00 +01:00
if( FS_WriteFile( "maps.lst", buffer, Q_strlen( buffer )))
2008-08-03 22:00:00 +02:00
{
2008-11-01 22:00:00 +01:00
if( buffer ) Mem_Free( buffer );
2008-08-03 22:00:00 +02:00
return true;
}
return false;
}
2010-11-17 22:00:00 +01:00
qboolean Cmd_CheckMapsList( qboolean fRefresh )
{
return Cmd_CheckMapsList_R( fRefresh, true );
}
2008-08-03 22:00:00 +02:00
autocomplete_list_t cmd_list[] =
{
2011-02-22 22:00:00 +01:00
{ "map_background", Cmd_GetMapList },
2008-08-03 22:00:00 +02:00
{ "changelevel", Cmd_GetMapList },
{ "playdemo", Cmd_GetDemoList, },
2018-02-12 22:00:00 +01:00
{ "timedemo", Cmd_GetDemoList, },
2014-05-11 22:00:00 +02:00
{ "playvol", Cmd_GetSoundList },
2010-10-28 22:00:00 +02:00
{ "hpkval", Cmd_GetCustomList },
2011-09-19 22:00:00 +02:00
{ "entpatch", Cmd_GetMapList },
2010-09-16 22:00:00 +02:00
{ "music", Cmd_GetMusicList, },
2008-08-03 22:00:00 +02:00
{ "movie", Cmd_GetMovieList },
2010-07-31 22:00:00 +02:00
{ "exec", Cmd_GetConfigList },
2009-01-23 22:00:00 +01:00
{ "give", Cmd_GetItemsList },
2009-09-24 22:00:00 +02:00
{ "drop", Cmd_GetItemsList },
2008-08-04 22:00:00 +02:00
{ "game", Cmd_GetGamesList },
2009-09-28 22:00:00 +02:00
{ "save", Cmd_GetSavesList },
{ "load", Cmd_GetSavesList },
2010-06-27 22:00:00 +02:00
{ "play", Cmd_GetSoundList },
2008-08-03 22:00:00 +02:00
{ "map", Cmd_GetMapList },
2016-08-13 23:00:00 +02:00
{ "cd", Cmd_GetCDList },
2008-08-03 22:00:00 +02:00
{ NULL }, // termiantor
};
2017-01-14 22:00:00 +01:00
/*
===============
Cmd_CheckName
compare first argument with string
===============
*/
static qboolean Cmd_CheckName( const char *name )
{
if( !Q_stricmp( Cmd_Argv( 0 ), name ))
return true;
if( !Q_stricmp( Cmd_Argv( 0 ), va( "\\%s", name )))
return true;
return false;
}
/*
============
Cmd_AutocompleteName
Autocomplete filename
for various cmds
============
*/
qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize )
{
autocomplete_list_t *list;
for( list = cmd_list; list->name; list++ )
{
if( Cmd_CheckName( list->name ))
return list->func( source, buffer, bufsize );
}
return false;
}
2008-08-03 22:00:00 +02:00
/*
============
Cmd_WriteVariables
Appends lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
2010-07-30 22:00:00 +02:00
static void Cmd_WriteOpenGLCvar( const char *name, const char *string, const char *desc, void *f )
2010-12-27 22:00:00 +01:00
{
if( !desc || !*desc ) return; // ignore cvars without description (fantom variables)
FS_Printf( f, "setgl %s \"%s\"\n", name, string );
}
2008-08-05 22:00:00 +02:00
static void Cmd_WriteHelp(const char *name, const char *unused, const char *desc, void *f )
{
2017-08-22 23:00:00 +02:00
int length;
if( !desc || !Q_strcmp( desc, "" ))
return; // ignore fantom cmds
if( name[0] == '+' || name[0] == '-' )
return; // key bindings
length = 3 - (Q_strlen( name ) / 10); // Asm_Ed default tab stop is 10
if( length == 3 ) FS_Printf( f, "%s\t\t\t\"%s\"\n", name, desc );
if( length == 2 ) FS_Printf( f, "%s\t\t\"%s\"\n", name, desc );
if( length == 1 ) FS_Printf( f, "%s\t\"%s\"\n", name, desc );
if( length == 0 ) FS_Printf( f, "%s \"%s\"\n", name, desc );
2008-08-05 22:00:00 +02:00
}
2010-07-30 22:00:00 +02:00
void Cmd_WriteOpenGLVariables( file_t *f )
{
2017-02-12 22:00:00 +01:00
Cvar_LookupVars( FCVAR_GLCONFIG, NULL, f, Cmd_WriteOpenGLCvar );
2010-12-27 22:00:00 +01:00
}
2008-08-03 22:00:00 +02:00
/*
===============
2009-09-10 22:00:00 +02:00
Host_WriteConfig
2008-08-03 22:00:00 +02:00
2010-07-30 22:00:00 +02:00
Writes key bindings and archived cvars to config.cfg
2008-08-03 22:00:00 +02:00
===============
*/
2009-09-10 22:00:00 +02:00
void Host_WriteConfig( void )
2008-08-03 22:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
kbutton_t *mlook = NULL;
kbutton_t *jlook = NULL;
2011-04-08 22:00:00 +02:00
file_t *f;
2008-08-03 22:00:00 +02:00
2011-09-19 22:00:00 +02:00
if( !clgame.hInstance ) return;
2008-08-03 22:00:00 +02:00
2011-02-25 22:00:00 +01:00
MsgDev( D_NOTE, "Host_WriteConfig()\n" );
2011-03-08 22:00:00 +01:00
f = FS_Open( "config.cfg", "w", false );
2008-11-12 22:00:00 +01:00
if( f )
2008-08-03 22:00:00 +02:00
{
2010-07-30 22:00:00 +02:00
FS_Printf( f, "//=======================================================================\n");
2011-03-09 22:00:00 +01:00
FS_Printf( f, "//\t\t\tCopyright XashXT Group %s <20>\n", Q_timestamp( TIME_YEAR_ONLY ));
2010-07-30 22:00:00 +02:00
FS_Printf( f, "//\t\t\tconfig.cfg - archive of cvars\n" );
2009-09-10 22:00:00 +02:00
FS_Printf( f, "//=======================================================================\n" );
Key_WriteBindings( f );
2017-02-12 22:00:00 +01:00
Cvar_WriteVariables( f, FCVAR_ARCHIVE );
Info_WriteVars( f );
2010-11-15 22:00:00 +01:00
2017-02-12 22:00:00 +01:00
if( clgame.hInstance )
{
mlook = (kbutton_t *)clgame.dllFuncs.KB_Find( "in_mlook" );
jlook = (kbutton_t *)clgame.dllFuncs.KB_Find( "in_jlook" );
}
2010-11-15 22:00:00 +01:00
if( mlook && ( mlook->state & 1 ))
FS_Printf( f, "+mlook\n" );
if( jlook && ( jlook->state & 1 ))
FS_Printf( f, "+jlook\n" );
2012-01-27 21:00:00 +01:00
FS_Printf( f, "exec userconfig.cfg" );
2009-09-10 22:00:00 +02:00
FS_Close( f );
2008-08-03 22:00:00 +02:00
}
2010-07-30 22:00:00 +02:00
else MsgDev( D_ERROR, "Couldn't write config.cfg.\n" );
2009-09-10 22:00:00 +02:00
}
/*
===============
2010-07-30 22:00:00 +02:00
Host_WriteServerConfig
2009-09-10 22:00:00 +02:00
2010-07-30 22:00:00 +02:00
save serverinfo variables into server.cfg (using for dedicated server too)
2009-09-10 22:00:00 +02:00
===============
*/
2010-07-30 22:00:00 +02:00
void Host_WriteServerConfig( const char *name )
2009-09-10 22:00:00 +02:00
{
file_t *f;
2010-07-30 22:00:00 +02:00
SV_InitGameProgs(); // collect user variables
2018-02-12 22:00:00 +01:00
// FIXME: move this out until menu parser is done
CSCR_LoadDefaultCVars( "settings.scr" );
CSCR_LoadDefaultCVars( "user.scr" );
2010-07-30 22:00:00 +02:00
2011-03-08 22:00:00 +01:00
if(( f = FS_Open( name, "w", false )) != NULL )
2009-09-10 22:00:00 +02:00
{
FS_Printf( f, "//=======================================================================\n" );
2011-03-09 22:00:00 +01:00
FS_Printf( f, "//\t\t\tCopyright XashXT Group %s <20>\n", Q_timestamp( TIME_YEAR_ONLY ));
2017-03-14 22:00:00 +01:00
FS_Printf( f, "//\t\tgame.cfg - multiplayer server temporare config\n" );
2009-09-10 22:00:00 +02:00
FS_Printf( f, "//=======================================================================\n" );
2017-02-13 22:00:00 +01:00
Cvar_WriteVariables( f, FCVAR_SERVER );
2018-02-12 22:00:00 +01:00
CSCR_WriteGameCVars( f, "user.scr" );
CSCR_WriteGameCVars( f, "settings.scr" );
2009-09-10 22:00:00 +02:00
FS_Close( f );
2009-11-16 22:00:00 +01:00
}
2010-07-30 22:00:00 +02:00
else MsgDev( D_ERROR, "Couldn't write %s.\n", name );
2011-03-13 22:00:00 +01:00
SV_FreeGameProgs(); // release progs with all variables
2008-08-05 22:00:00 +02:00
}
2010-01-30 22:00:00 +01:00
/*
===============
2010-07-30 22:00:00 +02:00
Host_WriteOpenGLConfig
2010-01-30 22:00:00 +01:00
2010-12-27 22:00:00 +01:00
save opengl variables into opengl.cfg
2010-01-30 22:00:00 +01:00
===============
*/
2010-07-30 22:00:00 +02:00
void Host_WriteOpenGLConfig( void )
2010-01-30 22:00:00 +01:00
{
2010-07-22 22:00:00 +02:00
file_t *f;
2011-02-25 22:00:00 +01:00
MsgDev( D_NOTE, "Host_WriteGLConfig()\n" );
2011-03-08 22:00:00 +01:00
f = FS_Open( "opengl.cfg", "w", false );
2010-07-30 22:00:00 +02:00
if( f )
2010-01-30 22:00:00 +01:00
{
FS_Printf( f, "//=======================================================================\n" );
2011-03-09 22:00:00 +01:00
FS_Printf( f, "//\t\t\tCopyright XashXT Group %s <20>\n", Q_timestamp( TIME_YEAR_ONLY ));
2010-07-30 22:00:00 +02:00
FS_Printf( f, "//\t\t opengl.cfg - archive of opengl extension cvars\n");
2010-01-30 22:00:00 +01:00
FS_Printf( f, "//=======================================================================\n" );
2010-07-30 22:00:00 +02:00
Cmd_WriteOpenGLVariables( f );
FS_Close( f );
}
else MsgDev( D_ERROR, "can't update opengl.cfg.\n" );
2010-01-30 22:00:00 +01:00
}
2010-12-27 22:00:00 +01:00
/*
===============
Host_WriteVideoConfig
save render variables into video.cfg
===============
*/
void Host_WriteVideoConfig( void )
{
file_t *f;
2011-02-25 22:00:00 +01:00
MsgDev( D_NOTE, "Host_WriteVideoConfig()\n" );
2011-03-08 22:00:00 +01:00
f = FS_Open( "video.cfg", "w", false );
2010-12-27 22:00:00 +01:00
if( f )
{
FS_Printf( f, "//=======================================================================\n" );
2011-03-09 22:00:00 +01:00
FS_Printf( f, "//\t\t\tCopyright XashXT Group %s <20>\n", Q_timestamp( TIME_YEAR_ONLY ));
2010-12-27 22:00:00 +01:00
FS_Printf( f, "//\t\tvideo.cfg - archive of renderer variables\n");
FS_Printf( f, "//=======================================================================\n" );
2017-02-13 22:00:00 +01:00
Cvar_WriteVariables( f, FCVAR_RENDERINFO );
2010-12-27 22:00:00 +01:00
FS_Close( f );
}
else MsgDev( D_ERROR, "can't update video.cfg.\n" );
}
2008-08-05 22:00:00 +02:00
void Key_EnumCmds_f( void )
{
2010-01-30 22:00:00 +01:00
file_t *f;
FS_AllowDirectPaths( true );
2011-03-08 22:00:00 +01:00
if( FS_FileExists( "../help.txt", false ))
2010-01-30 22:00:00 +01:00
{
Msg( "help.txt already exist\n" );
FS_AllowDirectPaths( false );
return;
}
2011-03-08 22:00:00 +01:00
f = FS_Open( "../help.txt", "w", false );
2008-08-05 22:00:00 +02:00
if( f )
{
FS_Printf( f, "//=======================================================================\n");
2011-03-09 22:00:00 +01:00
FS_Printf( f, "//\t\t\tCopyright XashXT Group %s <20>\n", Q_timestamp( TIME_YEAR_ONLY ));
2008-08-05 22:00:00 +02:00
FS_Printf( f, "//\t\thelp.txt - xash commands and console variables\n");
FS_Printf( f, "//=======================================================================\n");
FS_Printf( f, "\n\n\t\t\tconsole variables\n\n");
Cvar_LookupVars( 0, NULL, f, Cmd_WriteHelp );
FS_Printf( f, "\n\n\t\t\tconsole commands\n\n");
Cmd_LookupCmds( NULL, f, Cmd_WriteHelp );
FS_Printf( f, "\n\n");
FS_Close( f );
2010-01-30 22:00:00 +01:00
Msg( "help.txt created\n" );
2008-08-05 22:00:00 +02:00
}
else MsgDev( D_ERROR, "Couldn't write help.txt.\n");
2010-01-30 22:00:00 +01:00
FS_AllowDirectPaths( false );
2008-08-03 22:00:00 +02:00
}