1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 // q_shared.c -- stateless support routines that are included in each code dll
30 #include "q_shared.h"
31
32 // os x game bundles have no standard library links, and the defines are not always defined!
33
34 #ifdef __APPLE__
qmax(int x,int y)35 int qmax( int x, int y ) {
36 return ( ( ( x ) > ( y ) ) ? ( x ) : ( y ) );
37 }
38
qmin(int x,int y)39 int qmin( int x, int y ) {
40 return ( ( ( x ) < ( y ) ) ? ( x ) : ( y ) );
41 }
42 #endif
43
44 // ^[0-9a-zA-Z]
Q_IsColorString(const char * p)45 qboolean Q_IsColorString(const char *p) {
46 if (!p)
47 return qfalse;
48
49 if (p[0] != Q_COLOR_ESCAPE)
50 return qfalse;
51
52 if (p[1] == 0)
53 return qfalse;
54
55 // isalnum expects a signed integer in the range -1 (EOF) to 255, or it might assert on undefined behaviour
56 // a dereferenced char pointer has the range -128 to 127, so we just need to rangecheck the negative part
57 if (p[1] < 0)
58 return qfalse;
59
60 if (isalnum(p[1]) == 0)
61 return qfalse;
62
63 return qtrue;
64 }
65
Com_Clamp(float min,float max,float value)66 float Com_Clamp( float min, float max, float value ) {
67 if ( value < min ) {
68 return min;
69 }
70 if ( value > max ) {
71 return max;
72 }
73 return value;
74 }
75
76
77 /*
78 ============
79 COM_SkipPath
80 ============
81 */
COM_SkipPath(char * pathname)82 char *COM_SkipPath( char *pathname ) {
83 char *last;
84
85 last = pathname;
86 while ( *pathname )
87 {
88 if ( *pathname == '/' ) {
89 last = pathname + 1;
90 }
91 pathname++;
92 }
93 return last;
94 }
95
96 /*
97 ============
98 COM_GetExtension
99 ============
100 */
COM_GetExtension(const char * name)101 const char *COM_GetExtension( const char *name )
102 {
103 const char *dot = strrchr(name, '.'), *slash;
104 if (dot && (!(slash = strrchr(name, '/')) || slash < dot))
105 return dot + 1;
106 else
107 return "";
108 }
109
110 /*
111 ============
112 COM_StripExtension
113 ============
114 */
COM_StripExtension(const char * in,char * out,int destsize)115 void COM_StripExtension( const char *in, char *out, int destsize )
116 {
117 const char *dot = strrchr(in, '.'), *slash;
118
119 if (dot && (!(slash = strrchr(in, '/')) || slash < dot))
120 destsize = (destsize < dot-in+1 ? destsize : dot-in+1);
121
122 if ( in == out && destsize > 1 )
123 out[destsize-1] = '\0';
124 else
125 Q_strncpyz(out, in, destsize);
126 }
127
128 /*
129 ============
130 COM_StripExtension2
131 a safer version
132 ============
133 */
COM_StripExtension2(const char * in,char * out,int destsize)134 void COM_StripExtension2( const char *in, char *out, int destsize ) {
135 int len = 0;
136 while ( len < destsize - 1 && *in && *in != '.' ) {
137 *out++ = *in++;
138 len++;
139 }
140 *out = 0;
141 }
142
COM_StripFilename(char * in,char * out)143 void COM_StripFilename( char *in, char *out ) {
144 char *end;
145 Q_strncpyz( out, in, strlen( in ) );
146 end = COM_SkipPath( out );
147 *end = 0;
148 }
149
150 /*
151 ============
152 COM_CompareExtension
153
154 string compare the end of the strings and return qtrue if strings match
155 ============
156 */
COM_CompareExtension(const char * in,const char * ext)157 qboolean COM_CompareExtension(const char *in, const char *ext)
158 {
159 int inlen, extlen;
160
161 inlen = strlen(in);
162 extlen = strlen(ext);
163
164 if(extlen <= inlen)
165 {
166 in += inlen - extlen;
167
168 if(!Q_stricmp(in, ext))
169 return qtrue;
170 }
171
172 return qfalse;
173 }
174
175 /*
176 ==================
177 COM_DefaultExtension
178
179 if path doesn't have an extension, then append
180 the specified one (which should include the .)
181 ==================
182 */
COM_DefaultExtension(char * path,int maxSize,const char * extension)183 void COM_DefaultExtension( char *path, int maxSize, const char *extension )
184 {
185 const char *dot = strrchr(path, '.'), *slash;
186 if (dot && (!(slash = strrchr(path, '/')) || slash < dot))
187 return;
188 else
189 Q_strcat(path, maxSize, extension);
190 }
191
192 //============================================================================
193 /*
194 ==================
195 COM_BitCheck
196
197 Allows bit-wise checks on arrays with more than one item (> 32 bits)
198 ==================
199 */
COM_BitCheck(const int array[],int bitNum)200 qboolean COM_BitCheck( const int array[], int bitNum ) {
201 int i;
202
203 i = 0;
204 while ( bitNum > 31 ) {
205 i++;
206 bitNum -= 32;
207 }
208
209 return ( ( array[i] & ( 1 << bitNum ) ) != 0 ); // (SA) heh, whoops. :)
210 }
211
212 /*
213 ==================
214 COM_BitSet
215
216 Allows bit-wise SETS on arrays with more than one item (> 32 bits)
217 ==================
218 */
COM_BitSet(int array[],int bitNum)219 void COM_BitSet( int array[], int bitNum ) {
220 int i;
221
222 i = 0;
223 while ( bitNum > 31 ) {
224 i++;
225 bitNum -= 32;
226 }
227
228 array[i] |= ( 1 << bitNum );
229 }
230
231 /*
232 ==================
233 COM_BitClear
234
235 Allows bit-wise CLEAR on arrays with more than one item (> 32 bits)
236 ==================
237 */
COM_BitClear(int array[],int bitNum)238 void COM_BitClear( int array[], int bitNum ) {
239 int i;
240
241 i = 0;
242 while ( bitNum > 31 ) {
243 i++;
244 bitNum -= 32;
245 }
246
247 array[i] &= ~( 1 << bitNum );
248 }
249 //============================================================================
250
251 /*
252 ============================================================================
253
254 BYTE ORDER FUNCTIONS
255
256 ============================================================================
257 */
258
CopyShortSwap(void * dest,void * src)259 void CopyShortSwap(void *dest, void *src)
260 {
261 byte *to = dest, *from = src;
262
263 to[0] = from[1];
264 to[1] = from[0];
265 }
266
CopyLongSwap(void * dest,void * src)267 void CopyLongSwap(void *dest, void *src)
268 {
269 byte *to = dest, *from = src;
270
271 to[0] = from[3];
272 to[1] = from[2];
273 to[2] = from[1];
274 to[3] = from[0];
275 }
276
ShortSwap(short l)277 short ShortSwap( short l ) {
278 byte b1,b2;
279
280 b1 = l & 255;
281 b2 = ( l >> 8 ) & 255;
282
283 return ( b1 << 8 ) + b2;
284 }
285
ShortNoSwap(short l)286 short ShortNoSwap( short l ) {
287 return l;
288 }
289
LongSwap(int l)290 int LongSwap( int l ) {
291 byte b1,b2,b3,b4;
292
293 b1 = l & 255;
294 b2 = ( l >> 8 ) & 255;
295 b3 = ( l >> 16 ) & 255;
296 b4 = ( l >> 24 ) & 255;
297
298 return ( (int)b1 << 24 ) + ( (int)b2 << 16 ) + ( (int)b3 << 8 ) + b4;
299 }
300
LongNoSwap(int l)301 int LongNoSwap( int l ) {
302 return l;
303 }
304
Long64Swap(qint64 ll)305 qint64 Long64Swap( qint64 ll ) {
306 qint64 result;
307
308 result.b0 = ll.b7;
309 result.b1 = ll.b6;
310 result.b2 = ll.b5;
311 result.b3 = ll.b4;
312 result.b4 = ll.b3;
313 result.b5 = ll.b2;
314 result.b6 = ll.b1;
315 result.b7 = ll.b0;
316
317 return result;
318 }
319
Long64NoSwap(qint64 ll)320 qint64 Long64NoSwap( qint64 ll ) {
321 return ll;
322 }
323
FloatSwap(const float * f)324 float FloatSwap (const float *f) {
325 floatint_t out;
326
327 out.f = *f;
328 out.ui = LongSwap(out.ui);
329
330 return out.f;
331 }
332
FloatNoSwap(float f)333 float FloatNoSwap( float f ) {
334 return f;
335 }
336
337 /*
338 ============================================================================
339
340 PARSING
341
342 ============================================================================
343 */
344
345 static char com_token[MAX_TOKEN_CHARS];
346 static char com_parsename[MAX_TOKEN_CHARS];
347 static int com_lines;
348 static int com_tokenline;
349
350 static int backup_lines;
351 static char *backup_text;
352
COM_BeginParseSession(const char * name)353 void COM_BeginParseSession( const char *name ) {
354 com_lines = 1;
355 com_tokenline = 0;
356 Com_sprintf( com_parsename, sizeof( com_parsename ), "%s", name );
357 }
358
COM_BackupParseSession(char ** data_p)359 void COM_BackupParseSession( char **data_p ) {
360 backup_lines = com_lines;
361 backup_text = *data_p;
362 }
363
COM_RestoreParseSession(char ** data_p)364 void COM_RestoreParseSession( char **data_p ) {
365 com_lines = backup_lines;
366 *data_p = backup_text;
367 }
368
COM_SetCurrentParseLine(int line)369 void COM_SetCurrentParseLine( int line ) {
370 com_lines = line;
371 }
372
COM_GetCurrentParseLine(void)373 int COM_GetCurrentParseLine( void ) {
374 if ( com_tokenline )
375 {
376 return com_tokenline;
377 }
378
379 return com_lines;
380 }
381
COM_Parse(char ** data_p)382 char *COM_Parse( char **data_p ) {
383 return COM_ParseExt( data_p, qtrue );
384 }
385
COM_ParseError(char * format,...)386 void COM_ParseError( char *format, ... ) {
387 va_list argptr;
388 static char string[4096];
389
390 va_start( argptr, format );
391 Q_vsnprintf( string, sizeof( string ), format, argptr );
392 va_end( argptr );
393
394 Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
395 }
396
COM_ParseWarning(char * format,...)397 void COM_ParseWarning( char *format, ... ) {
398 va_list argptr;
399 static char string[4096];
400
401 va_start( argptr, format );
402 Q_vsnprintf( string, sizeof( string ), format, argptr );
403 va_end( argptr );
404
405 Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
406 }
407
408 /*
409 ==============
410 COM_Parse
411
412 Parse a token out of a string
413 Will never return NULL, just empty strings
414
415 If "allowLineBreaks" is qtrue then an empty
416 string will be returned if the next token is
417 a newline.
418 ==============
419 */
SkipWhitespace(char * data,qboolean * hasNewLines)420 static char *SkipWhitespace( char *data, qboolean *hasNewLines ) {
421 int c;
422
423 while ( ( c = *data ) <= ' ' ) {
424 if ( !c ) {
425 return NULL;
426 }
427 if ( c == '\n' ) {
428 com_lines++;
429 *hasNewLines = qtrue;
430 }
431 data++;
432 }
433
434 return data;
435 }
436
COM_Compress(char * data_p)437 int COM_Compress( char *data_p ) {
438 char *in, *out;
439 int c;
440 qboolean newline = qfalse, whitespace = qfalse;
441
442 in = out = data_p;
443 if (in) {
444 while ((c = *in) != 0) {
445 // skip double slash comments
446 if ( c == '/' && in[1] == '/' ) {
447 while (*in && *in != '\n') {
448 in++;
449 }
450 // skip /* */ comments
451 } else if ( c == '/' && in[1] == '*' ) {
452 while ( *in && ( *in != '*' || in[1] != '/' ) )
453 in++;
454 if ( *in )
455 in += 2;
456 // record when we hit a newline
457 } else if ( c == '\n' || c == '\r' ) {
458 newline = qtrue;
459 in++;
460 // record when we hit whitespace
461 } else if ( c == ' ' || c == '\t') {
462 whitespace = qtrue;
463 in++;
464 // an actual token
465 } else {
466 // if we have a pending newline, emit it (and it counts as whitespace)
467 if (newline) {
468 *out++ = '\n';
469 newline = qfalse;
470 whitespace = qfalse;
471 } if (whitespace) {
472 *out++ = ' ';
473 whitespace = qfalse;
474 }
475
476 // copy quoted strings unmolested
477 if (c == '"') {
478 *out++ = c;
479 in++;
480 while (1) {
481 c = *in;
482 if (c && c != '"') {
483 *out++ = c;
484 in++;
485 } else {
486 break;
487 }
488 }
489 if (c == '"') {
490 *out++ = c;
491 in++;
492 }
493 } else {
494 *out = c;
495 out++;
496 in++;
497 }
498 }
499 }
500
501 *out = 0;
502 }
503 return out - data_p;
504 }
505
COM_ParseExt(char ** data_p,qboolean allowLineBreaks)506 char *COM_ParseExt( char **data_p, qboolean allowLineBreaks ) {
507 int c = 0, len;
508 qboolean hasNewLines = qfalse;
509 char *data;
510
511 data = *data_p;
512 len = 0;
513 com_token[0] = 0;
514 com_tokenline = 0;
515
516 // make sure incoming data is valid
517 if ( !data ) {
518 *data_p = NULL;
519 return com_token;
520 }
521
522 // RF, backup the session data so we can unget easily
523 COM_BackupParseSession( data_p );
524
525 while ( 1 )
526 {
527 // skip whitespace
528 data = SkipWhitespace( data, &hasNewLines );
529 if ( !data ) {
530 *data_p = NULL;
531 return com_token;
532 }
533 if ( hasNewLines && !allowLineBreaks ) {
534 *data_p = data;
535 return com_token;
536 }
537
538 c = *data;
539
540 // skip double slash comments
541 if ( c == '/' && data[1] == '/' ) {
542 data += 2;
543 while ( *data && *data != '\n' ) {
544 data++;
545 }
546 }
547 // skip /* */ comments
548 else if ( c == '/' && data[1] == '*' ) {
549 data += 2;
550 while ( *data && ( *data != '*' || data[1] != '/' ) )
551 {
552 if ( *data == '\n' )
553 {
554 com_lines++;
555 }
556 data++;
557 }
558 if ( *data ) {
559 data += 2;
560 }
561 } else
562 {
563 break;
564 }
565 }
566
567 // token starts on this line
568 com_tokenline = com_lines;
569
570 // handle quoted strings
571 if ( c == '\"' ) {
572 data++;
573 while ( 1 )
574 {
575 c = *data++;
576 if ( c == '\"' || !c ) {
577 com_token[len] = 0;
578 *data_p = ( char * ) data;
579 return com_token;
580 }
581 if ( c == '\n' )
582 {
583 com_lines++;
584 }
585 if ( len < MAX_TOKEN_CHARS - 1) {
586 com_token[len] = c;
587 len++;
588 }
589 }
590 }
591
592 // parse a regular word
593 do
594 {
595 if ( len < MAX_TOKEN_CHARS - 1) {
596 com_token[len] = c;
597 len++;
598 }
599 data++;
600 c = *data;
601 } while ( c > 32 );
602
603 com_token[len] = 0;
604
605 *data_p = ( char * ) data;
606 return com_token;
607 }
608
609
610
611
612 /*
613 ==================
614 COM_MatchToken
615 ==================
616 */
COM_MatchToken(char ** buf_p,char * match)617 void COM_MatchToken( char **buf_p, char *match ) {
618 char *token;
619
620 token = COM_Parse( buf_p );
621 if ( strcmp( token, match ) ) {
622 Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
623 }
624 }
625
626 /*
627 =================
628 SkipBracedSection
629
630 The next token should be an open brace or set depth to 1 if already parsed it.
631 Skips until a matching close brace is found.
632 Internal brace depths are properly skipped.
633 =================
634 */
SkipBracedSection(char ** program,int depth)635 qboolean SkipBracedSection (char **program, int depth) {
636 char *token;
637
638 do {
639 token = COM_ParseExt( program, qtrue );
640 if( token[1] == 0 ) {
641 if( token[0] == '{' ) {
642 depth++;
643 }
644 else if( token[0] == '}' ) {
645 depth--;
646 }
647 }
648 } while( depth && *program );
649
650 return ( depth == 0 );
651 }
652
653 /*
654 =================
655 SkipRestOfLine
656 =================
657 */
SkipRestOfLine(char ** data)658 void SkipRestOfLine( char **data ) {
659 char *p;
660 int c;
661
662 p = *data;
663
664 if ( !*p )
665 return;
666
667 while ( ( c = *p++ ) != 0 ) {
668 if ( c == '\n' ) {
669 com_lines++;
670 break;
671 }
672 }
673
674 *data = p;
675 }
676
677
Parse1DMatrix(char ** buf_p,int x,float * m)678 void Parse1DMatrix( char **buf_p, int x, float *m ) {
679 char *token;
680 int i;
681
682 COM_MatchToken( buf_p, "(" );
683
684 for ( i = 0 ; i < x ; i++ ) {
685 token = COM_Parse( buf_p );
686 m[i] = atof( token );
687 }
688
689 COM_MatchToken( buf_p, ")" );
690 }
691
Parse2DMatrix(char ** buf_p,int y,int x,float * m)692 void Parse2DMatrix( char **buf_p, int y, int x, float *m ) {
693 int i;
694
695 COM_MatchToken( buf_p, "(" );
696
697 for ( i = 0 ; i < y ; i++ ) {
698 Parse1DMatrix( buf_p, x, m + i * x );
699 }
700
701 COM_MatchToken( buf_p, ")" );
702 }
703
Parse3DMatrix(char ** buf_p,int z,int y,int x,float * m)704 void Parse3DMatrix( char **buf_p, int z, int y, int x, float *m ) {
705 int i;
706
707 COM_MatchToken( buf_p, "(" );
708
709 for ( i = 0 ; i < z ; i++ ) {
710 Parse2DMatrix( buf_p, y, x, m + i * x * y );
711 }
712
713 COM_MatchToken( buf_p, ")" );
714 }
715
716 /*
717 ===================
718 Com_HexStrToInt
719 ===================
720 */
Com_HexStrToInt(const char * str)721 int Com_HexStrToInt( const char *str )
722 {
723 if ( !str )
724 return -1;
725
726 // check for hex code
727 if( str[ 0 ] == '0' && str[ 1 ] == 'x' && str[ 2 ] != '\0' )
728 {
729 int i, n = 0, len = strlen( str );
730
731 for( i = 2; i < len; i++ )
732 {
733 char digit;
734
735 n *= 16;
736
737 digit = tolower( str[ i ] );
738
739 if( digit >= '0' && digit <= '9' )
740 digit -= '0';
741 else if( digit >= 'a' && digit <= 'f' )
742 digit = digit - 'a' + 10;
743 else
744 return -1;
745
746 n += digit;
747 }
748
749 return n;
750 }
751
752 return -1;
753 }
754
755 /*
756 ============================================================================
757
758 LIBRARY REPLACEMENT FUNCTIONS
759
760 ============================================================================
761 */
762
Q_isprint(int c)763 int Q_isprint( int c ) {
764 if ( c >= 0x20 && c <= 0x7E ) {
765 return ( 1 );
766 }
767 return ( 0 );
768 }
769
Q_islower(int c)770 int Q_islower( int c ) {
771 if ( c >= 'a' && c <= 'z' ) {
772 return ( 1 );
773 }
774 return ( 0 );
775 }
776
Q_isupper(int c)777 int Q_isupper( int c ) {
778 if ( c >= 'A' && c <= 'Z' ) {
779 return ( 1 );
780 }
781 return ( 0 );
782 }
783
Q_isalpha(int c)784 int Q_isalpha( int c ) {
785 if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ) {
786 return ( 1 );
787 }
788 return ( 0 );
789 }
790
Q_isanumber(const char * s)791 qboolean Q_isanumber( const char *s )
792 {
793 char *p;
794 double UNUSED_VAR d;
795
796 if( *s == '\0' )
797 return qfalse;
798
799 d = strtod( s, &p );
800
801 return *p == '\0';
802 }
803
Q_isintegral(float f)804 qboolean Q_isintegral( float f )
805 {
806 return (int)f == f;
807 }
808
809 #ifdef _WIN32
810 /*
811 =============
812 Q_vsnprintf
813
814 Special wrapper function for Microsoft's broken _vsnprintf() function.
815 MinGW comes with its own vsnprintf() which is not broken. mingw-w64
816 however, uses Microsoft's broken _vsnprintf() function.
817 =============
818 */
819
Q_vsnprintf(char * str,size_t size,const char * format,va_list ap)820 int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
821 {
822 int retval;
823
824 retval = _vsnprintf(str, size, format, ap);
825
826 if(retval < 0 || retval == size)
827 {
828 // Microsoft doesn't adhere to the C99 standard of vsnprintf,
829 // which states that the return value must be the number of
830 // bytes written if the output string had sufficient length.
831 //
832 // Obviously we cannot determine that value from Microsoft's
833 // implementation, so we have no choice but to return size.
834
835 str[size - 1] = '\0';
836 return size;
837 }
838
839 return retval;
840 }
841 #endif
842
843 /*
844 =============
845 Q_strncpyz
846
847 Safe strncpy that ensures a trailing zero
848 =============
849 */
Q_strncpyz(char * dest,const char * src,int destsize)850 void Q_strncpyz( char *dest, const char *src, int destsize ) {
851 if ( !dest ) {
852 Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
853 }
854 if ( !src ) {
855 Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
856 }
857 if ( destsize < 1 ) {
858 Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
859 }
860
861 strncpy( dest, src, destsize-1 );
862
863 dest[destsize-1] = 0;
864 }
865
Q_stricmpn(const char * s1,const char * s2,int n)866 int Q_stricmpn (const char *s1, const char *s2, int n) {
867 int c1, c2;
868
869 if ( s1 == NULL ) {
870 if ( s2 == NULL )
871 return 0;
872 else
873 return -1;
874 }
875 else if ( s2==NULL )
876 return 1;
877
878
879
880 do {
881 c1 = *s1++;
882 c2 = *s2++;
883
884 if (!n--) {
885 return 0; // strings are equal until end point
886 }
887
888 if (c1 != c2) {
889 if (c1 >= 'a' && c1 <= 'z') {
890 c1 -= ('a' - 'A');
891 }
892 if (c2 >= 'a' && c2 <= 'z') {
893 c2 -= ('a' - 'A');
894 }
895 if (c1 != c2) {
896 return c1 < c2 ? -1 : 1;
897 }
898 }
899 } while (c1);
900
901 return 0; // strings are equal
902 }
903
Q_strncmp(const char * s1,const char * s2,int n)904 int Q_strncmp( const char *s1, const char *s2, int n ) {
905 int c1, c2;
906
907 do {
908 c1 = *s1++;
909 c2 = *s2++;
910
911 if ( !n-- ) {
912 return 0; // strings are equal until end point
913 }
914
915 if ( c1 != c2 ) {
916 return c1 < c2 ? -1 : 1;
917 }
918 } while ( c1 );
919
920 return 0; // strings are equal
921 }
922
Q_stricmp(const char * s1,const char * s2)923 int Q_stricmp( const char *s1, const char *s2 ) {
924 return ( s1 && s2 ) ? Q_stricmpn( s1, s2, 99999 ) : -1;
925 }
926
927
Q_strlwr(char * s1)928 char *Q_strlwr( char *s1 ) {
929 char *s;
930
931 s = s1;
932 while ( *s ) {
933 *s = tolower( *s );
934 s++;
935 }
936 return s1;
937 }
938
Q_strupr(char * s1)939 char *Q_strupr( char *s1 ) {
940 char *s;
941
942 s = s1;
943 while ( *s ) {
944 *s = toupper( *s );
945 s++;
946 }
947 return s1;
948 }
949
950
951 // never goes past bounds or leaves without a terminating 0
Q_strcat(char * dest,int size,const char * src)952 void Q_strcat( char *dest, int size, const char *src ) {
953 int l1;
954
955 l1 = strlen( dest );
956 if ( l1 >= size ) {
957 Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
958 }
959 Q_strncpyz( dest + l1, src, size - l1 );
960 }
961
962 /*
963 * Find the first occurrence of find in s.
964 */
Q_stristr(const char * s,const char * find)965 const char *Q_stristr( const char *s, const char *find)
966 {
967 char c, sc;
968 size_t len;
969
970 if ((c = *find++) != 0)
971 {
972 if (c >= 'a' && c <= 'z')
973 {
974 c -= ('a' - 'A');
975 }
976 len = strlen(find);
977 do
978 {
979 do
980 {
981 if ((sc = *s++) == 0)
982 return NULL;
983 if (sc >= 'a' && sc <= 'z')
984 {
985 sc -= ('a' - 'A');
986 }
987 } while (sc != c);
988 } while (Q_stricmpn(s, find, len) != 0);
989 s--;
990 }
991 return s;
992 }
993
994
Q_PrintStrlen(const char * string)995 int Q_PrintStrlen( const char *string ) {
996 int len;
997 const char *p;
998
999 if ( !string ) {
1000 return 0;
1001 }
1002
1003 len = 0;
1004 p = string;
1005 while ( *p ) {
1006 if ( Q_IsColorString( p ) ) {
1007 p += 2;
1008 continue;
1009 }
1010 p++;
1011 len++;
1012 }
1013
1014 return len;
1015 }
1016
1017
Q_CleanStr(char * string)1018 char *Q_CleanStr( char *string ) {
1019 char* d;
1020 char* s;
1021 int c;
1022
1023 s = string;
1024 d = string;
1025 while ( ( c = *s ) != 0 ) {
1026 if ( Q_IsColorString( s ) ) {
1027 s++;
1028 } else if ( c >= 0x20 && c <= 0x7E ) {
1029 *d++ = c;
1030 }
1031 s++;
1032 }
1033 *d = '\0';
1034
1035 return string;
1036 }
1037
Q_CountChar(const char * string,char tocount)1038 int Q_CountChar(const char *string, char tocount)
1039 {
1040 int count;
1041
1042 for(count = 0; *string; string++)
1043 {
1044 if(*string == tocount)
1045 count++;
1046 }
1047
1048 return count;
1049 }
1050
Com_sprintf(char * dest,int size,const char * fmt,...)1051 int QDECL Com_sprintf(char *dest, int size, const char *fmt, ...)
1052 {
1053 int len;
1054 va_list argptr;
1055
1056 /*
1057 C99 for vsnprintf:
1058 return the number of characters (excluding the trailing '\0')
1059 which would have been written to the final string if enough space had been available.
1060 */
1061 va_start( argptr,fmt );
1062 len = Q_vsnprintf( dest, size, fmt, argptr );
1063 va_end( argptr );
1064
1065 if ( len >= size ) {
1066 Com_Printf("Com_sprintf: Output length %d too short, requires %d bytes.\n", size, len + 1);
1067 }
1068
1069 return len;
1070 }
1071
1072 // Ridah, ripped from l_bsp.c
Q_strncasecmp(char * s1,char * s2,int n)1073 int Q_strncasecmp( char *s1, char *s2, int n ) {
1074 int c1, c2;
1075
1076 do
1077 {
1078 c1 = *s1++;
1079 c2 = *s2++;
1080
1081 if ( !n-- ) {
1082 return 0; // strings are equal until end point
1083
1084 }
1085 if ( c1 != c2 ) {
1086 if ( c1 >= 'a' && c1 <= 'z' ) {
1087 c1 -= ( 'a' - 'A' );
1088 }
1089 if ( c2 >= 'a' && c2 <= 'z' ) {
1090 c2 -= ( 'a' - 'A' );
1091 }
1092 if ( c1 != c2 ) {
1093 return -1; // strings not equal
1094 }
1095 }
1096 } while ( c1 );
1097
1098 return 0; // strings are equal
1099 }
1100
Q_strcasecmp(char * s1,char * s2)1101 int Q_strcasecmp( char *s1, char *s2 ) {
1102 return Q_strncasecmp( s1, s2, 99999 );
1103 }
1104 // done.
1105
1106 /*
1107 ============
1108 va
1109
1110 does a varargs printf into a temp buffer, so I don't need to have
1111 varargs versions of all text functions.
1112
1113 Ridah, modified this into a circular list, to further prevent stepping on
1114 previous strings
1115 ============
1116 */
va(char * format,...)1117 char * QDECL va( char *format, ... ) {
1118 va_list argptr;
1119 #define MAX_VA_STRING 32000
1120 static char temp_buffer[MAX_VA_STRING];
1121 static char string[MAX_VA_STRING]; // in case va is called by nested functions
1122 static int index = 0;
1123 char *buf;
1124 int len;
1125
1126
1127 va_start( argptr, format );
1128 Q_vsnprintf (temp_buffer, sizeof(temp_buffer), format, argptr);
1129 va_end( argptr );
1130
1131 if ( ( len = strlen( temp_buffer ) ) >= MAX_VA_STRING ) {
1132 Com_Error( ERR_DROP, "Attempted to overrun string in call to va()\n" );
1133 }
1134
1135 if ( len + index >= MAX_VA_STRING - 1 ) {
1136 index = 0;
1137 }
1138
1139 buf = &string[index];
1140 memcpy( buf, temp_buffer, len + 1 );
1141
1142 index += len + 1;
1143
1144 return buf;
1145 }
1146
1147 /*
1148 ============
1149 Com_TruncateLongString
1150
1151 Assumes buffer is atleast TRUNCATE_LENGTH big
1152 ============
1153 */
Com_TruncateLongString(char * buffer,const char * s)1154 void Com_TruncateLongString( char *buffer, const char *s )
1155 {
1156 int length = strlen( s );
1157
1158 if( length <= TRUNCATE_LENGTH )
1159 Q_strncpyz( buffer, s, TRUNCATE_LENGTH );
1160 else
1161 {
1162 Q_strncpyz( buffer, s, ( TRUNCATE_LENGTH / 2 ) - 3 );
1163 Q_strcat( buffer, TRUNCATE_LENGTH, " ... " );
1164 Q_strcat( buffer, TRUNCATE_LENGTH, s + length - ( TRUNCATE_LENGTH / 2 ) + 3 );
1165 }
1166 }
1167
1168 /*
1169 =============
1170 TempVector
1171
1172 (SA) this is straight out of g_utils.c around line 210
1173
1174 This is just a convenience function
1175 for making temporary vectors for function calls
1176 =============
1177 */
tv(float x,float y,float z)1178 float *tv( float x, float y, float z ) {
1179 static int index;
1180 static vec3_t vecs[8];
1181 float *v;
1182
1183 // use an array so that multiple tempvectors won't collide
1184 // for a while
1185 v = vecs[index];
1186 index = ( index + 1 ) & 7;
1187
1188 v[0] = x;
1189 v[1] = y;
1190 v[2] = z;
1191
1192 return v;
1193 }
1194
1195 /*
1196 =====================================================================
1197
1198 INFO STRINGS
1199
1200 =====================================================================
1201 */
1202
1203 /*
1204 ===============
1205 Info_ValueForKey
1206
1207 Searches the string for the given
1208 key and returns the associated value, or an empty string.
1209 FIXME: overflow check?
1210 ===============
1211 */
Info_ValueForKey(const char * s,const char * key)1212 char *Info_ValueForKey( const char *s, const char *key ) {
1213 char pkey[BIG_INFO_KEY];
1214 static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
1215 // work without stomping on each other
1216 static int valueindex = 0;
1217 char *o;
1218
1219 if ( !s || !key ) {
1220 return "";
1221 }
1222
1223 if ( strlen( s ) >= BIG_INFO_STRING ) {
1224 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
1225 }
1226
1227 valueindex ^= 1;
1228 if ( *s == '\\' ) {
1229 s++;
1230 }
1231 while ( 1 )
1232 {
1233 o = pkey;
1234 while ( *s != '\\' )
1235 {
1236 if ( !*s ) {
1237 return "";
1238 }
1239 *o++ = *s++;
1240 }
1241 *o = 0;
1242 s++;
1243
1244 o = value[valueindex];
1245
1246 while ( *s != '\\' && *s )
1247 {
1248 *o++ = *s++;
1249 }
1250 *o = 0;
1251
1252 if ( !Q_stricmp( key, pkey ) ) {
1253 return value[valueindex];
1254 }
1255
1256 if ( !*s ) {
1257 break;
1258 }
1259 s++;
1260 }
1261
1262 return "";
1263 }
1264
1265
1266 /*
1267 ===================
1268 Info_NextPair
1269
1270 Used to itterate through all the key/value pairs in an info string
1271 ===================
1272 */
Info_NextPair(const char ** head,char * key,char * value)1273 void Info_NextPair( const char **head, char *key, char *value ) {
1274 char *o;
1275 const char *s;
1276
1277 s = *head;
1278
1279 if ( *s == '\\' ) {
1280 s++;
1281 }
1282 key[0] = 0;
1283 value[0] = 0;
1284
1285 o = key;
1286 while ( *s != '\\' ) {
1287 if ( !*s ) {
1288 *o = 0;
1289 *head = s;
1290 return;
1291 }
1292 *o++ = *s++;
1293 }
1294 *o = 0;
1295 s++;
1296
1297 o = value;
1298 while ( *s != '\\' && *s ) {
1299 *o++ = *s++;
1300 }
1301 *o = 0;
1302
1303 *head = s;
1304 }
1305
1306
1307 /*
1308 ===================
1309 Info_RemoveKey
1310 ===================
1311 */
Info_RemoveKey(char * s,const char * key)1312 void Info_RemoveKey( char *s, const char *key ) {
1313 char *start;
1314 char pkey[MAX_INFO_KEY];
1315 char value[MAX_INFO_VALUE];
1316 char *o;
1317
1318 if ( strlen( s ) >= MAX_INFO_STRING ) {
1319 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
1320 }
1321
1322 if ( strchr( key, '\\' ) ) {
1323 return;
1324 }
1325
1326 while ( 1 )
1327 {
1328 start = s;
1329 if ( *s == '\\' ) {
1330 s++;
1331 }
1332 o = pkey;
1333 while ( *s != '\\' )
1334 {
1335 if ( !*s ) {
1336 return;
1337 }
1338 *o++ = *s++;
1339 }
1340 *o = 0;
1341 s++;
1342
1343 o = value;
1344 while ( *s != '\\' && *s )
1345 {
1346 if ( !*s ) {
1347 return;
1348 }
1349 *o++ = *s++;
1350 }
1351 *o = 0;
1352
1353 if ( !strcmp( key, pkey ) ) {
1354 memmove(start, s, strlen(s) + 1); // remove this part
1355 return;
1356 }
1357
1358 if ( !*s ) {
1359 return;
1360 }
1361 }
1362
1363 }
1364
1365 /*
1366 ===================
1367 Info_RemoveKey_Big
1368 ===================
1369 */
Info_RemoveKey_Big(char * s,const char * key)1370 void Info_RemoveKey_Big( char *s, const char *key ) {
1371 char *start;
1372 char pkey[BIG_INFO_KEY];
1373 char value[BIG_INFO_VALUE];
1374 char *o;
1375
1376 if ( strlen( s ) >= BIG_INFO_STRING ) {
1377 Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
1378 }
1379
1380 if ( strchr( key, '\\' ) ) {
1381 return;
1382 }
1383
1384 while ( 1 )
1385 {
1386 start = s;
1387 if ( *s == '\\' ) {
1388 s++;
1389 }
1390 o = pkey;
1391 while ( *s != '\\' )
1392 {
1393 if ( !*s ) {
1394 return;
1395 }
1396 *o++ = *s++;
1397 }
1398 *o = 0;
1399 s++;
1400
1401 o = value;
1402 while ( *s != '\\' && *s )
1403 {
1404 if ( !*s ) {
1405 return;
1406 }
1407 *o++ = *s++;
1408 }
1409 *o = 0;
1410
1411 if ( !strcmp( key, pkey ) ) {
1412 memmove(start, s, strlen(s) + 1); // remove this part
1413 return;
1414 }
1415
1416 if ( !*s ) {
1417 return;
1418 }
1419 }
1420
1421 }
1422
1423
1424
1425
1426 /*
1427 ==================
1428 Info_Validate
1429
1430 Some characters are illegal in info strings because they
1431 can mess up the server's parsing
1432 ==================
1433 */
Info_Validate(const char * s)1434 qboolean Info_Validate( const char *s ) {
1435 if ( strchr( s, '\"' ) ) {
1436 return qfalse;
1437 }
1438 if ( strchr( s, ';' ) ) {
1439 return qfalse;
1440 }
1441 return qtrue;
1442 }
1443
1444 /*
1445 ==================
1446 Info_SetValueForKey
1447
1448 Changes or adds a key/value pair
1449 ==================
1450 */
Info_SetValueForKey(char * s,const char * key,const char * value)1451 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
1452 char newi[MAX_INFO_STRING];
1453 const char* blacklist = "\\;\"";
1454
1455 if ( strlen( s ) >= MAX_INFO_STRING ) {
1456 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
1457 }
1458
1459 for(; *blacklist; ++blacklist)
1460 {
1461 if (strchr (key, *blacklist) || strchr (value, *blacklist))
1462 {
1463 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1464 return;
1465 }
1466 }
1467
1468 Info_RemoveKey( s, key );
1469 if ( !value || !strlen( value ) ) {
1470 return;
1471 }
1472
1473 Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
1474
1475 if ( strlen( newi ) + strlen( s ) >= MAX_INFO_STRING ) {
1476 Com_Printf( "Info string length exceeded\n" );
1477 return;
1478 }
1479
1480 strcat (newi, s);
1481 strcpy (s, newi);
1482 }
1483
1484 /*
1485 ==================
1486 Info_SetValueForKey_Big
1487
1488 Changes or adds a key/value pair
1489 Includes and retains zero-length values
1490 ==================
1491 */
Info_SetValueForKey_Big(char * s,const char * key,const char * value)1492 void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
1493 char newi[BIG_INFO_STRING];
1494 const char* blacklist = "\\;\"";
1495
1496 if ( strlen( s ) >= BIG_INFO_STRING ) {
1497 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
1498 }
1499
1500 for(; *blacklist; ++blacklist)
1501 {
1502 if (strchr (key, *blacklist) || strchr (value, *blacklist))
1503 {
1504 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1505 return;
1506 }
1507 }
1508
1509 Info_RemoveKey_Big( s, key );
1510 if ( !value ) {
1511 return;
1512 }
1513
1514 Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );
1515
1516 if ( strlen( newi ) + strlen( s ) >= BIG_INFO_STRING ) {
1517 Com_Printf( "BIG Info string length exceeded\n" );
1518 return;
1519 }
1520
1521 strcat( s, newi );
1522 }
1523
1524
1525
1526
1527 //====================================================================
1528
1529 /*
1530 ==================
1531 Com_CharIsOneOfCharset
1532 ==================
1533 */
Com_CharIsOneOfCharset(char c,char * set)1534 static qboolean Com_CharIsOneOfCharset( char c, char *set )
1535 {
1536 int i;
1537
1538 for( i = 0; i < strlen( set ); i++ )
1539 {
1540 if( set[ i ] == c )
1541 return qtrue;
1542 }
1543
1544 return qfalse;
1545 }
1546
1547 /*
1548 ==================
1549 Com_SkipCharset
1550 ==================
1551 */
Com_SkipCharset(char * s,char * sep)1552 char *Com_SkipCharset( char *s, char *sep )
1553 {
1554 char *p = s;
1555
1556 while( p )
1557 {
1558 if( Com_CharIsOneOfCharset( *p, sep ) )
1559 p++;
1560 else
1561 break;
1562 }
1563
1564 return p;
1565 }
1566
1567 /*
1568 ==================
1569 Com_SkipTokens
1570 ==================
1571 */
Com_SkipTokens(char * s,int numTokens,char * sep)1572 char *Com_SkipTokens( char *s, int numTokens, char *sep )
1573 {
1574 int sepCount = 0;
1575 char *p = s;
1576
1577 while( sepCount < numTokens )
1578 {
1579 if( Com_CharIsOneOfCharset( *p++, sep ) )
1580 {
1581 sepCount++;
1582 while( Com_CharIsOneOfCharset( *p, sep ) )
1583 p++;
1584 }
1585 else if( *p == '\0' )
1586 break;
1587 }
1588
1589 if( sepCount == numTokens )
1590 return p;
1591 else
1592 return s;
1593 }
1594