remove RSA's md4.c, replace by DP's
[xonotic/netradiant.git] / libs / splines / q_shared.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 // q_shared.c -- stateless support routines that are included in each code dll
23 #include "q_shared.h"
24
25 /*
26 ============================================================================
27
28 GROWLISTS
29
30 ============================================================================
31 */
32
33 // malloc / free all in one place for debugging
34 extern "C" void *Com_Allocate( int bytes );
35 extern "C" void Com_Dealloc( void *ptr );
36
37 void Com_InitGrowList( growList_t *list, int maxElements ) {
38         list->maxElements = maxElements;
39         list->currentElements = 0;
40         list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );
41 }
42
43 int Com_AddToGrowList( growList_t *list, void *data ) {
44         void    **old;
45
46         if ( list->currentElements != list->maxElements ) {
47                 list->elements[list->currentElements] = data;
48                 return list->currentElements++;
49         }
50
51         // grow, reallocate and move
52         old = list->elements;
53
54         if ( list->maxElements < 0 ) {
55                 Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements );
56         }
57
58         if ( list->maxElements == 0 ) {
59                 // initialize the list to hold 100 elements
60                 Com_InitGrowList( list, 100 );
61                 return Com_AddToGrowList( list, data );
62         }
63
64         list->maxElements *= 2;
65
66         Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements );
67
68         list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );
69
70         if ( !list->elements ) {
71                 Com_Error( ERR_DROP, "Growlist alloc failed" );
72         }
73
74         memcpy( list->elements, old, list->currentElements * sizeof( void * ) );
75
76         Com_Dealloc( old );
77
78         return Com_AddToGrowList( list, data );
79 }
80
81 void *Com_GrowListElement( const growList_t *list, int index ) {
82         if ( index < 0 || index >= list->currentElements ) {
83                 Com_Error( ERR_DROP, "Com_GrowListElement: %i out of range of %i", 
84                         index, list->currentElements );
85         }
86         return list->elements[index];
87 }
88
89 int Com_IndexForGrowListElement( const growList_t *list, const void *element ) {
90         int             i;
91
92         for ( i = 0 ; i < list->currentElements ; i++ ) {
93                 if ( list->elements[i] == element ) {
94                         return i;
95                 }
96         }
97         return -1;
98 }
99
100 //============================================================================
101
102
103 float Com_Clamp( float min, float max, float value ) {
104         if ( value < min ) {
105                 return min;
106         }
107         if ( value > max ) {
108                 return max;
109         }
110         return value;
111 }
112
113 /*
114 ============
115 Com_StringContains
116 ============
117 */
118 const char *Com_StringContains( const char *str1, const char *str2, int casesensitive) {
119         int len, i, j;
120
121         len = strlen(str1) - strlen(str2);
122         for (i = 0; i <= len; i++, str1++) {
123                 for (j = 0; str2[j]; j++) {
124                         if (casesensitive) {
125                                 if (str1[j] != str2[j]) {
126                                         break;
127                                 }
128                         }
129                         else {
130                                 if (toupper(str1[j]) != toupper(str2[j])) {
131                                         break;
132                                 }
133                         }
134                 }
135                 if (!str2[j]) {
136                         return str1;
137                 }
138         }
139         return NULL;
140 }
141
142 /*
143 ============
144 Com_Filter
145 ============
146 */
147 int Com_Filter( const char *filter, const char *name, int casesensitive)
148 {
149         char buf[MAX_TOKEN_CHARS];
150         const char *ptr;
151         int i, found;
152
153         while(*filter) {
154                 if (*filter == '*') {
155                         filter++;
156                         for (i = 0; *filter; i++) {
157                                 if (*filter == '*' || *filter == '?') break;
158                                 buf[i] = *filter;
159                                 filter++;
160                         }
161                         buf[i] = '\0';
162                         if (strlen(buf)) {
163                                 ptr = Com_StringContains(name, buf, casesensitive);
164                                 if (!ptr) return qfalse;
165                                 name = ptr + strlen(buf);
166                         }
167                 }
168                 else if (*filter == '?') {
169                         filter++;
170                         name++;
171                 }
172                 else if (*filter == '[' && *(filter+1) == '[') {
173                         filter++;
174                 }
175                 else if (*filter == '[') {
176                         filter++;
177                         found = qfalse;
178                         while(*filter && !found) {
179                                 if (*filter == ']' && *(filter+1) != ']') break;
180                                 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
181                                         if (casesensitive) {
182                                                 if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
183                                         }
184                                         else {
185                                                 if (toupper(*name) >= toupper(*filter) &&
186                                                         toupper(*name) <= toupper(*(filter+2))) found = qtrue;
187                                         }
188                                         filter += 3;
189                                 }
190                                 else {
191                                         if (casesensitive) {
192                                                 if (*filter == *name) found = qtrue;
193                                         }
194                                         else {
195                                                 if (toupper(*filter) == toupper(*name)) found = qtrue;
196                                         }
197                                         filter++;
198                                 }
199                         }
200                         if (!found) return qfalse;
201                         while(*filter) {
202                                 if (*filter == ']' && *(filter+1) != ']') break;
203                                 filter++;
204                         }
205                         filter++;
206                         name++;
207                 }
208                 else {
209                         if (casesensitive) {
210                                 if (*filter != *name) return qfalse;
211                         }
212                         else {
213                                 if (toupper(*filter) != toupper(*name)) return qfalse;
214                         }
215                         filter++;
216                         name++;
217                 }
218         }
219         return qtrue;
220 }
221
222
223 /*
224 ================
225 Com_HashString
226
227 ================
228 */
229 int Com_HashString( const char *fname ) {
230         int             i;
231         long    hash;
232         char    letter;
233
234         hash = 0;
235         i = 0;
236         while (fname[i] != '\0') {
237                 letter = tolower(fname[i]);
238                 if (letter =='.') break;                                // don't include extension
239                 if (letter =='\\') letter = '/';                // damn path names
240                 hash+=(long)(letter)*(i+119);
241                 i++;
242         }
243         hash &= (FILE_HASH_SIZE-1);
244         return hash;
245 }
246
247
248 /*
249 ============
250 Com_SkipPath
251 ============
252 */
253 char *Com_SkipPath (char *pathname)
254 {
255         char    *last;
256         
257         last = pathname;
258         while (*pathname)
259         {
260                 if (*pathname=='/')
261                         last = pathname+1;
262                 pathname++;
263         }
264         return last;
265 }
266
267 /*
268 ============
269 Com_StripExtension
270 ============
271 */
272 void Com_StripExtension( const char *in, char *out ) {
273         while ( *in && *in != '.' ) {
274                 *out++ = *in++;
275         }
276         *out = 0;
277 }
278
279
280 /*
281 ==================
282 Com_DefaultExtension
283 ==================
284 */
285 void Com_DefaultExtension (char *path, int maxSize, const char *extension ) {
286         char    oldPath[MAX_QPATH];
287         char    *src;
288
289 //
290 // if path doesn't have a .EXT, append extension
291 // (extension should include the .)
292 //
293         src = path + strlen(path) - 1;
294
295         while (*src != '/' && src != path) {
296                 if ( *src == '.' ) {
297                         return;                 // it has an extension
298                 }
299                 src--;
300         }
301
302         Q_strncpyz( oldPath, path, sizeof( oldPath ) );
303         Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
304 }
305
306 /*
307 ============================================================================
308
309                                         BYTE ORDER FUNCTIONS
310
311 ============================================================================
312 */
313
314 // can't just use function pointers, or dll linkage can
315 // mess up when qcommon is included in multiple places
316 static short    (*_BigShort) (short l);
317 static short    (*_LittleShort) (short l);
318 static int              (*_BigLong) (int l);
319 static int              (*_LittleLong) (int l);
320 static float    (*_BigFloat) (float l);
321 static float    (*_LittleFloat) (float l);
322
323 short   BigShort(short l){return _BigShort(l);}
324 short   LittleShort(short l) {return _LittleShort(l);}
325 int             BigLong (int l) {return _BigLong(l);}
326 int             LittleLong (int l) {return _LittleLong(l);}
327 float   BigFloat (float l) {return _BigFloat(l);}
328 float   LittleFloat (float l) {return _LittleFloat(l);}
329
330 short   ShortSwap (short l)
331 {
332         byte    b1,b2;
333
334         b1 = l&255;
335         b2 = (l>>8)&255;
336
337         return (b1<<8) + b2;
338 }
339
340 short   ShortNoSwap (short l)
341 {
342         return l;
343 }
344
345 int    LongSwap (int l)
346 {
347         byte    b1,b2,b3,b4;
348
349         b1 = l&255;
350         b2 = (l>>8)&255;
351         b3 = (l>>16)&255;
352         b4 = (l>>24)&255;
353
354         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
355 }
356
357 int     LongNoSwap (int l)
358 {
359         return l;
360 }
361
362 float FloatSwap (float f)
363 {
364         union
365         {
366                 float   f;
367                 byte    b[4];
368         } dat1, dat2;
369         
370         
371         dat1.f = f;
372         dat2.b[0] = dat1.b[3];
373         dat2.b[1] = dat1.b[2];
374         dat2.b[2] = dat1.b[1];
375         dat2.b[3] = dat1.b[0];
376         return dat2.f;
377 }
378
379 float FloatNoSwap (float f)
380 {
381         return f;
382 }
383
384 /*
385 ================
386 Swap_Init
387 ================
388 */
389 void Swap_Init (void)
390 {
391         byte    swaptest[2] = {1,0};
392
393 // set the byte swapping variables in a portable manner 
394         if ( *(short *)swaptest == 1)
395         {
396                 _BigShort = ShortSwap;
397                 _LittleShort = ShortNoSwap;
398                 _BigLong = LongSwap;
399                 _LittleLong = LongNoSwap;
400                 _BigFloat = FloatSwap;
401                 _LittleFloat = FloatNoSwap;
402         }
403         else
404         {
405                 _BigShort = ShortNoSwap;
406                 _LittleShort = ShortSwap;
407                 _BigLong = LongNoSwap;
408                 _LittleLong = LongSwap;
409                 _BigFloat = FloatNoSwap;
410                 _LittleFloat = FloatSwap;
411         }
412
413 }
414
415 /*
416 ===============
417 Com_ParseInfos
418 ===============
419 */
420 int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) {
421         const char      *token;
422         int             count;
423         char    key[MAX_TOKEN_CHARS];
424
425         count = 0;
426
427         while ( 1 ) {
428                 token = Com_Parse( &buf );
429                 if ( !token[0] ) {
430                         break;
431                 }
432                 if ( strcmp( token, "{" ) ) {
433                         Com_Printf( "Missing { in info file\n" );
434                         break;
435                 }
436
437                 if ( count == max ) {
438                         Com_Printf( "Max infos exceeded\n" );
439                         break;
440                 }
441
442                 infos[count][0] = 0;
443                 while ( 1 ) {
444                         token = Com_Parse( &buf );
445                         if ( !token[0] ) {
446                                 Com_Printf( "Unexpected end of info file\n" );
447                                 break;
448                         }
449                         if ( !strcmp( token, "}" ) ) {
450                                 break;
451                         }
452                         Q_strncpyz( key, token, sizeof( key ) );
453
454                         token = Com_ParseOnLine( &buf );
455                         if ( !token[0] ) {
456                                 token = "<NULL>";
457                         }
458                         Info_SetValueForKey( infos[count], key, token );
459                 }
460                 count++;
461         }
462
463         return count;
464 }
465
466
467
468 /*
469 ============================================================================
470
471                                         LIBRARY REPLACEMENT FUNCTIONS
472
473 ============================================================================
474 */
475
476 int Q_isprint( int c )
477 {
478         if ( c >= 0x20 && c <= 0x7E )
479                 return ( 1 );
480         return ( 0 );
481 }
482
483 int Q_islower( int c )
484 {
485         if (c >= 'a' && c <= 'z')
486                 return ( 1 );
487         return ( 0 );
488 }
489
490 int Q_isupper( int c )
491 {
492         if (c >= 'A' && c <= 'Z')
493                 return ( 1 );
494         return ( 0 );
495 }
496
497 int Q_isalpha( int c )
498 {
499         if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
500                 return ( 1 );
501         return ( 0 );
502 }
503
504 char* Q_strrchr( const char* string, int c )
505 {
506         char cc = c;
507         char *s;
508         char *sp=(char *)0;
509
510         s = (char*)string;
511
512         while (*s)
513         {
514                 if (*s == cc)
515                         sp = s;
516                 s++;
517         }
518         if (cc == 0)
519                 sp = s;
520
521         return sp;
522 }
523
524 /*
525 =============
526 Q_strncpyz
527  
528 Safe strncpy that ensures a trailing zero
529 =============
530 */
531 void Q_strncpyz( char *dest, const char *src, int destsize ) {
532         if ( !src ) {
533                 Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
534         }
535         if ( destsize < 1 ) {
536                 Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); 
537         }
538
539         strncpy( dest, src, destsize-1 );
540     dest[destsize-1] = 0;
541 }
542                  
543 int Q_stricmpn (const char *s1, const char *s2, int n) {
544         int             c1, c2;
545         
546         do {
547                 c1 = *s1++;
548                 c2 = *s2++;
549
550                 if (!n--) {
551                         return 0;               // strings are equal until end point
552                 }
553                 
554                 if (c1 != c2) {
555                         if (c1 >= 'a' && c1 <= 'z') {
556                                 c1 -= ('a' - 'A');
557                         }
558                         if (c2 >= 'a' && c2 <= 'z') {
559                                 c2 -= ('a' - 'A');
560                         }
561                         if (c1 != c2) {
562                                 return c1 < c2 ? -1 : 1;
563                         }
564                 }
565         } while (c1);
566         
567         return 0;               // strings are equal
568 }
569
570 int Q_strncmp (const char *s1, const char *s2, int n) {
571         int             c1, c2;
572         
573         do {
574                 c1 = *s1++;
575                 c2 = *s2++;
576
577                 if (!n--) {
578                         return 0;               // strings are equal until end point
579                 }
580                 
581                 if (c1 != c2) {
582                         return c1 < c2 ? -1 : 1;
583                 }
584         } while (c1);
585         
586         return 0;               // strings are equal
587 }
588
589 int Q_stricmp (const char *s1, const char *s2) {
590         return Q_stricmpn (s1, s2, 99999);
591 }
592
593
594 char *Q_strlwr( char *s1 ) {
595     char        *s;
596
597     s = s1;
598         while ( *s ) {
599                 *s = tolower(*s);
600                 s++;
601         }
602     return s1;
603 }
604
605 char *Q_strupr( char *s1 ) {
606     char        *s;
607
608     s = s1;
609         while ( *s ) {
610                 *s = toupper(*s);
611                 s++;
612         }
613     return s1;
614 }
615
616
617 // never goes past bounds or leaves without a terminating 0
618 void Q_strcat( char *dest, int size, const char *src ) {
619         int             l1;
620
621         l1 = strlen( dest );
622         if ( l1 >= size ) {
623                 Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
624         }
625         Q_strncpyz( dest + l1, src, size - l1 );
626 }
627
628
629 int Q_PrintStrlen( const char *string ) {
630         int                     len;
631         const char      *p;
632
633         if( !string ) {
634                 return 0;
635         }
636
637         len = 0;
638         p = string;
639         while( *p ) {
640                 if( Q_IsColorString( p ) ) {
641                         p += 2;
642                         continue;
643                 }
644                 p++;
645                 len++;
646         }
647
648         return len;
649 }
650
651
652 char *Q_CleanStr( char *string ) {
653         char*   d;
654         char*   s;
655         int             c;
656
657         s = string;
658         d = string;
659         while ((c = *s) != 0 ) {
660                 if ( Q_IsColorString( s ) ) {
661                         s++;
662                 }               
663                 else if ( c >= 0x20 && c <= 0x7E ) {
664                         *d++ = c;
665                 }
666                 s++;
667         }
668         *d = '\0';
669
670         return string;
671 }
672
673
674 void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
675         int             len;
676         va_list         argptr;
677         char    bigbuffer[32000];       // big, but small enough to fit in PPC stack
678
679         va_start (argptr,fmt);
680         len = vsprintf (bigbuffer,fmt,argptr);
681         va_end (argptr);
682         if ( len >= sizeof( bigbuffer ) ) {
683                 Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
684         }
685         if (len >= size) {
686                 Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
687         }
688         Q_strncpyz (dest, bigbuffer, size );
689 }
690
691
692 /*
693 ============
694 va
695
696 does a varargs printf into a temp buffer, so I don't need to have
697 varargs versions of all text functions.
698 FIXME: make this buffer size safe someday
699 ============
700 */
701 char    * QDECL va( char *format, ... ) {
702         va_list         argptr;
703         static char             string[2][32000];       // in case va is called by nested functions
704         static int              index = 0;
705         char    *buf;
706
707         buf = string[index & 1];
708         index++;
709
710         va_start (argptr, format);
711         vsprintf (buf, format,argptr);
712         va_end (argptr);
713
714         return buf;
715 }
716
717
718 /*
719 =====================================================================
720
721   INFO STRINGS
722
723 =====================================================================
724 */
725
726 /*
727 ===============
728 Info_ValueForKey
729
730 Searches the string for the given
731 key and returns the associated value, or an empty string.
732 FIXME: overflow check?
733 ===============
734 */
735 char *Info_ValueForKey( const char *s, const char *key ) {
736         char    pkey[MAX_INFO_KEY];
737         static  char value[2][MAX_INFO_VALUE];  // use two buffers so compares
738                                                                                         // work without stomping on each other
739         static  int     valueindex = 0;
740         char    *o;
741         
742         if ( !s || !key ) {
743                 return "";
744         }
745
746         if ( strlen( s ) >= MAX_INFO_STRING ) {
747                 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
748         }
749
750         valueindex ^= 1;
751         if (*s == '\\')
752                 s++;
753         while (1)
754         {
755                 o = pkey;
756                 while (*s != '\\')
757                 {
758                         if (!*s)
759                                 return "";
760                         *o++ = *s++;
761                 }
762                 *o = 0;
763                 s++;
764
765                 o = value[valueindex];
766
767                 while (*s != '\\' && *s)
768                 {
769                         *o++ = *s++;
770                 }
771                 *o = 0;
772
773                 if (!Q_stricmp (key, pkey) )
774                         return value[valueindex];
775
776                 if (!*s)
777                         break;
778                 s++;
779         }
780
781         return "";
782 }
783
784
785 /*
786 ===================
787 Info_NextPair
788
789 Used to itterate through all the key/value pairs in an info string
790 ===================
791 */
792 void Info_NextPair( const char *(*head), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {
793         char    *o;
794         const char      *s;
795
796         s = *head;
797
798         if ( *s == '\\' ) {
799                 s++;
800         }
801         key[0] = 0;
802         value[0] = 0;
803
804         o = key;
805         while ( *s != '\\' ) {
806                 if ( !*s ) {
807                         *o = 0;
808                         *head = s;
809                         return;
810                 }
811                 *o++ = *s++;
812         }
813         *o = 0;
814         s++;
815
816         o = value;
817         while ( *s != '\\' && *s ) {
818                 *o++ = *s++;
819         }
820         *o = 0;
821
822         *head = s;
823 }
824
825
826 /*
827 ===================
828 Info_RemoveKey
829 ===================
830 */
831 void Info_RemoveKey( char *s, const char *key ) {
832         char    *start;
833         char    pkey[MAX_INFO_KEY];
834         char    value[MAX_INFO_VALUE];
835         char    *o;
836
837         if ( strlen( s ) >= MAX_INFO_STRING ) {
838                 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
839         }
840
841         if (strchr (key, '\\')) {
842                 return;
843         }
844
845         while (1)
846         {
847                 start = s;
848                 if (*s == '\\')
849                         s++;
850                 o = pkey;
851                 while (*s != '\\')
852                 {
853                         if (!*s)
854                                 return;
855                         *o++ = *s++;
856                 }
857                 *o = 0;
858                 s++;
859
860                 o = value;
861                 while (*s != '\\' && *s)
862                 {
863                         if (!*s)
864                                 return;
865                         *o++ = *s++;
866                 }
867                 *o = 0;
868
869                 if (!strcmp (key, pkey) )
870                 {
871                         strcpy (start, s);      // remove this part
872                         return;
873                 }
874
875                 if (!*s)
876                         return;
877         }
878
879 }
880
881
882 /*
883 ==================
884 Info_Validate
885
886 Some characters are illegal in info strings because they
887 can mess up the server's parsing
888 ==================
889 */
890 qboolean Info_Validate( const char *s ) {
891         if ( strchr( s, '\"' ) ) {
892                 return qfalse;
893         }
894         if ( strchr( s, ';' ) ) {
895                 return qfalse;
896         }
897         return qtrue;
898 }
899
900 /*
901 ==================
902 Info_SetValueForKey
903
904 Changes or adds a key/value pair
905 ==================
906 */
907 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
908         char    newi[MAX_INFO_STRING];
909
910         if ( strlen( s ) >= MAX_INFO_STRING ) {
911                 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
912         }
913
914         if (strchr (key, '\\') || strchr (value, '\\'))
915         {
916                 Com_Printf ("Can't use keys or values with a \\\n");
917                 return;
918         }
919
920         if (strchr (key, ';') || strchr (value, ';'))
921         {
922                 Com_Printf ("Can't use keys or values with a semicolon\n");
923                 return;
924         }
925
926         if (strchr (key, '\"') || strchr (value, '\"'))
927         {
928                 Com_Printf ("Can't use keys or values with a \"\n");
929                 return;
930         }
931
932         Info_RemoveKey (s, key);
933         if (!value || !strlen(value))
934                 return;
935
936         Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
937
938         if (strlen(newi) + strlen(s) > MAX_INFO_STRING)
939         {
940                 Com_Printf ("Info string length exceeded\n");
941                 return;
942         }
943
944         strcat (s, newi);
945 }
946
947 //====================================================================
948
949
950 /*
951 ===============
952 ParseHex
953 ===============
954 */
955 int     ParseHex( const char *text ) {
956         int             value;
957         int             c;
958
959         value = 0;
960         while ( ( c = *text++ ) != 0 ) {
961                 if ( c >= '0' && c <= '9' ) {
962                         value = value * 16 + c - '0';
963                         continue;
964                 }
965                 if ( c >= 'a' && c <= 'f' ) {
966                         value = value * 16 + 10 + c - 'a';
967                         continue;
968                 }
969                 if ( c >= 'A' && c <= 'F' ) {
970                         value = value * 16 + 10 + c - 'A';
971                         continue;
972                 }
973         }
974
975         return value;
976 }