2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
22 // q_shared.c -- stateless support routines that are included in each code dll
\r
23 #include "q_shared.h"
\r
26 ============================================================================
\r
30 ============================================================================
\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
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
43 int Com_AddToGrowList( growList_t *list, void *data ) {
\r
46 if ( list->currentElements != list->maxElements ) {
\r
47 list->elements[list->currentElements] = data;
\r
48 return list->currentElements++;
\r
51 // grow, reallocate and move
\r
52 old = list->elements;
\r
54 if ( list->maxElements < 0 ) {
\r
55 Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements );
\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
64 list->maxElements *= 2;
\r
66 Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements );
\r
68 list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) );
\r
70 if ( !list->elements ) {
\r
71 Com_Error( ERR_DROP, "Growlist alloc failed" );
\r
74 memcpy( list->elements, old, list->currentElements * sizeof( void * ) );
\r
78 return Com_AddToGrowList( list, data );
\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
86 return list->elements[index];
\r
89 int Com_IndexForGrowListElement( const growList_t *list, const void *element ) {
\r
92 for ( i = 0 ; i < list->currentElements ; i++ ) {
\r
93 if ( list->elements[i] == element ) {
\r
100 //============================================================================
\r
103 float Com_Clamp( float min, float max, float value ) {
\r
104 if ( value < min ) {
\r
107 if ( value > max ) {
\r
118 const char *Com_StringContains( const char *str1, const char *str2, int casesensitive) {
\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
130 if (toupper(str1[j]) != toupper(str2[j])) {
\r
147 int Com_Filter( const char *filter, const char *name, int casesensitive)
\r
149 char buf[MAX_TOKEN_CHARS];
\r
154 if (*filter == '*') {
\r
156 for (i = 0; *filter; i++) {
\r
157 if (*filter == '*' || *filter == '?') break;
\r
163 ptr = Com_StringContains(name, buf, casesensitive);
\r
164 if (!ptr) return qfalse;
\r
165 name = ptr + strlen(buf);
\r
168 else if (*filter == '?') {
\r
172 else if (*filter == '[' && *(filter+1) == '[') {
\r
175 else if (*filter == '[') {
\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
185 if (toupper(*name) >= toupper(*filter) &&
\r
186 toupper(*name) <= toupper(*(filter+2))) found = qtrue;
\r
191 if (casesensitive) {
\r
192 if (*filter == *name) found = qtrue;
\r
195 if (toupper(*filter) == toupper(*name)) found = qtrue;
\r
200 if (!found) return qfalse;
\r
202 if (*filter == ']' && *(filter+1) != ']') break;
\r
209 if (casesensitive) {
\r
210 if (*filter != *name) return qfalse;
\r
213 if (toupper(*filter) != toupper(*name)) return qfalse;
\r
229 int Com_HashString( const char *fname ) {
\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
243 hash &= (FILE_HASH_SIZE-1);
\r
253 char *Com_SkipPath (char *pathname)
\r
260 if (*pathname=='/')
\r
272 void Com_StripExtension( const char *in, char *out ) {
\r
273 while ( *in && *in != '.' ) {
\r
282 Com_DefaultExtension
\r
285 void Com_DefaultExtension (char *path, int maxSize, const char *extension ) {
\r
286 char oldPath[MAX_QPATH];
\r
290 // if path doesn't have a .EXT, append extension
\r
291 // (extension should include the .)
\r
293 src = path + strlen(path) - 1;
\r
295 while (*src != '/' && src != path) {
\r
296 if ( *src == '.' ) {
\r
297 return; // it has an extension
\r
302 Q_strncpyz( oldPath, path, sizeof( oldPath ) );
\r
303 Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
\r
307 ============================================================================
\r
309 BYTE ORDER FUNCTIONS
\r
311 ============================================================================
\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
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
330 short ShortSwap (short l)
\r
337 return (b1<<8) + b2;
\r
340 short ShortNoSwap (short l)
\r
345 int LongSwap (int l)
\r
354 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
\r
357 int LongNoSwap (int l)
\r
362 float FloatSwap (float 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
379 float FloatNoSwap (float f)
\r
389 void Swap_Init (void)
\r
391 byte swaptest[2] = {1,0};
\r
393 // set the byte swapping variables in a portable manner
\r
394 if ( *(short *)swaptest == 1)
\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
405 _BigShort = ShortNoSwap;
\r
406 _LittleShort = ShortSwap;
\r
407 _BigLong = LongNoSwap;
\r
408 _LittleLong = LongSwap;
\r
409 _BigFloat = FloatNoSwap;
\r
410 _LittleFloat = FloatSwap;
\r
420 int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) {
\r
423 char key[MAX_TOKEN_CHARS];
\r
428 token = Com_Parse( &buf );
\r
432 if ( strcmp( token, "{" ) ) {
\r
433 Com_Printf( "Missing { in info file\n" );
\r
437 if ( count == max ) {
\r
438 Com_Printf( "Max infos exceeded\n" );
\r
442 infos[count][0] = 0;
\r
444 token = Com_Parse( &buf );
\r
446 Com_Printf( "Unexpected end of info file\n" );
\r
449 if ( !strcmp( token, "}" ) ) {
\r
452 Q_strncpyz( key, token, sizeof( key ) );
\r
454 token = Com_ParseOnLine( &buf );
\r
458 Info_SetValueForKey( infos[count], key, token );
\r
469 ============================================================================
\r
471 LIBRARY REPLACEMENT FUNCTIONS
\r
473 ============================================================================
\r
476 int Q_isprint( int c )
\r
478 if ( c >= 0x20 && c <= 0x7E )
\r
483 int Q_islower( int c )
\r
485 if (c >= 'a' && c <= 'z')
\r
490 int Q_isupper( int c )
\r
492 if (c >= 'A' && c <= 'Z')
\r
497 int Q_isalpha( int c )
\r
499 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
\r
504 char* Q_strrchr( const char* string, int c )
\r
508 char *sp=(char *)0;
\r
528 Safe strncpy that ensures a trailing zero
\r
531 void Q_strncpyz( char *dest, const char *src, int destsize ) {
\r
533 Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
\r
535 if ( destsize < 1 ) {
\r
536 Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
\r
539 strncpy( dest, src, destsize-1 );
\r
540 dest[destsize-1] = 0;
\r
543 int Q_stricmpn (const char *s1, const char *s2, int n) {
\r
551 return 0; // strings are equal until end point
\r
555 if (c1 >= 'a' && c1 <= 'z') {
\r
558 if (c2 >= 'a' && c2 <= 'z') {
\r
562 return c1 < c2 ? -1 : 1;
\r
567 return 0; // strings are equal
\r
570 int Q_strncmp (const char *s1, const char *s2, int n) {
\r
578 return 0; // strings are equal until end point
\r
582 return c1 < c2 ? -1 : 1;
\r
586 return 0; // strings are equal
\r
589 int Q_stricmp (const char *s1, const char *s2) {
\r
590 return Q_stricmpn (s1, s2, 99999);
\r
594 char *Q_strlwr( char *s1 ) {
\r
605 char *Q_strupr( char *s1 ) {
\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
621 l1 = strlen( dest );
\r
622 if ( l1 >= size ) {
\r
623 Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
\r
625 Q_strncpyz( dest + l1, src, size - l1 );
\r
629 int Q_PrintStrlen( const char *string ) {
\r
640 if( Q_IsColorString( p ) ) {
\r
652 char *Q_CleanStr( char *string ) {
\r
659 while ((c = *s) != 0 ) {
\r
660 if ( Q_IsColorString( s ) ) {
\r
663 else if ( c >= 0x20 && c <= 0x7E ) {
\r
674 void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
\r
677 char bigbuffer[32000]; // big, but small enough to fit in PPC stack
\r
679 va_start (argptr,fmt);
\r
680 len = vsprintf (bigbuffer,fmt,argptr);
\r
682 if ( len >= sizeof( bigbuffer ) ) {
\r
683 Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
\r
686 Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
\r
688 Q_strncpyz (dest, bigbuffer, size );
\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
701 char * QDECL va( char *format, ... ) {
\r
703 static char string[2][32000]; // in case va is called by nested functions
\r
704 static int index = 0;
\r
707 buf = string[index & 1];
\r
710 va_start (argptr, format);
\r
711 vsprintf (buf, format,argptr);
\r
719 =====================================================================
\r
723 =====================================================================
\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
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
742 if ( !s || !key ) {
\r
746 if ( strlen( s ) >= MAX_INFO_STRING ) {
\r
747 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
\r
765 o = value[valueindex];
\r
767 while (*s != '\\' && *s)
\r
773 if (!Q_stricmp (key, pkey) )
\r
774 return value[valueindex];
\r
786 ===================
\r
789 Used to itterate through all the key/value pairs in an info string
\r
790 ===================
\r
792 void Info_NextPair( const char *(*head), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {
\r
798 if ( *s == '\\' ) {
\r
805 while ( *s != '\\' ) {
\r
817 while ( *s != '\\' && *s ) {
\r
827 ===================
\r
829 ===================
\r
831 void Info_RemoveKey( char *s, const char *key ) {
\r
833 char pkey[MAX_INFO_KEY];
\r
834 char value[MAX_INFO_VALUE];
\r
837 if ( strlen( s ) >= MAX_INFO_STRING ) {
\r
838 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
\r
841 if (strchr (key, '\\')) {
\r
861 while (*s != '\\' && *s)
\r
869 if (!strcmp (key, pkey) )
\r
871 strcpy (start, s); // remove this part
\r
886 Some characters are illegal in info strings because they
\r
887 can mess up the server's parsing
\r
890 qboolean Info_Validate( const char *s ) {
\r
891 if ( strchr( s, '\"' ) ) {
\r
894 if ( strchr( s, ';' ) ) {
\r
902 Info_SetValueForKey
\r
904 Changes or adds a key/value pair
\r
907 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
\r
908 char newi[MAX_INFO_STRING];
\r
910 if ( strlen( s ) >= MAX_INFO_STRING ) {
\r
911 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
\r
914 if (strchr (key, '\\') || strchr (value, '\\'))
\r
916 Com_Printf ("Can't use keys or values with a \\\n");
\r
920 if (strchr (key, ';') || strchr (value, ';'))
\r
922 Com_Printf ("Can't use keys or values with a semicolon\n");
\r
926 if (strchr (key, '\"') || strchr (value, '\"'))
\r
928 Com_Printf ("Can't use keys or values with a \"\n");
\r
932 Info_RemoveKey (s, key);
\r
933 if (!value || !strlen(value))
\r
936 Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
\r
938 if (strlen(newi) + strlen(s) > MAX_INFO_STRING)
\r
940 Com_Printf ("Info string length exceeded\n");
\r
947 //====================================================================
\r
955 int ParseHex( const char *text ) {
\r
960 while ( ( c = *text++ ) != 0 ) {
\r
961 if ( c >= '0' && c <= '9' ) {
\r
962 value = value * 16 + c - '0';
\r
965 if ( c >= 'a' && c <= 'f' ) {
\r
966 value = value * 16 + 10 + c - 'a';
\r
969 if ( c >= 'A' && c <= 'F' ) {
\r
970 value = value * 16 + 10 + c - 'A';
\r