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