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