mirror of
https://github.com/ZDoom/gzdoom-last-svn.git
synced 2025-05-31 09:21:06 +00:00
- Added directory detection to the -file parameter. This obsoletes -dir, so that parameter is now gone. - Removed automatic ".wad" appending from FWadCollection::InitMultipleFiles() since it isn't needed and prevented files without extensions from being loaded. D_AddFile() already takes care of adding the extension if the name as-given does not exist. - Fixed: Loading single files did not print a newline in the startup text. - Fixed: A_JumpIf(InTarget)Inventory jumped if the check amount was greater than the item's max amount and the item was maxed. - Fixed: Some dmadds wads used zero-length sprites as placeholders. When you ran dmadds to combine it with the IWAD's sprites, they would be replaced by the IWAD's sprites, so when loading such wads, we should ignore those as valid sprites. (Thanks to entryway for finding this.) See 22ventry's 22sprite.wad for an example. - Replaced the use of autoconf's WORDS_BIGENDIAN with __BIG_ENDIAN__, since latter comes predefined by GCC. git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@433 b0f79afe-0144-0410-b225-9a4edf0717df
869 lines
17 KiB
C++
869 lines
17 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;
|
|
|
|
//==========================================================================
|
|
//
|
|
// IsSeperator
|
|
//
|
|
// Returns true if the character is a path seperator.
|
|
//
|
|
//==========================================================================
|
|
|
|
static inline bool IsSeperator (int c)
|
|
{
|
|
if (c == '/')
|
|
return true;
|
|
#ifdef WIN32
|
|
if (c == '\\' || c == ':')
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FixPathSeperator
|
|
//
|
|
// Convert backslashes to forward slashes.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FixPathSeperator (char *path)
|
|
{
|
|
while (*path)
|
|
{
|
|
if (*path == '\\')
|
|
*path = '/';
|
|
path++;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// copystring
|
|
//
|
|
// Replacement for strdup that uses new instead of malloc.
|
|
//
|
|
//==========================================================================
|
|
|
|
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;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ReplaceString
|
|
//
|
|
// Do not use in new code.
|
|
//
|
|
//==========================================================================
|
|
|
|
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
|
|
//
|
|
// Returns true if the given path exists and is a readable file.
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FileExists (const char *filename)
|
|
{
|
|
FILE *f;
|
|
|
|
// [RH] Empty filenames are never there
|
|
if (filename == NULL || *filename == 0)
|
|
return false;
|
|
|
|
f = fopen (filename, "r");
|
|
if (!f)
|
|
return false;
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DirEntryExists
|
|
//
|
|
// Returns true if the given path exists, be it a directory or a file.
|
|
//
|
|
//==========================================================================
|
|
|
|
bool DirEntryExists(const char *pathname)
|
|
{
|
|
if (pathname == NULL || *pathname == 0)
|
|
return false;
|
|
|
|
struct stat info;
|
|
return stat(pathname, &info) == 0;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DefaultExtension -- char array version
|
|
//
|
|
// Appends the extension to a pathname if it does not already have one.
|
|
//
|
|
//==========================================================================
|
|
|
|
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);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DefaultExtension -- FString version
|
|
//
|
|
// Appends the extension to a pathname if it does not already have one.
|
|
//
|
|
//==========================================================================
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// ExtractFilePath
|
|
//
|
|
// Returns the directory part of a pathname.
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ExtractFileBase
|
|
//
|
|
// Returns the file part of a pathname, optionally including the extension.
|
|
//
|
|
//==========================================================================
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ParseNum
|
|
//
|
|
//==========================================================================
|
|
|
|
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);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// IsNum
|
|
//
|
|
// [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;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CheckWildcards
|
|
//
|
|
// [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;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FormatGUID
|
|
//
|
|
// [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]);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// myasctime
|
|
//
|
|
// [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 c = fn[strlen(fn)-1];
|
|
|
|
if (c != '\\' && c != '/')
|
|
{
|
|
FString name(fn);
|
|
name += '/';
|
|
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
|
|
|
|
//==========================================================================
|
|
//
|
|
// strbin1 -- In-place version
|
|
//
|
|
// [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 int(str - start);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// strbin1 -- String-creating version
|
|
//
|
|
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
|
// This operation is done in-place.
|
|
//
|
|
//==========================================================================
|
|
|
|
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
|
|
}
|