-/*\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-2007 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, int 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, int size, const char *src ) {
+ int l1;
+
+ 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, int size, const char *fmt, ...) {
+ int len;
+ va_list argptr;
+ char bigbuffer[32000]; // big, but small enough to fit in PPC stack
+
+ va_start (argptr,fmt);
+ len = vsprintf (bigbuffer,fmt,argptr);
+ va_end (argptr);
+ 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( 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?
+===============
+*/
+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;
+}