/* Copyright (C) 1997-2001 Id Software, Inc. 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 2 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_shared.h" //============================================================================ /* ============ COM_SkipPath ============ */ char *COM_SkipPath (char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname=='/') last = pathname+1; pathname++; } return last; } /* ============ COM_StripExtension ============ */ void COM_StripExtension( char *filename ) { char *src, *last = NULL; src = filename + strlen(filename) - 1; while( *src != '/' && src != filename ) { if( *src == '.' ) { last = src; } src--; } if( last ) *last = 0; } /* ============ COM_FileExtension ============ */ char *COM_FileExtension( char *filename ) { static char extension[MAX_QPATH]; char *src, *last = NULL; extension[0] = 0; src = filename + strlen(filename) - 1; while( *src != '/' && src != filename ) { if( *src == '.' ) { last = src; } src--; } if( last ) Q_strncpyz( extension, last, sizeof(extension) ); return extension; } /* ================== COM_DefaultExtension If path doesn't have extension, appends one to it If there is no room for it overwrites the end of the path ================== */ void COM_DefaultExtension( char *path, const char *extension, size_t size ) { char *src; size_t extlen = strlen(extension); assert( extlen > 0 && extlen < size ); // if path doesn't have a .EXT, append extension // (extension should include the .) src = path + strlen(path) - 1; while( *src != '/' && src != path ) { if( *src == '.' ) return; // it has an extension src--; } if( strlen(path)+extlen >= size ) path[size-extlen-1] = 0; Q_strncatz( path, extension, size ); } /* ================== COM_ReplaceExtension Replaces current extension, if there is none appends one If there is no room for it overwrites the end of the path ================== */ void COM_ReplaceExtension( char *path, const char *extension, size_t size ) { COM_StripExtension( path ); COM_DefaultExtension( path, extension, size ); } /* ============ COM_FileBase ============ */ void COM_FileBase (char *in, char *out) { char *p, *start, *end; int length; end = in + strlen(in); start = in; for (p = end-1 ; p > in ; p--) { if (*p == '.') end = p; else if (*p == '/') { start = p + 1; break; } } length = end - start; if (length <= 0) strcpy (out, "?empty filename?"); else { if (length > 31) length = 31; memcpy (out, start, length); out[length] = 0; } } /* ============ COM_FilePath Returns the path up to, but not including the last / ============ */ void COM_FilePath (char *in, char *out) { char *s; s = in + strlen(in) - 1; while (s != in && *s != '/') { s--; } strncpy (out,in, s-in); out[s-in] = 0; } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) short (*BigShort) (short l); short (*LittleShort) (short l); int (*BigLong) (int l); int (*LittleLong) (int l); float (*BigFloat) (float l); float (*LittleFloat) (float l); #endif short ShortSwap (short l) { qbyte b1, b2; b1 = l&255; b2 = (l>>8)&255; return (b1<<8) + b2; } #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) short ShortNoSwap (short l) { return l; } #endif int LongSwap (int l) { qbyte b1, b2, b3, b4; b1 = l&255; b2 = (l>>8)&255; b3 = (l>>16)&255; b4 = (l>>24)&255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) int LongNoSwap (int l) { return l; } #endif float FloatSwap (float f) { union { float f; qbyte b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) float FloatNoSwap (float f) { return f; } #endif /* ================ Swap_Init ================ */ void Swap_Init (void) { #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) qbyte swaptest[2] = {1,0}; // set the byte swapping variables in a portable manner if ( *(short *)swaptest == 1) { BigShort = ShortSwap; LittleShort = ShortNoSwap; BigLong = LongSwap; LittleLong = LongNoSwap; BigFloat = FloatSwap; LittleFloat = FloatNoSwap; } else { BigShort = ShortNoSwap; LittleShort = ShortSwap; BigLong = LongNoSwap; LittleLong = LongSwap; BigFloat = FloatNoSwap; LittleFloat = FloatSwap; } #endif } /* ============= TempVector This is just a convenience function for making temporary vectors for function calls ============= */ float *tv (float x, float y, float z) { static int index; static vec3_t vecs[8]; float *v; // use an array so that multiple tempvectors won't collide // for a while v = vecs[index]; index = (index + 1)&7; v[0] = x; v[1] = y; v[2] = z; return v; } /* ============= VectorToString This is just a convenience function for printing vectors ============= */ char *vtos (vec3_t v) { static int index; static char str[8][32]; char *s; // use an array so that multiple vtos won't collide s = str[index]; index = (index + 1)&7; Q_snprintfz (s, 32, "(%+6.3f %+6.3f %+6.3f)", v[0], v[1], v[2]); return s; } /* ============ va does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functions. ============ */ char *va(char *format, ...) { va_list argptr; static int str_index; static char string[2][2048]; str_index = !str_index; va_start (argptr, format); vsnprintf (string[str_index], sizeof(string[str_index]), format, argptr); va_end (argptr); return string[str_index]; } char com_token[MAX_TOKEN_CHARS]; /* ============== COM_ParseExt Parse a token out of a string ============== */ char *COM_ParseExt2( char **data_p, qboolean nl, qboolean sq ) { int c; int len; char *data; qboolean newlines = qfalse; data = *data_p; len = 0; com_token[0] = 0; if (!data) { *data_p = NULL; return ""; } // skip whitespace skipwhite: while ( (c = *data) <= ' ') { if (c == 0) { *data_p = NULL; return ""; } if (c == '\n') newlines = qtrue; data++; } if ( newlines && !nl ) { *data_p = data; return com_token; } // skip // comments if (c == '/' && data[1] == '/') { data += 2; while (*data && *data != '\n') data++; goto skipwhite; } // skip /* */ comments if (c == '/' && data[1] == '*') { data += 2; while (1) { if (!*data) break; if (*data != '*' || *(data+1) != '/') data++; else { data += 2; break; } } goto skipwhite; } // handle quoted strings specially if (c == '\"') { if (sq) data++; while (1) { c = *data++; if (c=='\"' || !c) { if ((len < MAX_TOKEN_CHARS) && (!sq)) { com_token[len] = c; len++; // data++; } if (len == MAX_TOKEN_CHARS) { // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); len = 0; } com_token[len] = 0; *data_p = data; return com_token; } if (len < MAX_TOKEN_CHARS) { com_token[len] = c; len++; } } } // parse a regular word do { if (len < MAX_TOKEN_CHARS) { com_token[len] = c; len++; } data++; c = *data; } while (c>32); if (len == MAX_TOKEN_CHARS) { // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); len = 0; } com_token[len] = 0; *data_p = data; return com_token; } /* ============== COM_RemoveColorTokens Remove color tokens from a string ============== */ char *COM_RemoveColorTokens( const char *in ) { static char cleanString[MAX_STRING_CHARS]; char *out = cleanString; memset( cleanString, 0, MAX_STRING_CHARS); while( *in ) { if( Q_IsColorString( in ) ) { in += 2; continue; } *out = *in; in++; out++; } return cleanString; } /* ============== COM_RemoveJunkChars Remove junk chars from a string (created for autoaction filenames) ============== */ char *COM_RemoveJunkChars( const char *in ) { static char cleanString2[MAX_STRING_CHARS]; char *out = cleanString2; memset( cleanString2, 0, MAX_STRING_CHARS ); while( *in ) { if( isalpha(*in) || isdigit(*in) ) { // keep it *out=*in; in++; out++; } else if ( *in == '<' || *in == '[' || *in == '{' ) { *out = '('; in++; out++; } else if ( *in == '>' || *in == ']' || *in == '}' ) { *out = ')'; in++; out++; } else if ( *in == '.' ) { *out = '_'; in++; out++; } else { // another char // skip it in++; } } return cleanString2; } /* ============== COM_ReadColorRGBString ============== */ int COM_ReadColorRGBString( const char *in ) { static int playerColor[3]; if( in && strlen(in) ) { if( sscanf(in, "%i %i %i", &playerColor[0], &playerColor[1], &playerColor[2]) == 3 ) { return COLOR_RGB( playerColor[0], playerColor[1], playerColor[2] ); } } return -1; } /* ============================================================================ LIBRARY REPLACEMENT FUNCTIONS ============================================================================ */ /* ============== Q_strncpyz ============== */ void Q_strncpyz( char *dest, const char *src, size_t size ) { #ifdef HAVE_STRLCPY strlcpy( dest, src, size ); #else if( size ) { while( --size && (*dest++ = *src++) ); *dest = '\0'; } #endif } /* ============== Q_strncatz ============== */ void Q_strncatz( char *dest, const char *src, size_t size ) { #ifdef HAVE_STRLCAT strlcat( dest, src, size ); #else if( size ) { while( --size && *dest++ ); if( size ) { dest--; size++; while( --size && (*dest++ = *src++) ); } *dest = '\0'; } #endif } /* ============== Q_snprintfz ============== */ void Q_snprintfz( char *dest, size_t size, const char *fmt, ... ) { va_list argptr; if( size ) { va_start( argptr, fmt ); vsnprintf( dest, size, fmt, argptr ); va_end( argptr ); dest[size-1] = 0; } } /* ============== Q_strupr ============== */ char *Q_strupr( char *s ) { char *p; if( s ) { for( p = s; *s; s++ ) *s = toupper( *s ); return p; } return NULL; } /* ============== Q_strlwr ============== */ char *Q_strlwr( char *s ) { char *p; if( s ) { for( p = s; *s; s++ ) *s = tolower( *s ); return p; } return NULL; } /* ============================================================================ WILDCARD COMPARES FUNCTIONS ============================================================================ */ /* ============== Q_WildCmpAfterStar ============== */ static qboolean Q_WildCmpAfterStar( const char *pattern, const char *text ) { char c, c1; const char *p = pattern, *t = text; while( (c = *p++) == '?' || c == '*' ) { if( c == '?' && *t++ == '\0' ) return qfalse; } if( c == '\0' ) return qtrue; for( c1 = ((c == '\\') ? *p : c); ; ) { if( tolower( *t ) == c1 && Q_WildCmp( p - 1, t ) ) return qtrue; if( *t++ == '\0' ) return qfalse; } } /* ============== Q_WildCmp ============== */ qboolean Q_WildCmp( const char *pattern, const char *text ) { char c; while( (c = *pattern++) != '\0' ) { switch( c ) { case '?': if( *text++ == '\0' ) return qfalse; break; case '\\': if( tolower( *pattern++ ) != tolower( *text++ ) ) return qfalse; break; case '*': return Q_WildCmpAfterStar( pattern, text ); default: if( tolower( c ) != tolower( *text++ ) ) return qfalse; } } return (*text == '\0'); } /* ===================================================================== INFO STRINGS ===================================================================== */ /* =============== Info_ValueForKey Searches the string for the given key and returns the associated value, or an empty string. =============== */ char *Info_ValueForKey( char *s, char *key ) { char pkey[512]; static char value[2][512]; // use two buffers so compares // work without stomping on each other static int valueindex; char *o; valueindex ^= 1; if (*s == '\\') s++; while (1) { o = pkey; while (*s != '\\') { if (!*s) return ""; *o++ = *s++; } *o = 0; s++; o = value[valueindex]; while (*s != '\\' && *s) { if (!*s) return ""; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) return value[valueindex]; if (!*s) return ""; s++; } } void Info_RemoveKey( char *s, char *key ) { char *start; char pkey[512]; char value[512]; char *o; if (strstr (key, "\\")) { // Com_Printf ("Can't use a key with a \\\n"); return; } while (1) { start = s; if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) { strcpy (start, s); // remove this part return; } if (!*s) return; } } /* ================== Info_Validate Some characters are illegal in info strings because they can mess up the server's parsing ================== */ qboolean Info_Validate( char *s ) { if( strstr(s, "\"") ) return qfalse; if( strstr(s, ";") ) return qfalse; return qtrue; } void Info_SetValueForKey (char *s, char *key, char *value) { char newi[MAX_INFO_STRING], *v; int c; if (strstr (key, "\\") || strstr (value, "\\") ) { Com_Printf ("Can't use keys or values with a \\\n"); return; } if (strstr (key, ";") ) { Com_Printf ("Can't use keys or values with a semicolon\n"); return; } if (strstr (key, "\"") || strstr (value, "\"") ) { Com_Printf ("Can't use keys or values with a \"\n"); return; } if (strlen(key) > MAX_INFO_KEY-1 || strlen(value) > MAX_INFO_KEY-1) { Com_Printf ("Keys and values must be less than %i characters.\n", MAX_INFO_KEY); return; } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; Q_snprintfz (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) > MAX_INFO_STRING) { Com_Printf ("Info string length exceeded\n"); return; } // only copy ascii values s += strlen(s); v = newi; while (*v) { c = *v++; c &= 127; // strip high bits if (c >= 32 && c < 127) *s++ = c; } *s = 0; }