]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/splines/q_shared.cpp
Callback: work at any arity
[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, std::size_t 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, std::size_t size, const char *src ) {
637         auto l1 = strlen( dest );
638         if ( l1 >= size ) {
639                 Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
640         }
641         Q_strncpyz( dest + l1, src, size - l1 );
642 }
643
644
645 int Q_PrintStrlen( const char *string ) {
646         int len;
647         const char  *p;
648
649         if ( !string ) {
650                 return 0;
651         }
652
653         len = 0;
654         p = string;
655         while ( *p ) {
656                 if ( Q_IsColorString( p ) ) {
657                         p += 2;
658                         continue;
659                 }
660                 p++;
661                 len++;
662         }
663
664         return len;
665 }
666
667
668 char *Q_CleanStr( char *string ) {
669         char*   d;
670         char*   s;
671         int c;
672
673         s = string;
674         d = string;
675         while ( ( c = *s ) != 0 ) {
676                 if ( Q_IsColorString( s ) ) {
677                         s++;
678                 }
679                 else if ( c >= 0x20 && c <= 0x7E ) {
680                         *d++ = c;
681                 }
682                 s++;
683         }
684         *d = '\0';
685
686         return string;
687 }
688
689
690 void QDECL Com_sprintf( char *dest, std::size_t size, const char *fmt, ... ) {
691         va_list argptr;
692         char bigbuffer[32000];      // big, but small enough to fit in PPC stack
693
694         va_start( argptr,fmt );
695         int ret = vsprintf( bigbuffer,fmt,argptr );
696         va_end( argptr );
697         if ( ret < 0 ) {
698                 Com_Error(ERR_FATAL, "Com_sprintf: vsprintf failed");
699         }
700         auto len = static_cast<size_t>(ret);
701         if ( len >= sizeof( bigbuffer ) ) {
702                 Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
703         }
704         if ( len >= size ) {
705                 Com_Printf( "Com_sprintf: overflow of %i in %i\n", len, size );
706         }
707         Q_strncpyz( dest, bigbuffer, size );
708 }
709
710
711 /*
712    ============
713    va
714
715    does a varargs printf into a temp buffer, so I don't need to have
716    varargs versions of all text functions.
717    FIXME: make this buffer size safe someday
718    ============
719  */
720 char *QDECL va( const char *format, ... ) {
721         va_list argptr;
722         static char string[2][32000];       // in case va is called by nested functions
723         static int index = 0;
724         char    *buf;
725
726         buf = string[index & 1];
727         index++;
728
729         va_start( argptr, format );
730         vsprintf( buf, format,argptr );
731         va_end( argptr );
732
733         return buf;
734 }
735
736
737 /*
738    =====================================================================
739
740    INFO STRINGS
741
742    =====================================================================
743  */
744
745 /*
746    ===============
747    Info_ValueForKey
748
749    Searches the string for the given
750    key and returns the associated value, or an empty string.
751    FIXME: overflow check?
752    ===============
753  */
754 const char *Info_ValueForKey( const char *s, const char *key ) {
755         char pkey[MAX_INFO_KEY];
756         static char value[2][MAX_INFO_VALUE];   // use two buffers so compares
757                                                 // work without stomping on each other
758         static int valueindex = 0;
759         char    *o;
760
761         if ( !s || !key ) {
762                 return "";
763         }
764
765         if ( strlen( s ) >= MAX_INFO_STRING ) {
766                 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
767         }
768
769         valueindex ^= 1;
770         if ( *s == '\\' ) {
771                 s++;
772         }
773         while ( 1 )
774         {
775                 o = pkey;
776                 while ( *s != '\\' )
777                 {
778                         if ( !*s ) {
779                                 return "";
780                         }
781                         *o++ = *s++;
782                 }
783                 *o = 0;
784                 s++;
785
786                 o = value[valueindex];
787
788                 while ( *s != '\\' && *s )
789                 {
790                         *o++ = *s++;
791                 }
792                 *o = 0;
793
794                 if ( !Q_stricmp( key, pkey ) ) {
795                         return value[valueindex];
796                 }
797
798                 if ( !*s ) {
799                         break;
800                 }
801                 s++;
802         }
803
804         return "";
805 }
806
807
808 /*
809    ===================
810    Info_NextPair
811
812    Used to itterate through all the key/value pairs in an info string
813    ===================
814  */
815 void Info_NextPair( const char *( *head ), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {
816         char    *o;
817         const char  *s;
818
819         s = *head;
820
821         if ( *s == '\\' ) {
822                 s++;
823         }
824         key[0] = 0;
825         value[0] = 0;
826
827         o = key;
828         while ( *s != '\\' ) {
829                 if ( !*s ) {
830                         *o = 0;
831                         *head = s;
832                         return;
833                 }
834                 *o++ = *s++;
835         }
836         *o = 0;
837         s++;
838
839         o = value;
840         while ( *s != '\\' && *s ) {
841                 *o++ = *s++;
842         }
843         *o = 0;
844
845         *head = s;
846 }
847
848
849 /*
850    ===================
851    Info_RemoveKey
852    ===================
853  */
854 void Info_RemoveKey( char *s, const char *key ) {
855         char    *start;
856         char pkey[MAX_INFO_KEY];
857         char value[MAX_INFO_VALUE];
858         char    *o;
859
860         if ( strlen( s ) >= MAX_INFO_STRING ) {
861                 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
862         }
863
864         if ( strchr( key, '\\' ) ) {
865                 return;
866         }
867
868         while ( 1 )
869         {
870                 start = s;
871                 if ( *s == '\\' ) {
872                         s++;
873                 }
874                 o = pkey;
875                 while ( *s != '\\' )
876                 {
877                         if ( !*s ) {
878                                 return;
879                         }
880                         *o++ = *s++;
881                 }
882                 *o = 0;
883                 s++;
884
885                 o = value;
886                 while ( *s != '\\' && *s )
887                 {
888                         if ( !*s ) {
889                                 return;
890                         }
891                         *o++ = *s++;
892                 }
893                 *o = 0;
894
895                 if ( !strcmp( key, pkey ) ) {
896                         strcpy( start, s );  // remove this part
897                         return;
898                 }
899
900                 if ( !*s ) {
901                         return;
902                 }
903         }
904
905 }
906
907
908 /*
909    ==================
910    Info_Validate
911
912    Some characters are illegal in info strings because they
913    can mess up the server's parsing
914    ==================
915  */
916 qboolean Info_Validate( const char *s ) {
917         if ( strchr( s, '\"' ) ) {
918                 return qfalse;
919         }
920         if ( strchr( s, ';' ) ) {
921                 return qfalse;
922         }
923         return qtrue;
924 }
925
926 /*
927    ==================
928    Info_SetValueForKey
929
930    Changes or adds a key/value pair
931    ==================
932  */
933 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
934         char newi[MAX_INFO_STRING];
935
936         if ( strlen( s ) >= MAX_INFO_STRING ) {
937                 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
938         }
939
940         if ( strchr( key, '\\' ) || strchr( value, '\\' ) ) {
941                 Com_Printf( "Can't use keys or values with a \\\n" );
942                 return;
943         }
944
945         if ( strchr( key, ';' ) || strchr( value, ';' ) ) {
946                 Com_Printf( "Can't use keys or values with a semicolon\n" );
947                 return;
948         }
949
950         if ( strchr( key, '\"' ) || strchr( value, '\"' ) ) {
951                 Com_Printf( "Can't use keys or values with a \"\n" );
952                 return;
953         }
954
955         Info_RemoveKey( s, key );
956         if ( !value || !strlen( value ) ) {
957                 return;
958         }
959
960         Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
961
962         if ( strlen( newi ) + strlen( s ) > MAX_INFO_STRING ) {
963                 Com_Printf( "Info string length exceeded\n" );
964                 return;
965         }
966
967         strcat( s, newi );
968 }
969
970 //====================================================================
971
972
973 /*
974    ===============
975    ParseHex
976    ===============
977  */
978 int ParseHex( const char *text ) {
979         int value;
980         int c;
981
982         value = 0;
983         while ( ( c = *text++ ) != 0 ) {
984                 if ( c >= '0' && c <= '9' ) {
985                         value = value * 16 + c - '0';
986                         continue;
987                 }
988                 if ( c >= 'a' && c <= 'f' ) {
989                         value = value * 16 + 10 + c - 'a';
990                         continue;
991                 }
992                 if ( c >= 'A' && c <= 'F' ) {
993                         value = value * 16 + 10 + c - 'A';
994                         continue;
995                 }
996         }
997
998         return value;
999 }