]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/splines/q_shared.cpp
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / splines / q_shared.cpp
diff --git a/libs/splines/q_shared.cpp b/libs/splines/q_shared.cpp
new file mode 100644 (file)
index 0000000..39fccee
--- /dev/null
@@ -0,0 +1,976 @@
+/*\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