1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 // q_shared.c -- stateless support routines that are included in each code dll
26 #include "q_shared.h"
27
28 /*
29 -------------------------
30 GetIDForString
31 -------------------------
32 */
33
34
GetIDForString(stringID_table_t * table,const char * string)35 int GetIDForString ( stringID_table_t *table, const char *string )
36 {
37 int index = 0;
38
39 while ( ( table[index].name != NULL ) &&
40 ( table[index].name[0] != 0 ) )
41 {
42 if ( !Q_stricmp( table[index].name, string ) )
43 return table[index].id;
44
45 index++;
46 }
47
48 return -1;
49 }
50
51 /*
52 -------------------------
53 GetStringForID
54 -------------------------
55 */
56
GetStringForID(stringID_table_t * table,int id)57 const char *GetStringForID( stringID_table_t *table, int id )
58 {
59 int index = 0;
60
61 while ( ( table[index].name != NULL ) &&
62 ( table[index].name[0] != 0 ) )
63 {
64 if ( table[index].id == id )
65 return table[index].name;
66
67 index++;
68 }
69
70 return NULL;
71 }
72
73 /*
74 ============
75 COM_SkipPath
76 ============
77 */
COM_SkipPath(char * pathname)78 char *COM_SkipPath (char *pathname)
79 {
80 char *last;
81
82 last = pathname;
83 while (*pathname)
84 {
85 if (*pathname=='/')
86 last = pathname+1;
87 pathname++;
88 }
89 return last;
90 }
91
92 /*
93 ============
94 COM_GetExtension
95 ============
96 */
COM_GetExtension(const char * name)97 const char *COM_GetExtension( const char *name )
98 {
99 const char *dot = strrchr(name, '.'), *slash;
100 if (dot && (!(slash = strrchr(name, '/')) || slash < dot))
101 return dot + 1;
102 else
103 return "";
104 }
105
106 /*
107 ============
108 COM_StripExtension
109 ============
110 */
COM_StripExtension(const char * in,char * out,int destsize)111 void COM_StripExtension( const char *in, char *out, int destsize )
112 {
113 const char *dot = strrchr(in, '.'), *slash;
114 if (dot && (!(slash = strrchr(in, '/')) || slash < dot))
115 destsize = (destsize < dot-in+1 ? destsize : dot-in+1);
116
117 if ( in == out && destsize > 1 )
118 out[destsize-1] = '\0';
119 else
120 Q_strncpyz(out, in, destsize);
121 }
122
123 /*
124 ============
125 COM_CompareExtension
126
127 string compare the end of the strings and return qtrue if strings match
128 ============
129 */
COM_CompareExtension(const char * in,const char * ext)130 qboolean COM_CompareExtension(const char *in, const char *ext)
131 {
132 int inlen, extlen;
133
134 inlen = strlen(in);
135 extlen = strlen(ext);
136
137 if(extlen <= inlen)
138 {
139 in += inlen - extlen;
140
141 if(!Q_stricmp(in, ext))
142 return qtrue;
143 }
144
145 return qfalse;
146 }
147
148 /*
149 ==================
150 COM_DefaultExtension
151 ==================
152 */
COM_DefaultExtension(char * path,int maxSize,const char * extension)153 void COM_DefaultExtension( char *path, int maxSize, const char *extension )
154 {
155 const char *dot = strrchr(path, '.'), *slash;
156 if (dot && (!(slash = strrchr(path, '/')) || slash < dot))
157 return;
158 else
159 Q_strcat(path, maxSize, extension);
160 }
161
162 /*
163 ============================================================================
164
165 PARSING
166
167 ============================================================================
168 */
169
170 static char com_token[MAX_TOKEN_CHARS];
171 static char com_parsename[MAX_TOKEN_CHARS];
172 static int com_lines;
173 static int com_tokenline;
174
COM_BeginParseSession(const char * name)175 void COM_BeginParseSession( const char *name )
176 {
177 com_lines = 1;
178 com_tokenline = 0;
179 Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
180 }
181
COM_GetCurrentParseLine(void)182 int COM_GetCurrentParseLine( void )
183 {
184 if ( com_tokenline )
185 {
186 return com_tokenline;
187 }
188
189 return com_lines;
190 }
191
COM_Parse(const char ** data_p)192 char *COM_Parse( const char **data_p )
193 {
194 return COM_ParseExt( data_p, qtrue );
195 }
196
COM_ParseError(char * format,...)197 void COM_ParseError( char *format, ... )
198 {
199 va_list argptr;
200 static char string[4096];
201
202 va_start (argptr, format);
203 Q_vsnprintf (string, sizeof( string ), format, argptr);
204 va_end (argptr);
205
206 Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
207 }
208
COM_ParseWarning(char * format,...)209 void COM_ParseWarning( char *format, ... )
210 {
211 va_list argptr;
212 static char string[4096];
213
214 va_start (argptr, format);
215 Q_vsnprintf (string, sizeof(string), format, argptr);
216 va_end (argptr);
217
218 Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
219 }
220
221 /*
222 ==============
223 COM_Parse
224
225 Parse a token out of a string
226 Will never return NULL, just empty strings
227
228 If "allowLineBreaks" is qtrue then an empty
229 string will be returned if the next token is
230 a newline.
231 ==============
232 */
SkipWhitespace(const char * data,qboolean * hasNewLines)233 const char *SkipWhitespace( const char *data, qboolean *hasNewLines ) {
234 int c;
235
236 while( (c = *(const unsigned char* /*eurofix*/)data) <= ' ') {
237 if( !c ) {
238 return NULL;
239 }
240 if( c == '\n' ) {
241 com_lines++;
242 *hasNewLines = qtrue;
243 }
244 data++;
245 }
246
247 return data;
248 }
249
COM_Compress(char * data_p)250 int COM_Compress( char *data_p ) {
251 char *in, *out;
252 int c;
253 qboolean newline = qfalse, whitespace = qfalse;
254
255 in = out = data_p;
256 if (in) {
257 while ((c = *in) != 0) {
258 // skip double slash comments
259 if ( c == '/' && in[1] == '/' ) {
260 while (*in && *in != '\n') {
261 in++;
262 }
263 // skip /* */ comments
264 } else if ( c == '/' && in[1] == '*' ) {
265 while ( *in && ( *in != '*' || in[1] != '/' ) )
266 in++;
267 if ( *in )
268 in += 2;
269 // record when we hit a newline
270 } else if ( c == '\n' || c == '\r' ) {
271 newline = qtrue;
272 in++;
273 // record when we hit whitespace
274 } else if ( c == ' ' || c == '\t') {
275 whitespace = qtrue;
276 in++;
277 // an actual token
278 } else {
279 // if we have a pending newline, emit it (and it counts as whitespace)
280 if (newline) {
281 *out++ = '\n';
282 newline = qfalse;
283 whitespace = qfalse;
284 } if (whitespace) {
285 *out++ = ' ';
286 whitespace = qfalse;
287 }
288
289 // copy quoted strings unmolested
290 if (c == '"') {
291 *out++ = c;
292 in++;
293 while (1) {
294 c = *in;
295 if (c && c != '"') {
296 *out++ = c;
297 in++;
298 } else {
299 break;
300 }
301 }
302 if (c == '"') {
303 *out++ = c;
304 in++;
305 }
306 } else {
307 *out = c;
308 out++;
309 in++;
310 }
311 }
312 }
313
314 *out = 0;
315 }
316 return out - data_p;
317 }
318
COM_ParseExt(const char ** data_p,qboolean allowLineBreaks)319 char *COM_ParseExt( const char **data_p, qboolean allowLineBreaks )
320 {
321 int c = 0, len;
322 qboolean hasNewLines = qfalse;
323 const char *data;
324
325 data = *data_p;
326 len = 0;
327 com_token[0] = 0;
328 com_tokenline = 0;
329
330 // make sure incoming data is valid
331 if ( !data )
332 {
333 *data_p = NULL;
334 return com_token;
335 }
336
337 while ( 1 )
338 {
339 // skip whitespace
340 data = SkipWhitespace( data, &hasNewLines );
341 if ( !data )
342 {
343 *data_p = NULL;
344 return com_token;
345 }
346 if ( hasNewLines && !allowLineBreaks )
347 {
348 *data_p = data;
349 return com_token;
350 }
351
352 c = *data;
353
354 // skip double slash comments
355 if ( c == '/' && data[1] == '/' )
356 {
357 data += 2;
358 while (*data && *data != '\n') {
359 data++;
360 }
361 }
362 // skip /* */ comments
363 else if ( c=='/' && data[1] == '*' )
364 {
365 data += 2;
366 while ( *data && ( *data != '*' || data[1] != '/' ) )
367 {
368 if ( *data == '\n' )
369 {
370 com_lines++;
371 }
372 data++;
373 }
374 if ( *data )
375 {
376 data += 2;
377 }
378 }
379 else
380 {
381 break;
382 }
383 }
384
385 // token starts on this line
386 com_tokenline = com_lines;
387
388 // handle quoted strings
389 if (c == '\"')
390 {
391 data++;
392 while (1)
393 {
394 c = *data++;
395 if (c=='\"' || !c)
396 {
397 com_token[len] = 0;
398 *data_p = ( char * ) data;
399 return com_token;
400 }
401 if ( c == '\n' )
402 {
403 com_lines++;
404 }
405 if (len < MAX_TOKEN_CHARS - 1)
406 {
407 com_token[len] = c;
408 len++;
409 }
410 }
411 }
412
413 // parse a regular word
414 do
415 {
416 if (len < MAX_TOKEN_CHARS - 1)
417 {
418 com_token[len] = c;
419 len++;
420 }
421 data++;
422 c = *data;
423 } while (c>32);
424
425 com_token[len] = 0;
426
427 *data_p = ( char * ) data;
428 return com_token;
429 }
430
431 /*
432 ===============
433 COM_ParseString
434 ===============
435 */
COM_ParseString(const char ** data,const char ** s)436 qboolean COM_ParseString( const char **data, const char **s )
437 {
438 // *s = COM_ParseExt( data, qtrue );
439 *s = COM_ParseExt( data, qfalse );
440 if ( s[0] == 0 )
441 {
442 COM_ParseWarning( "COM_ParseString: unexpected EOF" );
443 return qtrue;
444 }
445 return qfalse;
446 }
447
448 /*
449 ===============
450 COM_ParseInt
451 ===============
452 */
COM_ParseInt(const char ** data,int * i)453 qboolean COM_ParseInt( const char **data, int *i )
454 {
455 const char *token;
456
457 token = COM_ParseExt( data, qfalse );
458 if ( token[0] == 0 )
459 {
460 COM_ParseWarning( "COM_ParseInt: unexpected EOF" );
461 return qtrue;
462 }
463
464 *i = atoi( token );
465 return qfalse;
466 }
467
468 /*
469 ===============
470 COM_ParseFloat
471 ===============
472 */
COM_ParseFloat(const char ** data,float * f)473 qboolean COM_ParseFloat( const char **data, float *f )
474 {
475 const char *token;
476
477 token = COM_ParseExt( data, qfalse );
478 if ( token[0] == 0 )
479 {
480 COM_ParseWarning( "COM_ParseFloat: unexpected EOF" );
481 return qtrue;
482 }
483
484 *f = atof( token );
485 return qfalse;
486 }
487
488 /*
489 ===============
490 COM_ParseVec4
491 ===============
492 */
COM_ParseVec4(const char ** buffer,vec4_t * c)493 qboolean COM_ParseVec4( const char **buffer, vec4_t *c)
494 {
495 int i;
496 float f;
497
498 for (i = 0; i < 4; i++)
499 {
500 if (COM_ParseFloat(buffer, &f))
501 {
502 return qtrue;
503 }
504 (*c)[i] = f;
505 }
506 return qfalse;
507 }
508
509 /*
510 ==================
511 COM_MatchToken
512 ==================
513 */
COM_MatchToken(const char ** buf_p,char * match)514 void COM_MatchToken( const char **buf_p, char *match ) {
515 char *token;
516
517 token = COM_Parse( buf_p );
518 if ( strcmp( token, match ) ) {
519 Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
520 }
521 }
522
523
524 /*
525 =================
526 SkipBracedSection
527
528 The next token should be an open brace or set depth to 1 if already parsed it.
529 Skips until a matching close brace is found.
530 Internal brace depths are properly skipped.
531 =================
532 */
SkipBracedSection(const char ** program,int depth)533 qboolean SkipBracedSection (const char **program, int depth) {
534 char *token;
535
536 do {
537 token = COM_ParseExt( program, qtrue );
538 if( token[1] == 0 ) {
539 if( token[0] == '{' ) {
540 depth++;
541 }
542 else if( token[0] == '}' ) {
543 depth--;
544 }
545 }
546 } while( depth && *program );
547
548 return (qboolean)( depth == 0 );
549 }
550
551 /*
552 =================
553 SkipRestOfLine
554 =================
555 */
SkipRestOfLine(const char ** data)556 void SkipRestOfLine ( const char **data ) {
557 const char *p;
558 int c;
559
560 p = *data;
561
562 if ( !*p )
563 return;
564
565 while ( (c = *p++) != 0 ) {
566 if ( c == '\n' ) {
567 com_lines++;
568 break;
569 }
570 }
571
572 *data = p;
573 }
574
575
Parse1DMatrix(const char ** buf_p,int x,float * m)576 void Parse1DMatrix (const char **buf_p, int x, float *m) {
577 char *token;
578 int i;
579
580 COM_MatchToken( buf_p, "(" );
581
582 for (i = 0 ; i < x ; i++) {
583 token = COM_Parse(buf_p);
584 m[i] = atof(token);
585 }
586
587 COM_MatchToken( buf_p, ")" );
588 }
589
Parse2DMatrix(const char ** buf_p,int y,int x,float * m)590 void Parse2DMatrix (const char **buf_p, int y, int x, float *m) {
591 int i;
592
593 COM_MatchToken( buf_p, "(" );
594
595 for (i = 0 ; i < y ; i++) {
596 Parse1DMatrix (buf_p, x, m + i * x);
597 }
598
599 COM_MatchToken( buf_p, ")" );
600 }
601
Parse3DMatrix(const char ** buf_p,int z,int y,int x,float * m)602 void Parse3DMatrix (const char **buf_p, int z, int y, int x, float *m) {
603 int i;
604
605 COM_MatchToken( buf_p, "(" );
606
607 for (i = 0 ; i < z ; i++) {
608 Parse2DMatrix (buf_p, y, x, m + i * x*y);
609 }
610
611 COM_MatchToken( buf_p, ")" );
612 }
613
614 /*
615 ===================
616 Com_HexStrToInt
617 ===================
618 */
Com_HexStrToInt(const char * str)619 int Com_HexStrToInt( const char *str )
620 {
621 if ( !str || !str[ 0 ] )
622 return -1;
623
624 // check for hex code
625 if( str[ 0 ] == '0' && str[ 1 ] == 'x' )
626 {
627 int n = 0;
628 size_t i;
629
630 for( i = 2; i < strlen( str ); i++ )
631 {
632 char digit;
633
634 n *= 16;
635
636 digit = tolower( str[ i ] );
637
638 if( digit >= '0' && digit <= '9' )
639 digit -= '0';
640 else if( digit >= 'a' && digit <= 'f' )
641 digit = digit - 'a' + 10;
642 else
643 return -1;
644
645 n += digit;
646 }
647
648 return n;
649 }
650
651 return -1;
652 }
653
654 /*
655 ============================================================================
656
657 LIBRARY REPLACEMENT FUNCTIONS
658
659 ============================================================================
660 */
661
Com_sprintf(char * dest,int size,const char * fmt,...)662 int QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
663 int len;
664 va_list argptr;
665
666 va_start (argptr,fmt);
667 len = Q_vsnprintf(dest, size, fmt, argptr);
668 va_end (argptr);
669
670 if(len >= size)
671 Com_Printf("Com_sprintf: Output length %d too short, require %d bytes.\n", size, len + 1);
672
673 return len;
674 }
675
FloatAsInt(float f)676 int FloatAsInt( float f ) {
677 byteAlias_t fi;
678 fi.f = f;
679 return fi.i;
680 }
681
682 /*
683 ============
684 va
685
686 does a varargs printf into a temp buffer, so I don't need to have
687 varargs versions of all text functions.
688 FIXME: make this buffer size safe someday
689 ============
690 */
691 #define MAX_VA_STRING 32000
692 #define MAX_VA_BUFFERS 4
693
va(const char * format,...)694 char * QDECL va( const char *format, ... )
695 {
696 va_list argptr;
697 static char string[MAX_VA_BUFFERS][MAX_VA_STRING]; // in case va is called by nested functions
698 static int index = 0;
699 char *buf;
700
701 va_start( argptr, format );
702 buf = (char *)&string[index++ & 3];
703 Q_vsnprintf( buf, sizeof(*string), format, argptr );
704 va_end( argptr );
705
706 return buf;
707 }
708
709 /*
710 ============
711 Com_TruncateLongString
712
713 Assumes buffer is atleast TRUNCATE_LENGTH big
714 ============
715 */
Com_TruncateLongString(char * buffer,const char * s)716 void Com_TruncateLongString( char *buffer, const char *s ) {
717 int length = strlen( s );
718
719 if ( length <= TRUNCATE_LENGTH )
720 Q_strncpyz( buffer, s, TRUNCATE_LENGTH );
721 else {
722 Q_strncpyz( buffer, s, (TRUNCATE_LENGTH/2) - 3 );
723 Q_strcat( buffer, TRUNCATE_LENGTH, " ... " );
724 Q_strcat( buffer, TRUNCATE_LENGTH, s + length - (TRUNCATE_LENGTH/2) + 3 );
725 }
726 }
727
728 /*
729 =====================================================================
730
731 INFO STRINGS
732
733 =====================================================================
734 */
735
736 /*
737 ===============
738 Info_ValueForKey
739
740 Searches the string for the given
741 key and returns the associated value, or an empty string.
742 FIXME: overflow check?
743 ===============
744 */
Info_ValueForKey(const char * s,const char * key)745 char *Info_ValueForKey( const char *s, const char *key ) {
746 char pkey[BIG_INFO_KEY];
747 static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
748 // work without stomping on each other
749 static int valueindex = 0;
750 char *o;
751
752 if ( !s || !key ) {
753 return "";
754 }
755
756 if ( strlen( s ) >= BIG_INFO_STRING ) {
757 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
758 }
759
760 valueindex ^= 1;
761 if (*s == '\\')
762 s++;
763 while (1)
764 {
765 o = pkey;
766 while (*s != '\\')
767 {
768 if (!*s)
769 return "";
770 *o++ = *s++;
771 }
772 *o = 0;
773 s++;
774
775 o = value[valueindex];
776
777 while (*s != '\\' && *s)
778 {
779 *o++ = *s++;
780 }
781 *o = 0;
782
783 if (!Q_stricmp (key, pkey) )
784 return value[valueindex];
785
786 if (!*s)
787 break;
788 s++;
789 }
790
791 return "";
792 }
793
794 /*
795 ===================
796 Info_NextPair
797
798 Used to itterate through all the key/value pairs in an info string
799 Return qfalse if we discover the infostring is invalid
800 ===================
801 */
Info_NextPair(const char ** head,char * key,char * value)802 qboolean Info_NextPair( const char **head, char *key, char *value ) {
803 char *o;
804 const char *s = *head;
805
806 if ( *s == '\\' )
807 s++;
808 key[0] = 0;
809 value[0] = 0;
810
811 o = key;
812 while ( *s != '\\' ) {
813 if ( !*s ) {
814 key[0] = 0;
815 *head = s;
816 return qtrue;
817 }
818 *o++ = *s++;
819 }
820 *o = 0;
821 s++;
822
823 // If they key is empty at this point with a slash after it
824 // then this is considered invalid, possibly an attempt at hacked userinfo strings
825 if ( !key[0] )
826 return qfalse;
827
828 o = value;
829 while ( *s != '\\' && *s ) {
830 *o++ = *s++;
831 }
832 *o = 0;
833
834 *head = s;
835
836 return qtrue;
837 }
838
839 /*
840 ===================
841 Info_RemoveKey
842 ===================
843 */
Info_RemoveKey(char * s,const char * key)844 void Info_RemoveKey( char *s, const char *key ) {
845 char *start = NULL, *o = NULL;
846 char pkey[MAX_INFO_KEY] = {0};
847 char value[MAX_INFO_VALUE] = {0};
848
849 if ( strlen( s ) >= MAX_INFO_STRING ) {
850 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
851 return;
852 }
853
854 if (strchr (key, '\\')) {
855 return;
856 }
857
858 while (1)
859 {
860 start = s;
861 if (*s == '\\')
862 s++;
863 o = pkey;
864 while (*s != '\\')
865 {
866 if (!*s)
867 return;
868 *o++ = *s++;
869 }
870 *o = 0;
871 s++;
872
873 o = value;
874 while (*s != '\\' && *s)
875 {
876 if (!*s)
877 return;
878 *o++ = *s++;
879 }
880 *o = 0;
881
882 //OJKNOTE: static analysis pointed out pkey may not be null-terminated
883 if (!strcmp (key, pkey) )
884 {
885 memmove(start, s, strlen(s) + 1); // remove this part
886 return;
887 }
888
889 if (!*s)
890 return;
891 }
892 }
893
894 /*
895 ===================
896 Info_RemoveKey_Big
897 ===================
898 */
Info_RemoveKey_Big(char * s,const char * key)899 void Info_RemoveKey_Big( char *s, const char *key ) {
900 char *start;
901 static char pkey[BIG_INFO_KEY], value[BIG_INFO_VALUE];
902 char *o;
903
904 pkey[0] = value[0] = '\0';
905
906 if ( strlen( s ) >= BIG_INFO_STRING ) {
907 Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
908 return;
909 }
910
911 if (strchr (key, '\\')) {
912 return;
913 }
914
915 while (1)
916 {
917 start = s;
918 if (*s == '\\')
919 s++;
920 o = pkey;
921 while (*s != '\\')
922 {
923 if (!*s)
924 return;
925 *o++ = *s++;
926 }
927 *o = 0;
928 s++;
929
930 o = value;
931 while (*s != '\\' && *s)
932 {
933 if (!*s)
934 return;
935 *o++ = *s++;
936 }
937 *o = 0;
938
939 //OJKNOTE: static analysis pointed out pkey may not be null-terminated
940 if (!strcmp (key, pkey) )
941 {
942 memmove(start, s, strlen(s) + 1); // remove this part
943 return;
944 }
945
946 if (!*s)
947 return;
948 }
949
950 }
951
952 /*
953 ==================
954 Info_Validate
955
956 Some characters are illegal in info strings because they
957 can mess up the server's parsing
958 ==================
959 */
Info_Validate(const char * s)960 qboolean Info_Validate( const char *s ) {
961 const char *c = s;
962
963 while ( *c != '\0' )
964 {
965 if( !Q_isprint( *c ) )
966 return qfalse;
967
968 if( *c == '\"' )
969 return qfalse;
970
971 if( *c == ';' )
972 return qfalse;
973
974 ++c;
975 }
976
977 return qtrue;
978 }
979
980 /*
981 ==================
982 Info_SetValueForKey
983
984 Changes or adds a key/value pair
985 ==================
986 */
Info_SetValueForKey(char * s,const char * key,const char * value)987 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
988 char newi[MAX_INFO_STRING];
989 const char* blacklist = "\\;\"";
990
991 if ( strlen( s ) >= MAX_INFO_STRING ) {
992 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
993 }
994
995 for(; *blacklist; ++blacklist)
996 {
997 if (strchr (key, *blacklist) || strchr (value, *blacklist))
998 {
999 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1000 return;
1001 }
1002 }
1003
1004 Info_RemoveKey (s, key);
1005 if (!value || !strlen(value))
1006 return;
1007
1008 Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1009
1010 if (strlen(newi) + strlen(s) >= MAX_INFO_STRING)
1011 {
1012 Com_Printf ("Info string length exceeded: %s\n", s);
1013 return;
1014 }
1015
1016 strcat (newi, s);
1017 strcpy (s, newi);
1018 }
1019
1020 /*
1021 ==================
1022 Info_SetValueForKey_Big
1023
1024 Changes or adds a key/value pair
1025 Includes and retains zero-length values
1026 ==================
1027 */
Info_SetValueForKey_Big(char * s,const char * key,const char * value)1028 void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
1029 char newi[BIG_INFO_STRING];
1030 const char* blacklist = "\\;\"";
1031
1032 if ( strlen( s ) >= BIG_INFO_STRING ) {
1033 Com_Error( ERR_DROP, "Info_SetValueForKey_Big: oversize infostring" );
1034 }
1035
1036 for(; *blacklist; ++blacklist)
1037 {
1038 if (strchr (key, *blacklist) || strchr (value, *blacklist))
1039 {
1040 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1041 return;
1042 }
1043 }
1044
1045 Info_RemoveKey_Big (s, key);
1046 if (!value)
1047 return;
1048
1049 Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1050
1051 if (strlen(newi) + strlen(s) >= BIG_INFO_STRING)
1052 {
1053 Com_Printf ("BIG Info string length exceeded\n");
1054 return;
1055 }
1056
1057 strcat (s, newi);
1058 }
1059
1060 /*
1061 ==================
1062 Com_CharIsOneOfCharset
1063 ==================
1064 */
Com_CharIsOneOfCharset(char c,char * set)1065 static qboolean Com_CharIsOneOfCharset( char c, char *set ) {
1066 size_t i;
1067
1068 for ( i=0; i<strlen( set ); i++ ) {
1069 if ( set[i] == c )
1070 return qtrue;
1071 }
1072
1073 return qfalse;
1074 }
1075
1076 /*
1077 ==================
1078 Com_SkipCharset
1079 ==================
1080 */
Com_SkipCharset(char * s,char * sep)1081 char *Com_SkipCharset( char *s, char *sep ) {
1082 char *p = s;
1083
1084 while ( p ) {
1085 if ( Com_CharIsOneOfCharset( *p, sep ) )
1086 p++;
1087 else
1088 break;
1089 }
1090
1091 return p;
1092 }
1093
1094 /*
1095 ==================
1096 Com_SkipTokens
1097 ==================
1098 */
Com_SkipTokens(char * s,int numTokens,char * sep)1099 char *Com_SkipTokens( char *s, int numTokens, char *sep ) {
1100 int sepCount = 0;
1101 char *p = s;
1102
1103 while ( sepCount < numTokens ) {
1104 if ( Com_CharIsOneOfCharset( *p++, sep ) ) {
1105 sepCount++;
1106 while ( Com_CharIsOneOfCharset( *p, sep ) )
1107 p++;
1108 }
1109 else if ( *p == '\0' )
1110 break;
1111 }
1112
1113 if ( sepCount == numTokens )
1114 return p;
1115 else
1116 return s;
1117 }
1118
Q_InBitflags(const uint32_t * bits,int index,uint32_t bitsPerByte)1119 qboolean Q_InBitflags( const uint32_t *bits, int index, uint32_t bitsPerByte ) {
1120 return ( bits[index / bitsPerByte] & (1 << (index % bitsPerByte)) ) ? qtrue : qfalse;
1121 }
1122
Q_AddToBitflags(uint32_t * bits,int index,uint32_t bitsPerByte)1123 void Q_AddToBitflags( uint32_t *bits, int index, uint32_t bitsPerByte ) {
1124 bits[index / bitsPerByte] |= (1 << (index % bitsPerByte));
1125 }
1126
Q_RemoveFromBitflags(uint32_t * bits,int index,uint32_t bitsPerByte)1127 void Q_RemoveFromBitflags( uint32_t *bits, int index, uint32_t bitsPerByte ) {
1128 bits[index / bitsPerByte] &= ~(1 << (index % bitsPerByte));
1129 }
1130
Q_LinearSearch(const void * key,const void * ptr,size_t count,size_t size,cmpFunc_t cmp)1131 void *Q_LinearSearch( const void *key, const void *ptr, size_t count,
1132 size_t size, cmpFunc_t cmp )
1133 {
1134 size_t i;
1135 for ( i = 0; i < count; i++ )
1136 {
1137 if ( cmp( key, ptr ) == 0 ) return (void *)ptr;
1138 ptr = (const char *)ptr + size;
1139 }
1140 return NULL;
1141 }
1142