Remove -Wno-sign-compare
[xonotic/netradiant.git] / libs / splines / q_shared.cpp
index 39fccee..1df4dfc 100644 (file)
-/*\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
+/*
+   Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+   For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant 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.
+
+   GtkRadiant 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 GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+// q_shared.c -- stateless support routines that are included in each code dll
+#include "q_shared.h"
+
+/*
+   ============================================================================
+
+   GROWLISTS
+
+   ============================================================================
+ */
+
+// malloc / free all in one place for debugging
+extern "C" void *Com_Allocate( int bytes );
+extern "C" void Com_Dealloc( void *ptr );
+
+void Com_InitGrowList( growList_t *list, int maxElements ) {
+       list->maxElements = maxElements;
+       list->currentElements = 0;
+       list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );
+}
+
+int Com_AddToGrowList( growList_t *list, void *data ) {
+       void    **old;
+
+       if ( list->currentElements != list->maxElements ) {
+               list->elements[list->currentElements] = data;
+               return list->currentElements++;
+       }
+
+       // grow, reallocate and move
+       old = list->elements;
+
+       if ( list->maxElements < 0 ) {
+               Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements );
+       }
+
+       if ( list->maxElements == 0 ) {
+               // initialize the list to hold 100 elements
+               Com_InitGrowList( list, 100 );
+               return Com_AddToGrowList( list, data );
+       }
+
+       list->maxElements *= 2;
+
+       Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements );
+
+       list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );
+
+       if ( !list->elements ) {
+               Com_Error( ERR_DROP, "Growlist alloc failed" );
+       }
+
+       memcpy( list->elements, old, list->currentElements * sizeof( void * ) );
+
+       Com_Dealloc( old );
+
+       return Com_AddToGrowList( list, data );
+}
+
+void *Com_GrowListElement( const growList_t *list, int index ) {
+       if ( index < 0 || index >= list->currentElements ) {
+               Com_Error( ERR_DROP, "Com_GrowListElement: %i out of range of %i",
+                                  index, list->currentElements );
+       }
+       return list->elements[index];
+}
+
+int Com_IndexForGrowListElement( const growList_t *list, const void *element ) {
+       int i;
+
+       for ( i = 0 ; i < list->currentElements ; i++ ) {
+               if ( list->elements[i] == element ) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+//============================================================================
+
+
+float Com_Clamp( float min, float max, float value ) {
+       if ( value < min ) {
+               return min;
+       }
+       if ( value > max ) {
+               return max;
+       }
+       return value;
+}
+
+/*
+   ============
+   Com_StringContains
+   ============
+ */
+const char *Com_StringContains( const char *str1, const char *str2, int casesensitive ) {
+       int len, i, j;
+
+       len = strlen( str1 ) - strlen( str2 );
+       for ( i = 0; i <= len; i++, str1++ ) {
+               for ( j = 0; str2[j]; j++ ) {
+                       if ( casesensitive ) {
+                               if ( str1[j] != str2[j] ) {
+                                       break;
+                               }
+                       }
+                       else {
+                               if ( toupper( str1[j] ) != toupper( str2[j] ) ) {
+                                       break;
+                               }
+                       }
+               }
+               if ( !str2[j] ) {
+                       return str1;
+               }
+       }
+       return NULL;
+}
+
+/*
+   ============
+   Com_Filter
+   ============
+ */
+int Com_Filter( const char *filter, const char *name, int casesensitive ){
+       char buf[MAX_TOKEN_CHARS];
+       const char *ptr;
+       int i, found;
+
+       while ( *filter ) {
+               if ( *filter == '*' ) {
+                       filter++;
+                       for ( i = 0; *filter; i++ ) {
+                               if ( *filter == '*' || *filter == '?' ) {
+                                       break;
+                               }
+                               buf[i] = *filter;
+                               filter++;
+                       }
+                       buf[i] = '\0';
+                       if ( strlen( buf ) ) {
+                               ptr = Com_StringContains( name, buf, casesensitive );
+                               if ( !ptr ) {
+                                       return qfalse;
+                               }
+                               name = ptr + strlen( buf );
+                       }
+               }
+               else if ( *filter == '?' ) {
+                       filter++;
+                       name++;
+               }
+               else if ( *filter == '[' && *( filter + 1 ) == '[' ) {
+                       filter++;
+               }
+               else if ( *filter == '[' ) {
+                       filter++;
+                       found = qfalse;
+                       while ( *filter && !found ) {
+                               if ( *filter == ']' && *( filter + 1 ) != ']' ) {
+                                       break;
+                               }
+                               if ( *( filter + 1 ) == '-' && *( filter + 2 ) && ( *( filter + 2 ) != ']' || *( filter + 3 ) == ']' ) ) {
+                                       if ( casesensitive ) {
+                                               if ( *name >= *filter && *name <= *( filter + 2 ) ) {
+                                                       found = qtrue;
+                                               }
+                                       }
+                                       else {
+                                               if ( toupper( *name ) >= toupper( *filter ) &&
+                                                        toupper( *name ) <= toupper( *( filter + 2 ) ) ) {
+                                                       found = qtrue;
+                                               }
+                                       }
+                                       filter += 3;
+                               }
+                               else {
+                                       if ( casesensitive ) {
+                                               if ( *filter == *name ) {
+                                                       found = qtrue;
+                                               }
+                                       }
+                                       else {
+                                               if ( toupper( *filter ) == toupper( *name ) ) {
+                                                       found = qtrue;
+                                               }
+                                       }
+                                       filter++;
+                               }
+                       }
+                       if ( !found ) {
+                               return qfalse;
+                       }
+                       while ( *filter ) {
+                               if ( *filter == ']' && *( filter + 1 ) != ']' ) {
+                                       break;
+                               }
+                               filter++;
+                       }
+                       filter++;
+                       name++;
+               }
+               else {
+                       if ( casesensitive ) {
+                               if ( *filter != *name ) {
+                                       return qfalse;
+                               }
+                       }
+                       else {
+                               if ( toupper( *filter ) != toupper( *name ) ) {
+                                       return qfalse;
+                               }
+                       }
+                       filter++;
+                       name++;
+               }
+       }
+       return qtrue;
+}
+
+
+/*
+   ================
+   Com_HashString
+
+   ================
+ */
+int Com_HashString( const char *fname ) {
+       int i;
+       long hash;
+       char letter;
+
+       hash = 0;
+       i = 0;
+       while ( fname[i] != '\0' ) {
+               letter = tolower( fname[i] );
+               if ( letter == '.' ) {
+                       break;                              // don't include extension
+               }
+               if ( letter == '\\' ) {
+                       letter = '/';                       // damn path names
+               }
+               hash += (long)( letter ) * ( i + 119 );
+               i++;
+       }
+       hash &= ( FILE_HASH_SIZE - 1 );
+       return hash;
+}
+
+
+/*
+   ============
+   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( const char *in, char *out ) {
+       while ( *in && *in != '.' ) {
+               *out++ = *in++;
+       }
+       *out = 0;
+}
+
+
+/*
+   ==================
+   Com_DefaultExtension
+   ==================
+ */
+void Com_DefaultExtension( char *path, int maxSize, const char *extension ) {
+       char oldPath[MAX_QPATH];
+       char    *src;
+
+//
+// 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--;
+       }
+
+       Q_strncpyz( oldPath, path, sizeof( oldPath ) );
+       Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
+}
+
+/*
+   ============================================================================
+
+                    BYTE ORDER FUNCTIONS
+
+   ============================================================================
+ */
+
+// can't just use function pointers, or dll linkage can
+// mess up when qcommon is included in multiple places
+static short ( *_BigShort )( short l );
+static short ( *_LittleShort )( short l );
+static int ( *_BigLong )( int l );
+static int ( *_LittleLong )( int l );
+static float ( *_BigFloat )( float l );
+static float ( *_LittleFloat )( float l );
+
+short   BigShort( short l ){return _BigShort( l ); }
+short   LittleShort( short l ) {return _LittleShort( l ); }
+int     BigLong( int l ) {return _BigLong( l ); }
+int     LittleLong( int l ) {return _LittleLong( l ); }
+float   BigFloat( float l ) {return _BigFloat( l ); }
+float   LittleFloat( float l ) {return _LittleFloat( l ); }
+
+short   ShortSwap( short l ){
+       byte b1,b2;
+
+       b1 = l & 255;
+       b2 = ( l >> 8 ) & 255;
+
+       return ( b1 << 8 ) + b2;
+}
+
+short   ShortNoSwap( short l ){
+       return l;
+}
+
+int    LongSwap( int l ){
+       byte 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;
+}
+
+int LongNoSwap( int l ){
+       return l;
+}
+
+float FloatSwap( float f ){
+       union
+       {
+               float f;
+               byte 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;
+}
+
+float FloatNoSwap( float f ){
+       return f;
+}
+
+/*
+   ================
+   Swap_Init
+   ================
+ */
+void Swap_Init( void ){
+       byte 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;
+       }
+
+}
+
+/*
+   ===============
+   Com_ParseInfos
+   ===============
+ */
+int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) {
+       const char  *token;
+       int count;
+       char key[MAX_TOKEN_CHARS];
+
+       count = 0;
+
+       while ( 1 ) {
+               token = Com_Parse( &buf );
+               if ( !token[0] ) {
+                       break;
+               }
+               if ( strcmp( token, "{" ) ) {
+                       Com_Printf( "Missing { in info file\n" );
+                       break;
+               }
+
+               if ( count == max ) {
+                       Com_Printf( "Max infos exceeded\n" );
+                       break;
+               }
+
+               infos[count][0] = 0;
+               while ( 1 ) {
+                       token = Com_Parse( &buf );
+                       if ( !token[0] ) {
+                               Com_Printf( "Unexpected end of info file\n" );
+                               break;
+                       }
+                       if ( !strcmp( token, "}" ) ) {
+                               break;
+                       }
+                       Q_strncpyz( key, token, sizeof( key ) );
+
+                       token = Com_ParseOnLine( &buf );
+                       if ( !token[0] ) {
+                               token = "<NULL>";
+                       }
+                       Info_SetValueForKey( infos[count], key, token );
+               }
+               count++;
+       }
+
+       return count;
+}
+
+
+
+/*
+   ============================================================================
+
+                    LIBRARY REPLACEMENT FUNCTIONS
+
+   ============================================================================
+ */
+
+int Q_isprint( int c ){
+       if ( c >= 0x20 && c <= 0x7E ) {
+               return ( 1 );
+       }
+       return ( 0 );
+}
+
+int Q_islower( int c ){
+       if ( c >= 'a' && c <= 'z' ) {
+               return ( 1 );
+       }
+       return ( 0 );
+}
+
+int Q_isupper( int c ){
+       if ( c >= 'A' && c <= 'Z' ) {
+               return ( 1 );
+       }
+       return ( 0 );
+}
+
+int Q_isalpha( int c ){
+       if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ) {
+               return ( 1 );
+       }
+       return ( 0 );
+}
+
+char* Q_strrchr( const char* string, int c ){
+       char cc = c;
+       char *s;
+       char *sp = (char *)0;
+
+       s = (char*)string;
+
+       while ( *s )
+       {
+               if ( *s == cc ) {
+                       sp = s;
+               }
+               s++;
+       }
+       if ( cc == 0 ) {
+               sp = s;
+       }
+
+       return sp;
+}
+
+/*
+   =============
+   Q_strncpyz
+
+   Safe strncpy that ensures a trailing zero
+   =============
+ */
+void Q_strncpyz( char *dest, const char *src, std::size_t destsize ) {
+       if ( !src ) {
+               Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
+       }
+       if ( destsize < 1 ) {
+               Com_Error( ERR_FATAL,"Q_strncpyz: destsize < 1" );
+       }
+
+       strncpy( dest, src, destsize - 1 );
+       dest[destsize - 1] = 0;
+}
+
+int Q_stricmpn( const char *s1, const char *s2, int n ) {
+       int c1, c2;
+
+       do {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if ( !n-- ) {
+                       return 0;       // strings are equal until end point
+               }
+
+               if ( c1 != c2 ) {
+                       if ( c1 >= 'a' && c1 <= 'z' ) {
+                               c1 -= ( 'a' - 'A' );
+                       }
+                       if ( c2 >= 'a' && c2 <= 'z' ) {
+                               c2 -= ( 'a' - 'A' );
+                       }
+                       if ( c1 != c2 ) {
+                               return c1 < c2 ? -1 : 1;
+                       }
+               }
+       } while ( c1 );
+
+       return 0;       // strings are equal
+}
+
+int Q_strncmp( const char *s1, const char *s2, int n ) {
+       int c1, c2;
+
+       do {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if ( !n-- ) {
+                       return 0;       // strings are equal until end point
+               }
+
+               if ( c1 != c2 ) {
+                       return c1 < c2 ? -1 : 1;
+               }
+       } while ( c1 );
+
+       return 0;       // strings are equal
+}
+
+int Q_stricmp( const char *s1, const char *s2 ) {
+       return Q_stricmpn( s1, s2, 99999 );
+}
+
+
+char *Q_strlwr( char *s1 ) {
+       char    *s;
+
+       s = s1;
+       while ( *s ) {
+               *s = tolower( *s );
+               s++;
+       }
+       return s1;
+}
+
+char *Q_strupr( char *s1 ) {
+       char    *s;
+
+       s = s1;
+       while ( *s ) {
+               *s = toupper( *s );
+               s++;
+       }
+       return s1;
+}
+
+
+// never goes past bounds or leaves without a terminating 0
+void Q_strcat( char *dest, std::size_t size, const char *src ) {
+       auto l1 = strlen( dest );
+       if ( l1 >= size ) {
+               Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
+       }
+       Q_strncpyz( dest + l1, src, size - l1 );
+}
+
+
+int Q_PrintStrlen( const char *string ) {
+       int len;
+       const char  *p;
+
+       if ( !string ) {
+               return 0;
+       }
+
+       len = 0;
+       p = string;
+       while ( *p ) {
+               if ( Q_IsColorString( p ) ) {
+                       p += 2;
+                       continue;
+               }
+               p++;
+               len++;
+       }
+
+       return len;
+}
+
+
+char *Q_CleanStr( char *string ) {
+       char*   d;
+       char*   s;
+       int c;
+
+       s = string;
+       d = string;
+       while ( ( c = *s ) != 0 ) {
+               if ( Q_IsColorString( s ) ) {
+                       s++;
+               }
+               else if ( c >= 0x20 && c <= 0x7E ) {
+                       *d++ = c;
+               }
+               s++;
+       }
+       *d = '\0';
+
+       return string;
+}
+
+
+void QDECL Com_sprintf( char *dest, std::size_t size, const char *fmt, ... ) {
+       va_list argptr;
+       char bigbuffer[32000];      // big, but small enough to fit in PPC stack
+
+       va_start( argptr,fmt );
+       int ret = vsprintf( bigbuffer,fmt,argptr );
+       va_end( argptr );
+       if ( ret < 0 ) {
+               Com_Error(ERR_FATAL, "Com_sprintf: vsprintf failed");
+       }
+       auto len = static_cast<size_t>(ret);
+       if ( len >= sizeof( bigbuffer ) ) {
+               Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
+       }
+       if ( len >= size ) {
+               Com_Printf( "Com_sprintf: overflow of %i in %i\n", len, size );
+       }
+       Q_strncpyz( dest, bigbuffer, size );
+}
+
+
+/*
+   ============
+   va
+
+   does a varargs printf into a temp buffer, so I don't need to have
+   varargs versions of all text functions.
+   FIXME: make this buffer size safe someday
+   ============
+ */
+char *QDECL va( const char *format, ... ) {
+       va_list argptr;
+       static char string[2][32000];       // in case va is called by nested functions
+       static int index = 0;
+       char    *buf;
+
+       buf = string[index & 1];
+       index++;
+
+       va_start( argptr, format );
+       vsprintf( buf, format,argptr );
+       va_end( argptr );
+
+       return buf;
+}
+
+
+/*
+   =====================================================================
+
+   INFO STRINGS
+
+   =====================================================================
+ */
+
+/*
+   ===============
+   Info_ValueForKey
+
+   Searches the string for the given
+   key and returns the associated value, or an empty string.
+   FIXME: overflow check?
+   ===============
+ */
+const char *Info_ValueForKey( const char *s, const char *key ) {
+       char pkey[MAX_INFO_KEY];
+       static char value[2][MAX_INFO_VALUE];   // use two buffers so compares
+                                               // work without stomping on each other
+       static int valueindex = 0;
+       char    *o;
+
+       if ( !s || !key ) {
+               return "";
+       }
+
+       if ( strlen( s ) >= MAX_INFO_STRING ) {
+               Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
+       }
+
+       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 )
+               {
+                       *o++ = *s++;
+               }
+               *o = 0;
+
+               if ( !Q_stricmp( key, pkey ) ) {
+                       return value[valueindex];
+               }
+
+               if ( !*s ) {
+                       break;
+               }
+               s++;
+       }
+
+       return "";
+}
+
+
+/*
+   ===================
+   Info_NextPair
+
+   Used to itterate through all the key/value pairs in an info string
+   ===================
+ */
+void Info_NextPair( const char *( *head ), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {
+       char    *o;
+       const char  *s;
+
+       s = *head;
+
+       if ( *s == '\\' ) {
+               s++;
+       }
+       key[0] = 0;
+       value[0] = 0;
+
+       o = key;
+       while ( *s != '\\' ) {
+               if ( !*s ) {
+                       *o = 0;
+                       *head = s;
+                       return;
+               }
+               *o++ = *s++;
+       }
+       *o = 0;
+       s++;
+
+       o = value;
+       while ( *s != '\\' && *s ) {
+               *o++ = *s++;
+       }
+       *o = 0;
+
+       *head = s;
+}
+
+
+/*
+   ===================
+   Info_RemoveKey
+   ===================
+ */
+void Info_RemoveKey( char *s, const char *key ) {
+       char    *start;
+       char pkey[MAX_INFO_KEY];
+       char value[MAX_INFO_VALUE];
+       char    *o;
+
+       if ( strlen( s ) >= MAX_INFO_STRING ) {
+               Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
+       }
+
+       if ( strchr( key, '\\' ) ) {
+               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( const char *s ) {
+       if ( strchr( s, '\"' ) ) {
+               return qfalse;
+       }
+       if ( strchr( s, ';' ) ) {
+               return qfalse;
+       }
+       return qtrue;
+}
+
+/*
+   ==================
+   Info_SetValueForKey
+
+   Changes or adds a key/value pair
+   ==================
+ */
+void Info_SetValueForKey( char *s, const char *key, const char *value ) {
+       char newi[MAX_INFO_STRING];
+
+       if ( strlen( s ) >= MAX_INFO_STRING ) {
+               Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
+       }
+
+       if ( strchr( key, '\\' ) || strchr( value, '\\' ) ) {
+               Com_Printf( "Can't use keys or values with a \\\n" );
+               return;
+       }
+
+       if ( strchr( key, ';' ) || strchr( value, ';' ) ) {
+               Com_Printf( "Can't use keys or values with a semicolon\n" );
+               return;
+       }
+
+       if ( strchr( key, '\"' ) || strchr( value, '\"' ) ) {
+               Com_Printf( "Can't use keys or values with a \"\n" );
+               return;
+       }
+
+       Info_RemoveKey( s, key );
+       if ( !value || !strlen( value ) ) {
+               return;
+       }
+
+       Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
+
+       if ( strlen( newi ) + strlen( s ) > MAX_INFO_STRING ) {
+               Com_Printf( "Info string length exceeded\n" );
+               return;
+       }
+
+       strcat( s, newi );
+}
+
+//====================================================================
+
+
+/*
+   ===============
+   ParseHex
+   ===============
+ */
+int ParseHex( const char *text ) {
+       int value;
+       int c;
+
+       value = 0;
+       while ( ( c = *text++ ) != 0 ) {
+               if ( c >= '0' && c <= '9' ) {
+                       value = value * 16 + c - '0';
+                       continue;
+               }
+               if ( c >= 'a' && c <= 'f' ) {
+                       value = value * 16 + 10 + c - 'a';
+                       continue;
+               }
+               if ( c >= 'A' && c <= 'F' ) {
+                       value = value * 16 + 10 + c - 'A';
+                       continue;
+               }
+       }
+
+       return value;
+}