mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-02-27 06:11:46 +00:00
have those escapes stripped before printing so that they do not merge with subsequent text. - Moved default weapon slot assignments into the player classes. Weapon.SlotNumber is now used solely for mods that want to add new weapons without completely redoing the player's arsenal. Restored some config-based weapon slot customization, though slots are no longer automatically saved to the config and section names have changed slightly. However, unlike before, config slots are now the definitive word on slot assignments and cannot be overridden by any other files loaded. - Fixed: Several weapons were missing a game filter from their definitions. - Removed storage of weapon slots in the config so that weapon slots can be setup in the weapons themselves. Slots are still configurable, since they need to be for KEYCONF to work; any changes simply won't be saved when you quit. - Removed limit on weapon slot sizes. SVN r1428 (trunk)
741 lines
13 KiB
C++
741 lines
13 KiB
C++
// cmdlib.c (mostly borrowed from the Q2 source)
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
#include "doomtype.h"
|
|
#include "cmdlib.h"
|
|
#include "i_system.h"
|
|
#include "v_text.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
|
|
/*
|
|
progdir will hold the path up to the game directory, including the slash
|
|
|
|
f:\quake\
|
|
/raid/quake/
|
|
|
|
gamedir will hold progdir + the game directory (id1, id2, etc)
|
|
|
|
*/
|
|
|
|
FString progdir;
|
|
|
|
static inline bool IsSeperator (int c)
|
|
{
|
|
if (c == '/')
|
|
return true;
|
|
#ifdef WIN32
|
|
if (c == '\\' || c == ':')
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void FixPathSeperator (char *path)
|
|
{
|
|
while (*path)
|
|
{
|
|
if (*path == '\\')
|
|
*path = '/';
|
|
path++;
|
|
}
|
|
}
|
|
|
|
char *copystring (const char *s)
|
|
{
|
|
char *b;
|
|
if (s)
|
|
{
|
|
size_t len = strlen (s) + 1;
|
|
b = new char[len];
|
|
memcpy (b, s, len);
|
|
}
|
|
else
|
|
{
|
|
b = new char[1];
|
|
b[0] = '\0';
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
void ReplaceString (char **ptr, const char *str)
|
|
{
|
|
if (*ptr)
|
|
{
|
|
if (*ptr == str)
|
|
return;
|
|
delete[] *ptr;
|
|
}
|
|
*ptr = copystring (str);
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
MISC FUNCTIONS
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
================
|
|
Q_filelength
|
|
================
|
|
*/
|
|
int Q_filelength (FILE *f)
|
|
{
|
|
int pos;
|
|
int end;
|
|
|
|
pos = ftell (f);
|
|
fseek (f, 0, SEEK_END);
|
|
end = ftell (f);
|
|
fseek (f, pos, SEEK_SET);
|
|
|
|
return end;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
FileExists
|
|
==============
|
|
*/
|
|
bool FileExists (const char *filename)
|
|
{
|
|
FILE *f;
|
|
|
|
// [RH] Empty filenames are never there
|
|
if (*filename == 0)
|
|
return false;
|
|
|
|
f = fopen (filename, "r");
|
|
if (!f)
|
|
return false;
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
|
|
void DefaultExtension (char *path, const char *extension)
|
|
{
|
|
char *src;
|
|
//
|
|
// if path doesn't have a .EXT, append extension
|
|
// (extension should include the .)
|
|
//
|
|
src = path + strlen(path) - 1;
|
|
|
|
while (src != path && !IsSeperator(*src))
|
|
{
|
|
if (*src == '.')
|
|
return; // it has an extension
|
|
src--;
|
|
}
|
|
|
|
strcat (path, extension);
|
|
}
|
|
|
|
void DefaultExtension (FString &path, const char *extension)
|
|
{
|
|
const char *src = &path[int(path.Len())-1];
|
|
|
|
while (src != &path[0] && !IsSeperator(*src))
|
|
{
|
|
if (*src == '.')
|
|
return; // it has an extension
|
|
src--;
|
|
}
|
|
|
|
path += extension;
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
Extract file parts
|
|
====================
|
|
*/
|
|
// FIXME: should include the slash, otherwise
|
|
// backing to an empty path will be wrong when appending a slash
|
|
FString ExtractFilePath (const char *path)
|
|
{
|
|
const char *src;
|
|
|
|
src = path + strlen(path) - 1;
|
|
|
|
//
|
|
// back up until a \ or the start
|
|
//
|
|
while (src != path && !IsSeperator(*(src-1)))
|
|
src--;
|
|
|
|
return FString(path, src - path);
|
|
}
|
|
|
|
FString ExtractFileBase (const char *path, bool include_extension)
|
|
{
|
|
const char *src, *dot;
|
|
|
|
src = path + strlen(path) - 1;
|
|
|
|
if (src >= path)
|
|
{
|
|
// back up until a / or the start
|
|
while (src != path && !IsSeperator(*(src-1)))
|
|
src--;
|
|
|
|
// Check for files with drive specification but no path
|
|
#if defined(_WIN32) || defined(DOS)
|
|
if (src == path && src[0] != 0)
|
|
{
|
|
if (src[1] == ':')
|
|
src += 2;
|
|
}
|
|
#endif
|
|
|
|
if (!include_extension)
|
|
{
|
|
dot = src;
|
|
while (*dot && *dot != '.')
|
|
{
|
|
dot++;
|
|
}
|
|
return FString(src, dot - src);
|
|
}
|
|
else
|
|
{
|
|
return FString(src);
|
|
}
|
|
}
|
|
return FString();
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
ParseNum / ParseHex
|
|
==============
|
|
*/
|
|
int ParseHex (const char *hex)
|
|
{
|
|
const char *str;
|
|
int num;
|
|
|
|
num = 0;
|
|
str = hex;
|
|
|
|
while (*str)
|
|
{
|
|
num <<= 4;
|
|
if (*str >= '0' && *str <= '9')
|
|
num += *str-'0';
|
|
else if (*str >= 'a' && *str <= 'f')
|
|
num += 10 + *str-'a';
|
|
else if (*str >= 'A' && *str <= 'F')
|
|
num += 10 + *str-'A';
|
|
else {
|
|
Printf ("Bad hex number: %s\n",hex);
|
|
return 0;
|
|
}
|
|
str++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
|
|
int ParseNum (const char *str)
|
|
{
|
|
if (str[0] == '$')
|
|
return ParseHex (str+1);
|
|
if (str[0] == '0' && str[1] == 'x')
|
|
return ParseHex (str+2);
|
|
return atol (str);
|
|
}
|
|
|
|
|
|
// [RH] Returns true if the specified string is a valid decimal number
|
|
|
|
bool IsNum (const char *str)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (((*str < '0') || (*str > '9')) && (*str != '-'))
|
|
{
|
|
return false;
|
|
}
|
|
str++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// [RH] Checks if text matches the wildcard pattern using ? or *
|
|
|
|
bool CheckWildcards (const char *pattern, const char *text)
|
|
{
|
|
if (pattern == NULL || text == NULL)
|
|
return true;
|
|
|
|
while (*pattern)
|
|
{
|
|
if (*pattern == '*')
|
|
{
|
|
char stop = tolower (*++pattern);
|
|
while (*text && tolower(*text) != stop)
|
|
{
|
|
text++;
|
|
}
|
|
if (*text && tolower(*text) == stop)
|
|
{
|
|
if (CheckWildcards (pattern, text++))
|
|
{
|
|
return true;
|
|
}
|
|
pattern--;
|
|
}
|
|
}
|
|
else if (*pattern == '?' || tolower(*pattern) == tolower(*text))
|
|
{
|
|
pattern++;
|
|
text++;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return (*pattern | *text) == 0;
|
|
}
|
|
|
|
// [RH] Print a GUID to a text buffer using the standard format.
|
|
|
|
void FormatGUID (char *buffer, size_t buffsize, const GUID &guid)
|
|
{
|
|
mysnprintf (buffer, buffsize, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
|
(uint32)guid.Data1, guid.Data2, guid.Data3,
|
|
guid.Data4[0], guid.Data4[1],
|
|
guid.Data4[2], guid.Data4[3],
|
|
guid.Data4[4], guid.Data4[5],
|
|
guid.Data4[6], guid.Data4[7]);
|
|
}
|
|
|
|
// [RH] Returns the current local time as ASCII, even if it's too early
|
|
const char *myasctime ()
|
|
{
|
|
time_t clock;
|
|
struct tm *lt;
|
|
|
|
time (&clock);
|
|
lt = localtime (&clock);
|
|
if (lt != NULL)
|
|
{
|
|
return asctime (lt);
|
|
}
|
|
else
|
|
{
|
|
return "Pre Jan 01 00:00:00 1970\n";
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreatePath: creates a directory including all levels necessary */
|
|
/* */
|
|
/************************************************************************/
|
|
#ifdef _WIN32
|
|
void DoCreatePath(const char * fn)
|
|
{
|
|
char drive[_MAX_DRIVE];
|
|
char path[PATH_MAX];
|
|
char p[PATH_MAX];
|
|
int i;
|
|
|
|
_splitpath(fn,drive,path,NULL,NULL);
|
|
_makepath(p,drive,path,NULL,NULL);
|
|
i=(int)strlen(p);
|
|
if (p[i-1]=='/' || p[i-1]=='\\') p[i-1]=0;
|
|
if (*path) DoCreatePath(p);
|
|
_mkdir(p);
|
|
}
|
|
|
|
void CreatePath(const char * fn)
|
|
{
|
|
char name[PATH_MAX];
|
|
char c = fn[strlen(fn)-1];
|
|
|
|
if (c!='\\' && c!='/')
|
|
{
|
|
mysnprintf(name, countof(name), "%s/", fn);
|
|
DoCreatePath(name);
|
|
}
|
|
else DoCreatePath(fn);
|
|
}
|
|
#else
|
|
void CreatePath(const char *fn)
|
|
{
|
|
char *copy, *p;
|
|
|
|
if (fn[0] == '/' && fn[1] == '\0')
|
|
{
|
|
return;
|
|
}
|
|
p = copy = strdup(fn);
|
|
do
|
|
{
|
|
p = strchr(p + 1, '/');
|
|
if (p != NULL)
|
|
{
|
|
*p = '\0';
|
|
}
|
|
printf("%s\n", copy);
|
|
if (mkdir(copy, 0755) == -1)
|
|
{ // failed
|
|
return;
|
|
}
|
|
if (p != NULL)
|
|
{
|
|
*p = '/';
|
|
}
|
|
} while (p);
|
|
free(copy);
|
|
}
|
|
#endif
|
|
|
|
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
|
// This operation is done in-place. The result is the new length of the string.
|
|
|
|
int strbin (char *str)
|
|
{
|
|
char *start = str;
|
|
char *p = str, c;
|
|
int i;
|
|
|
|
while ( (c = *p++) ) {
|
|
if (c != '\\') {
|
|
*str++ = c;
|
|
} else {
|
|
switch (*p) {
|
|
case 'a':
|
|
*str++ = '\a';
|
|
break;
|
|
case 'b':
|
|
*str++ = '\b';
|
|
break;
|
|
case 'c':
|
|
*str++ = '\034'; // TEXTCOLOR_ESCAPE
|
|
break;
|
|
case 'f':
|
|
*str++ = '\f';
|
|
break;
|
|
case 'n':
|
|
*str++ = '\n';
|
|
break;
|
|
case 't':
|
|
*str++ = '\t';
|
|
break;
|
|
case 'r':
|
|
*str++ = '\r';
|
|
break;
|
|
case 'v':
|
|
*str++ = '\v';
|
|
break;
|
|
case '?':
|
|
*str++ = '\?';
|
|
break;
|
|
case '\n':
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
c = 0;
|
|
p++;
|
|
for (i = 0; i < 2; i++) {
|
|
c <<= 4;
|
|
if (*p >= '0' && *p <= '9')
|
|
c += *p-'0';
|
|
else if (*p >= 'a' && *p <= 'f')
|
|
c += 10 + *p-'a';
|
|
else if (*p >= 'A' && *p <= 'F')
|
|
c += 10 + *p-'A';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
*str++ = c;
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
c = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
c <<= 3;
|
|
if (*p >= '0' && *p <= '7')
|
|
c += *p-'0';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
*str++ = c;
|
|
break;
|
|
default:
|
|
*str++ = *p;
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
*str = 0;
|
|
return str - start;
|
|
}
|
|
|
|
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
|
// This operation is done in-place. The result is the new length of the string.
|
|
|
|
FString strbin1 (const char *start)
|
|
{
|
|
FString result;
|
|
const char *p = start;
|
|
char c;
|
|
int i;
|
|
|
|
while ( (c = *p++) ) {
|
|
if (c != '\\') {
|
|
result << c;
|
|
} else {
|
|
switch (*p) {
|
|
case 'a':
|
|
result << '\a';
|
|
break;
|
|
case 'b':
|
|
result << '\b';
|
|
break;
|
|
case 'c':
|
|
result << '\034'; // TEXTCOLOR_ESCAPE
|
|
break;
|
|
case 'f':
|
|
result << '\f';
|
|
break;
|
|
case 'n':
|
|
result << '\n';
|
|
break;
|
|
case 't':
|
|
result << '\t';
|
|
break;
|
|
case 'r':
|
|
result << '\r';
|
|
break;
|
|
case 'v':
|
|
result << '\v';
|
|
break;
|
|
case '?':
|
|
result << '\?';
|
|
break;
|
|
case '\n':
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
c = 0;
|
|
p++;
|
|
for (i = 0; i < 2; i++) {
|
|
c <<= 4;
|
|
if (*p >= '0' && *p <= '9')
|
|
c += *p-'0';
|
|
else if (*p >= 'a' && *p <= 'f')
|
|
c += 10 + *p-'a';
|
|
else if (*p >= 'A' && *p <= 'F')
|
|
c += 10 + *p-'A';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
result << c;
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
c = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
c <<= 3;
|
|
if (*p >= '0' && *p <= '7')
|
|
c += *p-'0';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
result << c;
|
|
break;
|
|
default:
|
|
result << *p;
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CleanseString
|
|
//
|
|
// Does some mild sanity checking on a string: If it ends with an incomplete
|
|
// color escape, the escape is removed.
|
|
//
|
|
//==========================================================================
|
|
|
|
void CleanseString(char *str)
|
|
{
|
|
char *escape = strrchr(str, TEXTCOLOR_ESCAPE);
|
|
if (escape != NULL)
|
|
{
|
|
if (escape[1] == '\0')
|
|
{
|
|
*escape = '\0';
|
|
}
|
|
else if (escape[1] == '[')
|
|
{
|
|
char *close = strchr(escape + 2, ']');
|
|
if (close == NULL)
|
|
{
|
|
*escape = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ExpandEnvVars
|
|
//
|
|
// Expands environment variable references in a string. Intended primarily
|
|
// for use with IWAD search paths in config files.
|
|
//
|
|
//==========================================================================
|
|
|
|
FString ExpandEnvVars(const char *searchpathstring)
|
|
{
|
|
static const char envvarnamechars[] =
|
|
"01234567890"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"_"
|
|
"abcdefghijklmnopqrstuvwxyz";
|
|
|
|
if (searchpathstring == NULL)
|
|
return FString("");
|
|
|
|
const char *dollar = strchr(searchpathstring, '$');
|
|
if (dollar == NULL)
|
|
{
|
|
return FString(searchpathstring);
|
|
}
|
|
|
|
const char *nextchars = searchpathstring;
|
|
FString out = FString(searchpathstring, dollar - searchpathstring);
|
|
while ( (dollar != NULL) && (*nextchars != 0) )
|
|
{
|
|
size_t length = strspn(dollar + 1, envvarnamechars);
|
|
if (length != 0)
|
|
{
|
|
FString varname = FString(dollar + 1, length);
|
|
if (stricmp(varname, "progdir") == 0)
|
|
{
|
|
out += progdir;
|
|
}
|
|
else
|
|
{
|
|
char *varvalue = getenv(varname);
|
|
if ( (varvalue != NULL) && (strlen(varvalue) != 0) )
|
|
{
|
|
out += varvalue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out += '$';
|
|
}
|
|
nextchars = dollar + length + 1;
|
|
dollar = strchr(nextchars, '$');
|
|
if (dollar != NULL)
|
|
{
|
|
out += FString(nextchars, dollar - nextchars);
|
|
}
|
|
}
|
|
if (*nextchars != 0)
|
|
{
|
|
out += nextchars;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// NicePath
|
|
//
|
|
// Handles paths with leading ~ characters on Unix as well as environment
|
|
// variable substitution. On Windows, this is identical to ExpandEnvVars.
|
|
//
|
|
//==========================================================================
|
|
|
|
FString NicePath(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
return ExpandEnvVars(path);
|
|
#else
|
|
if (path == NULL || *path == '\0')
|
|
{
|
|
return FString("");
|
|
}
|
|
if (*path != '~')
|
|
{
|
|
return ExpandEnvVars(path);
|
|
}
|
|
|
|
passwd *pwstruct;
|
|
const char *slash;
|
|
|
|
if (path[1] == '/' || path[1] == '\0')
|
|
{ // Get my home directory
|
|
pwstruct = getpwuid(getuid());
|
|
slash = path + 1;
|
|
}
|
|
else
|
|
{ // Get somebody else's home directory
|
|
slash = strchr(path, '/');
|
|
if (slash == NULL)
|
|
{
|
|
slash = path + strlen(path);
|
|
}
|
|
FString who(path, slash - path);
|
|
pwstruct = getpwnam(who);
|
|
}
|
|
if (pwstruct == NULL)
|
|
{
|
|
return ExpandEnvVars(path);
|
|
}
|
|
FString where(pwstruct->pw_dir);
|
|
if (*slash != '\0')
|
|
{
|
|
where += ExpandEnvVars(slash);
|
|
}
|
|
return where;
|
|
#endif
|
|
}
|