--- /dev/null
+/*\r
+Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
+For a list of contributors, see the accompanying CONTRIBUTORS file.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+*/\r
+\r
+// q_shared.c -- stateless support routines that are included in each code dll\r
+#include "q_shared.h"\r
+\r
+/*\r
+============================================================================\r
+\r
+GROWLISTS\r
+\r
+============================================================================\r
+*/\r
+\r
+// malloc / free all in one place for debugging\r
+extern "C" void *Com_Allocate( int bytes );\r
+extern "C" void Com_Dealloc( void *ptr );\r
+\r
+void Com_InitGrowList( growList_t *list, int maxElements ) {\r
+ list->maxElements = maxElements;\r
+ list->currentElements = 0;\r
+ list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );\r
+}\r
+\r
+int Com_AddToGrowList( growList_t *list, void *data ) {\r
+ void **old;\r
+\r
+ if ( list->currentElements != list->maxElements ) {\r
+ list->elements[list->currentElements] = data;\r
+ return list->currentElements++;\r
+ }\r
+\r
+ // grow, reallocate and move\r
+ old = list->elements;\r
+\r
+ if ( list->maxElements < 0 ) {\r
+ Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements );\r
+ }\r
+\r
+ if ( list->maxElements == 0 ) {\r
+ // initialize the list to hold 100 elements\r
+ Com_InitGrowList( list, 100 );\r
+ return Com_AddToGrowList( list, data );\r
+ }\r
+\r
+ list->maxElements *= 2;\r
+\r
+ Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements );\r
+\r
+ list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );\r
+\r
+ if ( !list->elements ) {\r
+ Com_Error( ERR_DROP, "Growlist alloc failed" );\r
+ }\r
+\r
+ memcpy( list->elements, old, list->currentElements * sizeof( void * ) );\r
+\r
+ Com_Dealloc( old );\r
+\r
+ return Com_AddToGrowList( list, data );\r
+}\r
+\r
+void *Com_GrowListElement( const growList_t *list, int index ) {\r
+ if ( index < 0 || index >= list->currentElements ) {\r
+ Com_Error( ERR_DROP, "Com_GrowListElement: %i out of range of %i", \r
+ index, list->currentElements );\r
+ }\r
+ return list->elements[index];\r
+}\r
+\r
+int Com_IndexForGrowListElement( const growList_t *list, const void *element ) {\r
+ int i;\r
+\r
+ for ( i = 0 ; i < list->currentElements ; i++ ) {\r
+ if ( list->elements[i] == element ) {\r
+ return i;\r
+ }\r
+ }\r
+ return -1;\r
+}\r
+\r
+//============================================================================\r
+\r
+\r
+float Com_Clamp( float min, float max, float value ) {\r
+ if ( value < min ) {\r
+ return min;\r
+ }\r
+ if ( value > max ) {\r
+ return max;\r
+ }\r
+ return value;\r
+}\r
+\r
+/*\r
+============\r
+Com_StringContains\r
+============\r
+*/\r
+const char *Com_StringContains( const char *str1, const char *str2, int casesensitive) {\r
+ int len, i, j;\r
+\r
+ len = strlen(str1) - strlen(str2);\r
+ for (i = 0; i <= len; i++, str1++) {\r
+ for (j = 0; str2[j]; j++) {\r
+ if (casesensitive) {\r
+ if (str1[j] != str2[j]) {\r
+ break;\r
+ }\r
+ }\r
+ else {\r
+ if (toupper(str1[j]) != toupper(str2[j])) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (!str2[j]) {\r
+ return str1;\r
+ }\r
+ }\r
+ return NULL;\r
+}\r
+\r
+/*\r
+============\r
+Com_Filter\r
+============\r
+*/\r
+int Com_Filter( const char *filter, const char *name, int casesensitive)\r
+{\r
+ char buf[MAX_TOKEN_CHARS];\r
+ const char *ptr;\r
+ int i, found;\r
+\r
+ while(*filter) {\r
+ if (*filter == '*') {\r
+ filter++;\r
+ for (i = 0; *filter; i++) {\r
+ if (*filter == '*' || *filter == '?') break;\r
+ buf[i] = *filter;\r
+ filter++;\r
+ }\r
+ buf[i] = '\0';\r
+ if (strlen(buf)) {\r
+ ptr = Com_StringContains(name, buf, casesensitive);\r
+ if (!ptr) return qfalse;\r
+ name = ptr + strlen(buf);\r
+ }\r
+ }\r
+ else if (*filter == '?') {\r
+ filter++;\r
+ name++;\r
+ }\r
+ else if (*filter == '[' && *(filter+1) == '[') {\r
+ filter++;\r
+ }\r
+ else if (*filter == '[') {\r
+ filter++;\r
+ found = qfalse;\r
+ while(*filter && !found) {\r
+ if (*filter == ']' && *(filter+1) != ']') break;\r
+ if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {\r
+ if (casesensitive) {\r
+ if (*name >= *filter && *name <= *(filter+2)) found = qtrue;\r
+ }\r
+ else {\r
+ if (toupper(*name) >= toupper(*filter) &&\r
+ toupper(*name) <= toupper(*(filter+2))) found = qtrue;\r
+ }\r
+ filter += 3;\r
+ }\r
+ else {\r
+ if (casesensitive) {\r
+ if (*filter == *name) found = qtrue;\r
+ }\r
+ else {\r
+ if (toupper(*filter) == toupper(*name)) found = qtrue;\r
+ }\r
+ filter++;\r
+ }\r
+ }\r
+ if (!found) return qfalse;\r
+ while(*filter) {\r
+ if (*filter == ']' && *(filter+1) != ']') break;\r
+ filter++;\r
+ }\r
+ filter++;\r
+ name++;\r
+ }\r
+ else {\r
+ if (casesensitive) {\r
+ if (*filter != *name) return qfalse;\r
+ }\r
+ else {\r
+ if (toupper(*filter) != toupper(*name)) return qfalse;\r
+ }\r
+ filter++;\r
+ name++;\r
+ }\r
+ }\r
+ return qtrue;\r
+}\r
+\r
+\r
+/*\r
+================\r
+Com_HashString\r
+\r
+================\r
+*/\r
+int Com_HashString( const char *fname ) {\r
+ int i;\r
+ long hash;\r
+ char letter;\r
+\r
+ hash = 0;\r
+ i = 0;\r
+ while (fname[i] != '\0') {\r
+ letter = tolower(fname[i]);\r
+ if (letter =='.') break; // don't include extension\r
+ if (letter =='\\') letter = '/'; // damn path names\r
+ hash+=(long)(letter)*(i+119);\r
+ i++;\r
+ }\r
+ hash &= (FILE_HASH_SIZE-1);\r
+ return hash;\r
+}\r
+\r
+\r
+/*\r
+============\r
+Com_SkipPath\r
+============\r
+*/\r
+char *Com_SkipPath (char *pathname)\r
+{\r
+ char *last;\r
+ \r
+ last = pathname;\r
+ while (*pathname)\r
+ {\r
+ if (*pathname=='/')\r
+ last = pathname+1;\r
+ pathname++;\r
+ }\r
+ return last;\r
+}\r
+\r
+/*\r
+============\r
+Com_StripExtension\r
+============\r
+*/\r
+void Com_StripExtension( const char *in, char *out ) {\r
+ while ( *in && *in != '.' ) {\r
+ *out++ = *in++;\r
+ }\r
+ *out = 0;\r
+}\r
+\r
+\r
+/*\r
+==================\r
+Com_DefaultExtension\r
+==================\r
+*/\r
+void Com_DefaultExtension (char *path, int maxSize, const char *extension ) {\r
+ char oldPath[MAX_QPATH];\r
+ char *src;\r
+\r
+//\r
+// if path doesn't have a .EXT, append extension\r
+// (extension should include the .)\r
+//\r
+ src = path + strlen(path) - 1;\r
+\r
+ while (*src != '/' && src != path) {\r
+ if ( *src == '.' ) {\r
+ return; // it has an extension\r
+ }\r
+ src--;\r
+ }\r
+\r
+ Q_strncpyz( oldPath, path, sizeof( oldPath ) );\r
+ Com_sprintf( path, maxSize, "%s%s", oldPath, extension );\r
+}\r
+\r
+/*\r
+============================================================================\r
+\r
+ BYTE ORDER FUNCTIONS\r
+\r
+============================================================================\r
+*/\r
+\r
+// can't just use function pointers, or dll linkage can\r
+// mess up when qcommon is included in multiple places\r
+static short (*_BigShort) (short l);\r
+static short (*_LittleShort) (short l);\r
+static int (*_BigLong) (int l);\r
+static int (*_LittleLong) (int l);\r
+static float (*_BigFloat) (float l);\r
+static float (*_LittleFloat) (float l);\r
+\r
+short BigShort(short l){return _BigShort(l);}\r
+short LittleShort(short l) {return _LittleShort(l);}\r
+int BigLong (int l) {return _BigLong(l);}\r
+int LittleLong (int l) {return _LittleLong(l);}\r
+float BigFloat (float l) {return _BigFloat(l);}\r
+float LittleFloat (float l) {return _LittleFloat(l);}\r
+\r
+short ShortSwap (short l)\r
+{\r
+ byte b1,b2;\r
+\r
+ b1 = l&255;\r
+ b2 = (l>>8)&255;\r
+\r
+ return (b1<<8) + b2;\r
+}\r
+\r
+short ShortNoSwap (short l)\r
+{\r
+ return l;\r
+}\r
+\r
+int LongSwap (int l)\r
+{\r
+ byte b1,b2,b3,b4;\r
+\r
+ b1 = l&255;\r
+ b2 = (l>>8)&255;\r
+ b3 = (l>>16)&255;\r
+ b4 = (l>>24)&255;\r
+\r
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;\r
+}\r
+\r
+int LongNoSwap (int l)\r
+{\r
+ return l;\r
+}\r
+\r
+float FloatSwap (float f)\r
+{\r
+ union\r
+ {\r
+ float f;\r
+ byte b[4];\r
+ } dat1, dat2;\r
+ \r
+ \r
+ dat1.f = f;\r
+ dat2.b[0] = dat1.b[3];\r
+ dat2.b[1] = dat1.b[2];\r
+ dat2.b[2] = dat1.b[1];\r
+ dat2.b[3] = dat1.b[0];\r
+ return dat2.f;\r
+}\r
+\r
+float FloatNoSwap (float f)\r
+{\r
+ return f;\r
+}\r
+\r
+/*\r
+================\r
+Swap_Init\r
+================\r
+*/\r
+void Swap_Init (void)\r
+{\r
+ byte swaptest[2] = {1,0};\r
+\r
+// set the byte swapping variables in a portable manner \r
+ if ( *(short *)swaptest == 1)\r
+ {\r
+ _BigShort = ShortSwap;\r
+ _LittleShort = ShortNoSwap;\r
+ _BigLong = LongSwap;\r
+ _LittleLong = LongNoSwap;\r
+ _BigFloat = FloatSwap;\r
+ _LittleFloat = FloatNoSwap;\r
+ }\r
+ else\r
+ {\r
+ _BigShort = ShortNoSwap;\r
+ _LittleShort = ShortSwap;\r
+ _BigLong = LongNoSwap;\r
+ _LittleLong = LongSwap;\r
+ _BigFloat = FloatNoSwap;\r
+ _LittleFloat = FloatSwap;\r
+ }\r
+\r
+}\r
+\r
+/*\r
+===============\r
+Com_ParseInfos\r
+===============\r
+*/\r
+int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) {\r
+ const char *token;\r
+ int count;\r
+ char key[MAX_TOKEN_CHARS];\r
+\r
+ count = 0;\r
+\r
+ while ( 1 ) {\r
+ token = Com_Parse( &buf );\r
+ if ( !token[0] ) {\r
+ break;\r
+ }\r
+ if ( strcmp( token, "{" ) ) {\r
+ Com_Printf( "Missing { in info file\n" );\r
+ break;\r
+ }\r
+\r
+ if ( count == max ) {\r
+ Com_Printf( "Max infos exceeded\n" );\r
+ break;\r
+ }\r
+\r
+ infos[count][0] = 0;\r
+ while ( 1 ) {\r
+ token = Com_Parse( &buf );\r
+ if ( !token[0] ) {\r
+ Com_Printf( "Unexpected end of info file\n" );\r
+ break;\r
+ }\r
+ if ( !strcmp( token, "}" ) ) {\r
+ break;\r
+ }\r
+ Q_strncpyz( key, token, sizeof( key ) );\r
+\r
+ token = Com_ParseOnLine( &buf );\r
+ if ( !token[0] ) {\r
+ token = "<NULL>";\r
+ }\r
+ Info_SetValueForKey( infos[count], key, token );\r
+ }\r
+ count++;\r
+ }\r
+\r
+ return count;\r
+}\r
+\r
+\r
+\r
+/*\r
+============================================================================\r
+\r
+ LIBRARY REPLACEMENT FUNCTIONS\r
+\r
+============================================================================\r
+*/\r
+\r
+int Q_isprint( int c )\r
+{\r
+ if ( c >= 0x20 && c <= 0x7E )\r
+ return ( 1 );\r
+ return ( 0 );\r
+}\r
+\r
+int Q_islower( int c )\r
+{\r
+ if (c >= 'a' && c <= 'z')\r
+ return ( 1 );\r
+ return ( 0 );\r
+}\r
+\r
+int Q_isupper( int c )\r
+{\r
+ if (c >= 'A' && c <= 'Z')\r
+ return ( 1 );\r
+ return ( 0 );\r
+}\r
+\r
+int Q_isalpha( int c )\r
+{\r
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))\r
+ return ( 1 );\r
+ return ( 0 );\r
+}\r
+\r
+char* Q_strrchr( const char* string, int c )\r
+{\r
+ char cc = c;\r
+ char *s;\r
+ char *sp=(char *)0;\r
+\r
+ s = (char*)string;\r
+\r
+ while (*s)\r
+ {\r
+ if (*s == cc)\r
+ sp = s;\r
+ s++;\r
+ }\r
+ if (cc == 0)\r
+ sp = s;\r
+\r
+ return sp;\r
+}\r
+\r
+/*\r
+=============\r
+Q_strncpyz\r
+ \r
+Safe strncpy that ensures a trailing zero\r
+=============\r
+*/\r
+void Q_strncpyz( char *dest, const char *src, int destsize ) {\r
+ if ( !src ) {\r
+ Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );\r
+ }\r
+ if ( destsize < 1 ) {\r
+ Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); \r
+ }\r
+\r
+ strncpy( dest, src, destsize-1 );\r
+ dest[destsize-1] = 0;\r
+}\r
+ \r
+int Q_stricmpn (const char *s1, const char *s2, int n) {\r
+ int c1, c2;\r
+ \r
+ do {\r
+ c1 = *s1++;\r
+ c2 = *s2++;\r
+\r
+ if (!n--) {\r
+ return 0; // strings are equal until end point\r
+ }\r
+ \r
+ if (c1 != c2) {\r
+ if (c1 >= 'a' && c1 <= 'z') {\r
+ c1 -= ('a' - 'A');\r
+ }\r
+ if (c2 >= 'a' && c2 <= 'z') {\r
+ c2 -= ('a' - 'A');\r
+ }\r
+ if (c1 != c2) {\r
+ return c1 < c2 ? -1 : 1;\r
+ }\r
+ }\r
+ } while (c1);\r
+ \r
+ return 0; // strings are equal\r
+}\r
+\r
+int Q_strncmp (const char *s1, const char *s2, int n) {\r
+ int c1, c2;\r
+ \r
+ do {\r
+ c1 = *s1++;\r
+ c2 = *s2++;\r
+\r
+ if (!n--) {\r
+ return 0; // strings are equal until end point\r
+ }\r
+ \r
+ if (c1 != c2) {\r
+ return c1 < c2 ? -1 : 1;\r
+ }\r
+ } while (c1);\r
+ \r
+ return 0; // strings are equal\r
+}\r
+\r
+int Q_stricmp (const char *s1, const char *s2) {\r
+ return Q_stricmpn (s1, s2, 99999);\r
+}\r
+\r
+\r
+char *Q_strlwr( char *s1 ) {\r
+ char *s;\r
+\r
+ s = s1;\r
+ while ( *s ) {\r
+ *s = tolower(*s);\r
+ s++;\r
+ }\r
+ return s1;\r
+}\r
+\r
+char *Q_strupr( char *s1 ) {\r
+ char *s;\r
+\r
+ s = s1;\r
+ while ( *s ) {\r
+ *s = toupper(*s);\r
+ s++;\r
+ }\r
+ return s1;\r
+}\r
+\r
+\r
+// never goes past bounds or leaves without a terminating 0\r
+void Q_strcat( char *dest, int size, const char *src ) {\r
+ int l1;\r
+\r
+ l1 = strlen( dest );\r
+ if ( l1 >= size ) {\r
+ Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );\r
+ }\r
+ Q_strncpyz( dest + l1, src, size - l1 );\r
+}\r
+\r
+\r
+int Q_PrintStrlen( const char *string ) {\r
+ int len;\r
+ const char *p;\r
+\r
+ if( !string ) {\r
+ return 0;\r
+ }\r
+\r
+ len = 0;\r
+ p = string;\r
+ while( *p ) {\r
+ if( Q_IsColorString( p ) ) {\r
+ p += 2;\r
+ continue;\r
+ }\r
+ p++;\r
+ len++;\r
+ }\r
+\r
+ return len;\r
+}\r
+\r
+\r
+char *Q_CleanStr( char *string ) {\r
+ char* d;\r
+ char* s;\r
+ int c;\r
+\r
+ s = string;\r
+ d = string;\r
+ while ((c = *s) != 0 ) {\r
+ if ( Q_IsColorString( s ) ) {\r
+ s++;\r
+ } \r
+ else if ( c >= 0x20 && c <= 0x7E ) {\r
+ *d++ = c;\r
+ }\r
+ s++;\r
+ }\r
+ *d = '\0';\r
+\r
+ return string;\r
+}\r
+\r
+\r
+void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {\r
+ int len;\r
+ va_list argptr;\r
+ char bigbuffer[32000]; // big, but small enough to fit in PPC stack\r
+\r
+ va_start (argptr,fmt);\r
+ len = vsprintf (bigbuffer,fmt,argptr);\r
+ va_end (argptr);\r
+ if ( len >= sizeof( bigbuffer ) ) {\r
+ Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );\r
+ }\r
+ if (len >= size) {\r
+ Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);\r
+ }\r
+ Q_strncpyz (dest, bigbuffer, size );\r
+}\r
+\r
+\r
+/*\r
+============\r
+va\r
+\r
+does a varargs printf into a temp buffer, so I don't need to have\r
+varargs versions of all text functions.\r
+FIXME: make this buffer size safe someday\r
+============\r
+*/\r
+char * QDECL va( char *format, ... ) {\r
+ va_list argptr;\r
+ static char string[2][32000]; // in case va is called by nested functions\r
+ static int index = 0;\r
+ char *buf;\r
+\r
+ buf = string[index & 1];\r
+ index++;\r
+\r
+ va_start (argptr, format);\r
+ vsprintf (buf, format,argptr);\r
+ va_end (argptr);\r
+\r
+ return buf;\r
+}\r
+\r
+\r
+/*\r
+=====================================================================\r
+\r
+ INFO STRINGS\r
+\r
+=====================================================================\r
+*/\r
+\r
+/*\r
+===============\r
+Info_ValueForKey\r
+\r
+Searches the string for the given\r
+key and returns the associated value, or an empty string.\r
+FIXME: overflow check?\r
+===============\r
+*/\r
+char *Info_ValueForKey( const char *s, const char *key ) {\r
+ char pkey[MAX_INFO_KEY];\r
+ static char value[2][MAX_INFO_VALUE]; // use two buffers so compares\r
+ // work without stomping on each other\r
+ static int valueindex = 0;\r
+ char *o;\r
+ \r
+ if ( !s || !key ) {\r
+ return "";\r
+ }\r
+\r
+ if ( strlen( s ) >= MAX_INFO_STRING ) {\r
+ Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );\r
+ }\r
+\r
+ valueindex ^= 1;\r
+ if (*s == '\\')\r
+ s++;\r
+ while (1)\r
+ {\r
+ o = pkey;\r
+ while (*s != '\\')\r
+ {\r
+ if (!*s)\r
+ return "";\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+ s++;\r
+\r
+ o = value[valueindex];\r
+\r
+ while (*s != '\\' && *s)\r
+ {\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+\r
+ if (!Q_stricmp (key, pkey) )\r
+ return value[valueindex];\r
+\r
+ if (!*s)\r
+ break;\r
+ s++;\r
+ }\r
+\r
+ return "";\r
+}\r
+\r
+\r
+/*\r
+===================\r
+Info_NextPair\r
+\r
+Used to itterate through all the key/value pairs in an info string\r
+===================\r
+*/\r
+void Info_NextPair( const char *(*head), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {\r
+ char *o;\r
+ const char *s;\r
+\r
+ s = *head;\r
+\r
+ if ( *s == '\\' ) {\r
+ s++;\r
+ }\r
+ key[0] = 0;\r
+ value[0] = 0;\r
+\r
+ o = key;\r
+ while ( *s != '\\' ) {\r
+ if ( !*s ) {\r
+ *o = 0;\r
+ *head = s;\r
+ return;\r
+ }\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+ s++;\r
+\r
+ o = value;\r
+ while ( *s != '\\' && *s ) {\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+\r
+ *head = s;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+Info_RemoveKey\r
+===================\r
+*/\r
+void Info_RemoveKey( char *s, const char *key ) {\r
+ char *start;\r
+ char pkey[MAX_INFO_KEY];\r
+ char value[MAX_INFO_VALUE];\r
+ char *o;\r
+\r
+ if ( strlen( s ) >= MAX_INFO_STRING ) {\r
+ Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );\r
+ }\r
+\r
+ if (strchr (key, '\\')) {\r
+ return;\r
+ }\r
+\r
+ while (1)\r
+ {\r
+ start = s;\r
+ if (*s == '\\')\r
+ s++;\r
+ o = pkey;\r
+ while (*s != '\\')\r
+ {\r
+ if (!*s)\r
+ return;\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+ s++;\r
+\r
+ o = value;\r
+ while (*s != '\\' && *s)\r
+ {\r
+ if (!*s)\r
+ return;\r
+ *o++ = *s++;\r
+ }\r
+ *o = 0;\r
+\r
+ if (!strcmp (key, pkey) )\r
+ {\r
+ strcpy (start, s); // remove this part\r
+ return;\r
+ }\r
+\r
+ if (!*s)\r
+ return;\r
+ }\r
+\r
+}\r
+\r
+\r
+/*\r
+==================\r
+Info_Validate\r
+\r
+Some characters are illegal in info strings because they\r
+can mess up the server's parsing\r
+==================\r
+*/\r
+qboolean Info_Validate( const char *s ) {\r
+ if ( strchr( s, '\"' ) ) {\r
+ return qfalse;\r
+ }\r
+ if ( strchr( s, ';' ) ) {\r
+ return qfalse;\r
+ }\r
+ return qtrue;\r
+}\r
+\r
+/*\r
+==================\r
+Info_SetValueForKey\r
+\r
+Changes or adds a key/value pair\r
+==================\r
+*/\r
+void Info_SetValueForKey( char *s, const char *key, const char *value ) {\r
+ char newi[MAX_INFO_STRING];\r
+\r
+ if ( strlen( s ) >= MAX_INFO_STRING ) {\r
+ Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );\r
+ }\r
+\r
+ if (strchr (key, '\\') || strchr (value, '\\'))\r
+ {\r
+ Com_Printf ("Can't use keys or values with a \\\n");\r
+ return;\r
+ }\r
+\r
+ if (strchr (key, ';') || strchr (value, ';'))\r
+ {\r
+ Com_Printf ("Can't use keys or values with a semicolon\n");\r
+ return;\r
+ }\r
+\r
+ if (strchr (key, '\"') || strchr (value, '\"'))\r
+ {\r
+ Com_Printf ("Can't use keys or values with a \"\n");\r
+ return;\r
+ }\r
+\r
+ Info_RemoveKey (s, key);\r
+ if (!value || !strlen(value))\r
+ return;\r
+\r
+ Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);\r
+\r
+ if (strlen(newi) + strlen(s) > MAX_INFO_STRING)\r
+ {\r
+ Com_Printf ("Info string length exceeded\n");\r
+ return;\r
+ }\r
+\r
+ strcat (s, newi);\r
+}\r
+\r
+//====================================================================\r
+\r
+\r
+/*\r
+===============\r
+ParseHex\r
+===============\r
+*/\r
+int ParseHex( const char *text ) {\r
+ int value;\r
+ int c;\r
+\r
+ value = 0;\r
+ while ( ( c = *text++ ) != 0 ) {\r
+ if ( c >= '0' && c <= '9' ) {\r
+ value = value * 16 + c - '0';\r
+ continue;\r
+ }\r
+ if ( c >= 'a' && c <= 'f' ) {\r
+ value = value * 16 + 10 + c - 'a';\r
+ continue;\r
+ }\r
+ if ( c >= 'A' && c <= 'F' ) {\r
+ value = value * 16 + 10 + c - 'A';\r
+ continue;\r
+ }\r
+ }\r
+\r
+ return value;\r
+}\r