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