]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
Add THREADDISABLE define to make builds with whole threading system disabled.
[xonotic/darkplaces.git] / common.c
index aaa9fd6859ac9c0cc89a010df177e4cca0b14f71..b5005c6415accd4b461af261fa1cd6ce73c053ff 100644 (file)
--- a/common.c
+++ b/common.c
@@ -19,20 +19,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // common.c -- misc functions used in client and server
 
 */
 // common.c -- misc functions used in client and server
 
-#include "quakedef.h"
-
 #include <stdlib.h>
 #include <fcntl.h>
 #ifndef WIN32
 #include <unistd.h>
 #endif
 
 #include <stdlib.h>
 #include <fcntl.h>
 #ifndef WIN32
 #include <unistd.h>
 #endif
 
+#include "quakedef.h"
+#include "utf8lib.h"
+
 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
 
 char com_token[MAX_INPUTLINE];
 int com_argc;
 const char **com_argv;
 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
 
 char com_token[MAX_INPUTLINE];
 int com_argc;
 const char **com_argv;
+int com_selffd = -1;
 
 gamemode_t gamemode;
 const char *gamename;
 
 gamemode_t gamemode;
 const char *gamename;
@@ -51,68 +53,78 @@ char com_modname[MAX_OSPATH] = "";
 ============================================================================
 */
 
 ============================================================================
 */
 
-short   ShortSwap (short l)
-{
-       unsigned char    b1,b2;
-
-       b1 = l&255;
-       b2 = (l>>8)&255;
 
 
-       return (b1<<8) + b2;
+float BuffBigFloat (const unsigned char *buffer)
+{
+       union
+       {
+               float f;
+               unsigned int i;
+       }
+       u;
+       u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       return u.f;
 }
 
 }
 
-int    LongSwap (int l)
+int BuffBigLong (const unsigned char *buffer)
 {
 {
-       unsigned char    b1,b2,b3,b4;
-
-       b1 = l&255;
-       b2 = (l>>8)&255;
-       b3 = (l>>16)&255;
-       b4 = (l>>24)&255;
+       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+}
 
 
-       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+short BuffBigShort (const unsigned char *buffer)
+{
+       return (buffer[0] << 8) | buffer[1];
 }
 
 }
 
-float FloatSwap (float f)
+float BuffLittleFloat (const unsigned char *buffer)
 {
        union
        {
 {
        union
        {
-               float   f;
-               unsigned char    b[4];
-       } dat1, dat2;
-
-
-       dat1.f = f;
-       dat2.b[0] = dat1.b[3];
-       dat2.b[1] = dat1.b[2];
-       dat2.b[2] = dat1.b[1];
-       dat2.b[3] = dat1.b[0];
-       return dat2.f;
+               float f;
+               unsigned int i;
+       }
+       u;
+       u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       return u.f;
 }
 
 }
 
+int BuffLittleLong (const unsigned char *buffer)
+{
+       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+}
 
 
-// Extract integers from buffers
-
-unsigned int BuffBigLong (const unsigned char *buffer)
+short BuffLittleShort (const unsigned char *buffer)
 {
 {
-       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       return (buffer[1] << 8) | buffer[0];
 }
 
 }
 
-unsigned short BuffBigShort (const unsigned char *buffer)
+void StoreBigLong (unsigned char *buffer, unsigned int i)
 {
 {
-       return (buffer[0] << 8) | buffer[1];
+       buffer[0] = (i >> 24) & 0xFF;
+       buffer[1] = (i >> 16) & 0xFF;
+       buffer[2] = (i >>  8) & 0xFF;
+       buffer[3] = i         & 0xFF;
 }
 
 }
 
-unsigned int BuffLittleLong (const unsigned char *buffer)
+void StoreBigShort (unsigned char *buffer, unsigned short i)
 {
 {
-       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       buffer[0] = (i >>  8) & 0xFF;
+       buffer[1] = i         & 0xFF;
 }
 
 }
 
-unsigned short BuffLittleShort (const unsigned char *buffer)
+void StoreLittleLong (unsigned char *buffer, unsigned int i)
 {
 {
-       return (buffer[1] << 8) | buffer[0];
+       buffer[0] = i         & 0xFF;
+       buffer[1] = (i >>  8) & 0xFF;
+       buffer[2] = (i >> 16) & 0xFF;
+       buffer[3] = (i >> 24) & 0xFF;
 }
 
 }
 
+void StoreLittleShort (unsigned char *buffer, unsigned short i)
+{
+       buffer[0] = i         & 0xFF;
+       buffer[1] = (i >>  8) & 0xFF;
+}
 
 /*
 ============================================================================
 
 /*
 ============================================================================
@@ -173,6 +185,14 @@ unsigned short CRC_Block(const unsigned char *data, size_t size)
        return crc ^ CRC_XOR_VALUE;
 }
 
        return crc ^ CRC_XOR_VALUE;
 }
 
+unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
+{
+       unsigned short crc = CRC_INIT_VALUE;
+       while (size--)
+               crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
+       return crc ^ CRC_XOR_VALUE;
+}
+
 // QuakeWorld
 static unsigned char chktbl[1024 + 4] =
 {
 // QuakeWorld
 static unsigned char chktbl[1024 + 4] =
 {
@@ -384,167 +404,167 @@ void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
 //
 // reading functions
 //
 //
 // reading functions
 //
-int msg_readcount;
-qboolean msg_badread;
 
 
-void MSG_BeginReading (void)
+void MSG_BeginReading(sizebuf_t *sb)
 {
 {
-       msg_readcount = 0;
-       msg_badread = false;
+       sb->readcount = 0;
+       sb->badread = false;
 }
 
 }
 
-int MSG_ReadLittleShort (void)
+int MSG_ReadLittleShort(sizebuf_t *sb)
 {
 {
-       if (msg_readcount+2 > net_message.cursize)
+       if (sb->readcount+2 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 2;
-       return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
+       sb->readcount += 2;
+       return (short)(sb->data[sb->readcount-2] | (sb->data[sb->readcount-1]<<8));
 }
 
 }
 
-int MSG_ReadBigShort (void)
+int MSG_ReadBigShort (sizebuf_t *sb)
 {
 {
-       if (msg_readcount+2 > net_message.cursize)
+       if (sb->readcount+2 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 2;
-       return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
+       sb->readcount += 2;
+       return (short)((sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]);
 }
 
 }
 
-int MSG_ReadLittleLong (void)
+int MSG_ReadLittleLong (sizebuf_t *sb)
 {
 {
-       if (msg_readcount+4 > net_message.cursize)
+       if (sb->readcount+4 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 4;
-       return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
+       sb->readcount += 4;
+       return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
 }
 
 }
 
-int MSG_ReadBigLong (void)
+int MSG_ReadBigLong (sizebuf_t *sb)
 {
 {
-       if (msg_readcount+4 > net_message.cursize)
+       if (sb->readcount+4 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 4;
-       return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
+       sb->readcount += 4;
+       return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
 }
 
 }
 
-float MSG_ReadLittleFloat (void)
+float MSG_ReadLittleFloat (sizebuf_t *sb)
 {
        union
        {
                float f;
                int l;
        } dat;
 {
        union
        {
                float f;
                int l;
        } dat;
-       if (msg_readcount+4 > net_message.cursize)
+       if (sb->readcount+4 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 4;
-       dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
+       sb->readcount += 4;
+       dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
        return dat.f;
 }
 
        return dat.f;
 }
 
-float MSG_ReadBigFloat (void)
+float MSG_ReadBigFloat (sizebuf_t *sb)
 {
        union
        {
                float f;
                int l;
        } dat;
 {
        union
        {
                float f;
                int l;
        } dat;
-       if (msg_readcount+4 > net_message.cursize)
+       if (sb->readcount+4 > sb->cursize)
        {
        {
-               msg_badread = true;
+               sb->badread = true;
                return -1;
        }
                return -1;
        }
-       msg_readcount += 4;
-       dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
+       sb->readcount += 4;
+       dat.l = (sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1];
        return dat.f;
 }
 
        return dat.f;
 }
 
-char *MSG_ReadString (void)
+char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring)
 {
 {
-       static char string[MAX_INPUTLINE];
-       int l,c;
-       for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
-               string[l] = c;
+       int c;
+       size_t l = 0;
+       // read string into sbfer, but only store as many characters as will fit
+       while ((c = MSG_ReadByte(sb)) > 0)
+               if (l < maxstring - 1)
+                       string[l++] = c;
        string[l] = 0;
        return string;
 }
 
        string[l] = 0;
        return string;
 }
 
-int MSG_ReadBytes (int numbytes, unsigned char *out)
+int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
 {
        int l, c;
 {
        int l, c;
-       for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
+       for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++)
                out[l] = c;
        return l;
 }
 
                out[l] = c;
        return l;
 }
 
-float MSG_ReadCoord13i (void)
+float MSG_ReadCoord13i (sizebuf_t *sb)
 {
 {
-       return MSG_ReadLittleShort() * (1.0/8.0);
+       return MSG_ReadLittleShort(sb) * (1.0/8.0);
 }
 
 }
 
-float MSG_ReadCoord16i (void)
+float MSG_ReadCoord16i (sizebuf_t *sb)
 {
 {
-       return (signed short) MSG_ReadLittleShort();
+       return (signed short) MSG_ReadLittleShort(sb);
 }
 
 }
 
-float MSG_ReadCoord32f (void)
+float MSG_ReadCoord32f (sizebuf_t *sb)
 {
 {
-       return MSG_ReadLittleFloat();
+       return MSG_ReadLittleFloat(sb);
 }
 
 }
 
-float MSG_ReadCoord (protocolversion_t protocol)
+float MSG_ReadCoord (sizebuf_t *sb, protocolversion_t protocol)
 {
        if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
 {
        if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
-               return MSG_ReadCoord13i();
+               return MSG_ReadCoord13i(sb);
        else if (protocol == PROTOCOL_DARKPLACES1)
        else if (protocol == PROTOCOL_DARKPLACES1)
-               return MSG_ReadCoord32f();
+               return MSG_ReadCoord32f(sb);
        else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
        else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
-               return MSG_ReadCoord16i();
+               return MSG_ReadCoord16i(sb);
        else
        else
-               return MSG_ReadCoord32f();
+               return MSG_ReadCoord32f(sb);
 }
 
 }
 
-void MSG_ReadVector (float *v, protocolversion_t protocol)
+void MSG_ReadVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
 {
 {
-       v[0] = MSG_ReadCoord(protocol);
-       v[1] = MSG_ReadCoord(protocol);
-       v[2] = MSG_ReadCoord(protocol);
+       v[0] = MSG_ReadCoord(sb, protocol);
+       v[1] = MSG_ReadCoord(sb, protocol);
+       v[2] = MSG_ReadCoord(sb, protocol);
 }
 
 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
 }
 
 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
-float MSG_ReadAngle8i (void)
+float MSG_ReadAngle8i (sizebuf_t *sb)
 {
 {
-       return (signed char) MSG_ReadByte () * (360.0/256.0);
+       return (signed char) MSG_ReadByte (sb) * (360.0/256.0);
 }
 
 }
 
-float MSG_ReadAngle16i (void)
+float MSG_ReadAngle16i (sizebuf_t *sb)
 {
 {
-       return (signed short)MSG_ReadShort () * (360.0/65536.0);
+       return (signed short)MSG_ReadShort (sb) * (360.0/65536.0);
 }
 
 }
 
-float MSG_ReadAngle32f (void)
+float MSG_ReadAngle32f (sizebuf_t *sb)
 {
 {
-       return MSG_ReadFloat ();
+       return MSG_ReadFloat (sb);
 }
 
 }
 
-float MSG_ReadAngle (protocolversion_t protocol)
+float MSG_ReadAngle (sizebuf_t *sb, protocolversion_t protocol)
 {
        if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
 {
        if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
-               return MSG_ReadAngle8i ();
+               return MSG_ReadAngle8i (sb);
        else
        else
-               return MSG_ReadAngle16i ();
+               return MSG_ReadAngle16i (sb);
 }
 
 
 }
 
 
@@ -587,7 +607,7 @@ void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
 // attention, it has been eradicated from here, its only (former) use in
 // all of darkplaces.
 
 // attention, it has been eradicated from here, its only (former) use in
 // all of darkplaces.
 
-static char *hexchar = "0123456789ABCDEF";
+static const char *hexchar = "0123456789ABCDEF";
 void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
        int i, j, n;
 void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
        int i, j, n;
@@ -635,7 +655,7 @@ void Com_HexDumpToConsole(const unsigned char *data, int size)
                                        *cur++ = STRING_COLOR_TAG;
                                        *cur++ = STRING_COLOR_TAG;
                                }
                                        *cur++ = STRING_COLOR_TAG;
                                        *cur++ = STRING_COLOR_TAG;
                                }
-                               else if (d[j] >= ' ')
+                               else if (d[j] >= (unsigned char) ' ')
                                        *cur++ = d[j];
                                else
                                        *cur++ = '.';
                                        *cur++ = d[j];
                                else
                                        *cur++ = '.';
@@ -672,6 +692,9 @@ would be good for any more. At the beginning of the string, it will be called
 for the char 0 to initialize a clean state, and then once with the string " "
 (a space) so the routine knows how long a space is.
 
 for the char 0 to initialize a clean state, and then once with the string " "
 (a space) so the routine knows how long a space is.
 
+In case no single character fits into the given width, the wordWidth function
+must return the width of exactly one character.
+
 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
 
 The sum of the return values of the processLine function will be returned.
 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
 
 The sum of the return values of the processLine function will be returned.
@@ -713,7 +736,6 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                goto out;
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                goto out;
-                               break;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
@@ -741,7 +763,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                }
                                out_inner:
                                spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
                                }
                                out_inner:
                                spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
-                               if(wordLen < 1)
+                               if(wordLen < 1) // cannot happen according to current spec of wordWidth
                                {
                                        wordLen = 1;
                                        spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
                                {
                                        wordLen = 1;
                                        spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
@@ -967,7 +989,7 @@ COM_ParseToken_Simple
 Parse a token out of a string
 ==============
 */
 Parse a token out of a string
 ==============
 */
-int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
+int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
 {
        int len;
        int c;
 {
        int len;
        int c;
@@ -988,7 +1010,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1002,14 +1024,14 @@ skipwhite:
        if (data[0] == '\r' && data[1] == '\n')
                data++;
 
        if (data[0] == '\r' && data[1] == '\n')
                data++;
 
-       if (data[0] == '/' && data[1] == '/')
+       if (parsecomments && data[0] == '/' && data[1] == '/')
        {
                // comment
                while (*data && *data != '\n' && *data != '\r')
                        data++;
                goto skipwhite;
        }
        {
                // comment
                while (*data && *data != '\n' && *data != '\r')
                        data++;
                goto skipwhite;
        }
-       else if (data[0] == '/' && data[1] == '*')
+       else if (parsecomments && data[0] == '/' && data[1] == '*')
        {
                // comment
                data++;
        {
                // comment
                data++;
@@ -1064,7 +1086,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ';data++)
+               for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1101,7 +1123,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1178,7 +1200,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
+               for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1215,7 +1237,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1292,7 +1314,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
+               for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1324,7 +1346,7 @@ int COM_ParseToken_Console(const char **datapointer)
 
 // skip whitespace
 skipwhite:
 
 // skip whitespace
 skipwhite:
-       for (;*data <= ' ';data++)
+       for (;ISWHITESPACE(*data);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1360,7 +1382,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ';data++)
+               for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1398,115 +1420,148 @@ int COM_CheckParm (const char *parm)
 
 // Game mods
 
 
 // Game mods
 
+gamemode_t com_startupgamemode;
+gamemode_t com_startupgamegroup;
+
 typedef struct gamemode_info_s
 {
 typedef struct gamemode_info_s
 {
-       const char* prog_name;
-       const char* cmdline;
-       const char* gamename;
-       const char* gamedirname1;
-       const char* gamedirname2;
-       const char* gamescreenshotname;
-       const char* gameuserdirname;
+       gamemode_t mode; // this gamemode
+       gamemode_t group; // different games with same group can switch automatically when gamedirs change
+       const char* prog_name; // not null
+       const char* cmdline; // not null
+       const char* gamename; // not null
+       const char* gamedirname1; // not null
+       const char* gamedirname2; // null
+       const char* gamescreenshotname; // not nul
+       const char* gameuserdirname; // not null
 } gamemode_info_t;
 
 } gamemode_info_t;
 
-static const gamemode_info_t gamemode_info [] =
-{// prog_name          cmdline                 gamename                                gamedirname     gamescreenshotname
-
-// GAME_NORMAL
-// COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
-{ "",                          "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
-// GAME_HIPNOTIC
-// COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
-{ "hipnotic",          "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
-// GAME_ROGUE
-// COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
-{ "rogue",                     "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
-// GAME_NEHAHRA
-// COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
-{ "nehahra",           "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
-// GAME_NEXUIZ
-// COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
-{ "nexuiz",                    "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
-// GAME_TRANSFUSION
-// COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
-{ "transfusion",       "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
-// GAME_GOODVSBAD2
-// COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
-{ "gvb2",                      "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
-// GAME_TEU
-// COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
-{ "teu",                       "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
-// GAME_BATTLEMECH
-// COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
-{ "battlemech",                "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
-// GAME_ZYMOTIC
-// COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
-{ "zymotic",           "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
-// GAME_SETHERAL
-// COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
-{ "setheral",          "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
-// GAME_SOM
-// COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
-{ "som",                       "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
-// GAME_TENEBRAE
-// COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
-{ "tenebrae",          "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
-// GAME_NEOTERIC
-// COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
-{ "neoteric",          "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
-// GAME_OPENQUARTZ
-// COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
-{ "openquartz",                "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
-// GAME_PRYDON
-// COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
-{ "prydon",                    "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
-// GAME_DELUXEQUAKE
-// COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
-{ "dq",        "-dq",  "Deluxe Quake",         "basedq",               "extradq",              "basedq",               "dq" },
-// GAME_THEHUNTED
-// COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
-{ "thehunted",         "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
-// GAME_DEFEATINDETAIL2
-// COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
-{ "did2",                      "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
-// GAME_DARSANA
-// COMMANDLINEOPTION: Game: -darsana runs the game Darsana
-{ "darsana",           "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
-// GAME_CONTAGIONTHEORY
-// COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
-{ "contagiontheory",           "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
+static const gamemode_info_t gamemode_info [GAME_COUNT] =
+{// game                               basegame                                prog_name                       cmdline                         gamename                                basegame        modgame                 screenshot              userdir                            // commandline option
+{ GAME_NORMAL,                 GAME_NORMAL,                    "",                                     "-quake",                       "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
+{ GAME_HIPNOTIC,               GAME_NORMAL,                    "hipnotic",                     "-hipnotic",            "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
+{ GAME_ROGUE,                  GAME_NORMAL,                    "rogue",                        "-rogue",                       "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
+{ GAME_NEHAHRA,                        GAME_NORMAL,                    "nehahra",                      "-nehahra",                     "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
+{ GAME_NEXUIZ,                 GAME_NEXUIZ,                    "nexuiz",                       "-nexuiz",                      "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz"                        }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
+{ GAME_XONOTIC,                        GAME_XONOTIC,                   "xonotic",                      "-xonotic",                     "Xonotic",                              "data",         NULL,                   "xonotic",              "xonotic"                       }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
+{ GAME_TRANSFUSION,            GAME_TRANSFUSION,               "transfusion",          "-transfusion",         "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion"           }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
+{ GAME_GOODVSBAD2,             GAME_GOODVSBAD2,                "gvb2",                         "-goodvsbad2",          "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2"                          }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
+{ GAME_TEU,                            GAME_TEU,                               "teu",                          "-teu",                         "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu"                           }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
+{ GAME_BATTLEMECH,             GAME_BATTLEMECH,                "battlemech",           "-battlemech",          "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech"            }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
+{ GAME_ZYMOTIC,                        GAME_ZYMOTIC,                   "zymotic",                      "-zymotic",                     "Zymotic",                              "basezym",      NULL,                   "zymotic",              "zymotic"                       }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
+{ GAME_SETHERAL,               GAME_SETHERAL,                  "setheral",                     "-setheral",            "Setheral",                             "data",         NULL,                   "setheral",             "setheral"                      }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
+{ GAME_SOM,                            GAME_NORMAL,                    "sonofman",                     "-som",                         "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
+{ GAME_TENEBRAE,               GAME_NORMAL,                    "tenebrae",                     "-tenebrae",            "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces"            }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
+{ GAME_NEOTERIC,               GAME_NORMAL,                    "neoteric",                     "-neoteric",            "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces"            }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
+{ GAME_OPENQUARTZ,             GAME_NORMAL,                    "openquartz",           "-openquartz",          "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces"            }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
+{ GAME_PRYDON,                 GAME_NORMAL,                    "prydon",                       "-prydon",                      "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"            }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
+{ GAME_DELUXEQUAKE,            GAME_DELUXEQUAKE,               "dq",                           "-dq",                          "Deluxe Quake",                 "basedq",       "extradq",              "basedq",               "dq"                            }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
+{ GAME_THEHUNTED,              GAME_THEHUNTED,                 "thehunted",            "-thehunted",           "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"                     }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
+{ GAME_DEFEATINDETAIL2,        GAME_DEFEATINDETAIL2,   "did2",                         "-did2",                        "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2"                          }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
+{ GAME_DARSANA,                        GAME_DARSANA,                   "darsana",                      "-darsana",                     "Darsana",                              "ddata",        NULL,                   "darsana",              "darsana"                       }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
+{ GAME_CONTAGIONTHEORY,        GAME_CONTAGIONTHEORY,   "contagiontheory",      "-contagiontheory",     "Contagion Theory",             "ctdata",       NULL,                   "ct",                   "contagiontheory"       }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
+{ GAME_EDU2P,                  GAME_EDU2P,                             "edu2p",                        "-edu2p",                       "EDU2 Prototype",               "id1",          "edu2",                 "edu2_p",               "edu2prototype"         }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
+{ GAME_PROPHECY,               GAME_PROPHECY,                  "prophecy",             "-prophecy",            "Prophecy",                     "gamedata",             NULL,                   "phcy",         "prophecy"                      }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
+{ GAME_BLOODOMNICIDE,  GAME_BLOODOMNICIDE,             "omnicide",                     "-omnicide",            "Blood Omnicide",               "kain",         NULL,                   "omnicide",             "omnicide"                      }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
+{ GAME_STEELSTORM,             GAME_STEELSTORM,                "steelstorm",           "-steelstorm",          "Steel-Storm",                  "gamedata",     NULL,                   "ss",                   "steelstorm"            }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ GAME_STEELSTORM2,            GAME_STEELSTORM2,               "steelstorm2",          "-steelstorm2",         "Steel Storm 2",                        "gamedata",     NULL,                   "ss2",                  "steelstorm2"           }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ GAME_TOMESOFMEPHISTOPHELES,          GAME_TOMESOFMEPHISTOPHELES,             "tomesofmephistopheles",                "-tomesofmephistopheles",               "Tomes of Mephistopheles",                      "gamedata",     NULL,                   "tom",                  "tomesofmephistopheles"         }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ GAME_STRAPBOMB,              GAME_STRAPBOMB,                 "strapbomb",            "-strapbomb",           "Strap-on-bomb Car",    "id1",          NULL,                   "strap",                "strapbomb"                     }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
+{ GAME_MOONHELM,               GAME_MOONHELM,                  "moonhelm",                     "-moonhelm",            "MoonHelm",                             "data",         NULL,                   "mh",                   "moonhelm"                      }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
 };
 
 };
 
+static void COM_SetGameType(int index);
 void COM_InitGameType (void)
 {
        char name [MAX_OSPATH];
 void COM_InitGameType (void)
 {
        char name [MAX_OSPATH];
-       unsigned int i;
+       int i;
+       int index = 0;
+
+       // check executable filename for keywords, but do it SMARTLY - only check the last path element
+       FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
+       COM_ToLowerString(name, name, sizeof (name));
+       for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+               if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
+                       index = i;
 
 
-       FS_StripExtension (com_argv[0], name, sizeof (name));
-       COM_ToLowerString (name, name, sizeof (name));
+       // check commandline options for keywords
+       for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+               if (COM_CheckParm (gamemode_info[i].cmdline))
+                       index = i;
 
 
-       // Check the binary name; default to GAME_NORMAL (0)
-       gamemode = GAME_NORMAL;
-       for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
-               if (strstr (name, gamemode_info[i].prog_name))
+       com_startupgamemode = gamemode_info[index].mode;
+       com_startupgamegroup = gamemode_info[index].group;
+       COM_SetGameType(index);
+}
+
+void COM_ChangeGameTypeForGameDirs(void)
+{
+       int i;
+       int index = -1;
+       // this will not not change the gamegroup
+       // first check if a base game (single gamedir) matches
+       for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+       {
+               if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
                {
                {
-                       gamemode = (gamemode_t)i;
+                       index = i;
                        break;
                }
                        break;
                }
-
-       // Look for a command-line option
-       for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
-               if (COM_CheckParm (gamemode_info[i].cmdline))
+       }
+       // now that we have a base game, see if there is a matching derivative game (two gamedirs)
+       if (fs_numgamedirs)
+       {
+               for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
                {
                {
-                       gamemode = (gamemode_t)i;
-                       break;
+                       if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
+                       {
+                               index = i;
+                               break;
+                       }
                }
                }
+       }
+       // we now have a good guess at which game this is meant to be...
+       if (index >= 0 && gamemode != gamemode_info[index].mode)
+               COM_SetGameType(index);
+}
 
 
-       gamename = gamemode_info[gamemode].gamename;
-       gamedirname1 = gamemode_info[gamemode].gamedirname1;
-       gamedirname2 = gamemode_info[gamemode].gamedirname2;
-       gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
-       gameuserdirname = gamemode_info[gamemode].gameuserdirname;
+static void COM_SetGameType(int index)
+{
+       int i, t;
+       if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
+               index = 0;
+       gamemode = gamemode_info[index].mode;
+       gamename = gamemode_info[index].gamename;
+       gamedirname1 = gamemode_info[index].gamedirname1;
+       gamedirname2 = gamemode_info[index].gamedirname2;
+       gamescreenshotname = gamemode_info[index].gamescreenshotname;
+       gameuserdirname = gamemode_info[index].gameuserdirname;
+
+       if (gamemode == com_startupgamemode)
+       {
+               if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
+                       gamename = com_argv[t+1];
+               if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
+                       gamedirname1 = com_argv[t+1];
+               if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
+                       gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
+               if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
+                       gamescreenshotname = com_argv[t+1];
+               if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
+                       gameuserdirname = com_argv[t+1];
+       }
+
+       if (gamedirname2 && gamedirname2[0])
+               Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
+       else
+               Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
+       for (i = 0;i < fs_numgamedirs;i++)
+       {
+               if (i == 0)
+                       Con_Printf(", with mod gamedirs");
+               Con_Printf(" %s", fs_gamedirs[i]);
+       }
+       Con_Printf("\n");
 }
 
 
 }
 
 
@@ -1554,25 +1609,18 @@ void COM_Init_Commands (void)
 ============
 va
 
 ============
 va
 
-does a varargs printf into a temp buffer, so I don't need to have
-varargs versions of all text functions.
-FIXME: make this buffer size safe someday
+varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
 ============
 */
 ============
 */
-char *va(const char *format, ...)
+char *va(char *buf, size_t buflen, const char *format, ...)
 {
        va_list argptr;
 {
        va_list argptr;
-       // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
-       static char string[8][1024], *s;
-       static int stringindex = 0;
 
 
-       s = string[stringindex];
-       stringindex = (stringindex + 1) & 7;
        va_start (argptr, format);
        va_start (argptr, format);
-       dpvsnprintf (s, sizeof (string[0]), format,argptr);
+       dpvsnprintf (buf, buflen, format,argptr);
        va_end (argptr);
 
        va_end (argptr);
 
-       return s;
+       return buf;
 }
 
 
 }
 
 
@@ -1606,7 +1654,11 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
 {
        int result;
 
 {
        int result;
 
+#if _MSC_VER >= 1400
+       result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
+#else
        result = vsnprintf (buffer, buffersize, format, args);
        result = vsnprintf (buffer, buffersize, format, args);
+#endif
        if (result < 0 || (size_t)result >= buffersize)
        {
                buffer[buffersize - 1] = '\0';
        if (result < 0 || (size_t)result >= buffersize)
        {
                buffer[buffersize - 1] = '\0';
@@ -1624,6 +1676,23 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
        if (size_out == 0)
                return;
 
        if (size_out == 0)
                return;
 
+       if(utf8_enable.integer)
+       {
+               *out = 0;
+               while(*in && size_out > 1)
+               {
+                       int n;
+                       Uchar ch = u8_getchar_utf8_enabled(in, &in);
+                       ch = u8_tolower(ch);
+                       n = u8_fromchar(ch, out, size_out);
+                       if(n <= 0)
+                               break;
+                       out += n;
+                       size_out -= n;
+               }
+               return;
+       }
+
        while (*in && size_out > 1)
        {
                if (*in >= 'A' && *in <= 'Z')
        while (*in && size_out > 1)
        {
                if (*in >= 'A' && *in <= 'Z')
@@ -1640,6 +1709,23 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
        if (size_out == 0)
                return;
 
        if (size_out == 0)
                return;
 
+       if(utf8_enable.integer)
+       {
+               *out = 0;
+               while(*in && size_out > 1)
+               {
+                       int n;
+                       Uchar ch = u8_getchar_utf8_enabled(in, &in);
+                       ch = u8_toupper(ch);
+                       n = u8_fromchar(ch, out, size_out);
+                       if(n <= 0)
+                               break;
+                       out += n;
+                       size_out -= n;
+               }
+               return;
+       }
+
        while (*in && size_out > 1)
        {
                if (*in >= 'a' && *in <= 'z')
        while (*in && size_out > 1)
        {
                if (*in >= 'a' && *in <= 'z')
@@ -1672,7 +1758,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                commentprefixlength = (int)strlen(commentprefix);
        while (*l && *l != '\n' && *l != '\r')
        {
                commentprefixlength = (int)strlen(commentprefix);
        while (*l && *l != '\n' && *l != '\r')
        {
-               if (*l > ' ')
+               if (!ISWHITESPACE(*l))
                {
                        if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
                        {
                {
                        if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
                        {
@@ -1697,7 +1783,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                        }
                        else
                        {
                        }
                        else
                        {
-                               while (*l > ' ')
+                               while (!ISWHITESPACE(*l))
                                {
                                        if (tokenbuf >= tokenbufend)
                                                return -1;
                                {
                                        if (tokenbuf >= tokenbufend)
                                                return -1;
@@ -1756,6 +1842,17 @@ COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
                                ++s;
                                switch((s == end) ? 0 : *s)
                                {
                                ++s;
                                switch((s == end) ? 0 : *s)
                                {
+                                       case STRING_COLOR_RGB_TAG_CHAR:
+                                               if (s+1 != end && isxdigit(s[1]) &&
+                                                       s+2 != end && isxdigit(s[2]) &&
+                                                       s+3 != end && isxdigit(s[3]) )
+                                               {
+                                                       s+=3;
+                                                       break;
+                                               }
+                                               ++len; // STRING_COLOR_TAG
+                                               ++len; // STRING_COLOR_RGB_TAG_CHAR
+                                               break;
                                        case 0: // ends with unfinished color code!
                                                ++len;
                                                if(valid)
                                        case 0: // ends with unfinished color code!
                                                ++len;
                                                if(valid)
@@ -1794,7 +1891,7 @@ for example).
 
 If the output buffer size did not suffice for converting, the function returns
 FALSE. Generally, if escape_carets is false, the output buffer needs
 
 If the output buffer size did not suffice for converting, the function returns
 FALSE. Generally, if escape_carets is false, the output buffer needs
-strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
+strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
 bytes. In any case, the function makes sure that the resulting string is
 zero terminated.
 
 bytes. In any case, the function makes sure that the resulting string is
 zero terminated.
 
@@ -1820,6 +1917,19 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                ++in;
                                switch((in == end) ? 0 : *in)
                                {
                                ++in;
                                switch((in == end) ? 0 : *in)
                                {
+                                       case STRING_COLOR_RGB_TAG_CHAR:
+                                               if (in+1 != end && isxdigit(in[1]) &&
+                                                       in+2 != end && isxdigit(in[2]) &&
+                                                       in+3 != end && isxdigit(in[3]) )
+                                               {
+                                                       in+=3;
+                                                       break;
+                                               }
+                                               APPEND(STRING_COLOR_TAG);
+                                               if(escape_carets)
+                                                       APPEND(STRING_COLOR_TAG);
+                                               APPEND(STRING_COLOR_RGB_TAG_CHAR);
+                                               break;
                                        case 0: // ends with unfinished color code!
                                                APPEND(STRING_COLOR_TAG);
                                                // finish the code by appending another caret when escaping
                                        case 0: // ends with unfinished color code!
                                                APPEND(STRING_COLOR_TAG);
                                                // finish the code by appending another caret when escaping
@@ -1852,97 +1962,33 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
 #undef APPEND
 }
 
 #undef APPEND
 }
 
-// written by Elric, thanks Elric!
-char *SearchInfostring(const char *infostring, const char *key)
-{
-       static char value [MAX_INPUTLINE];
-       char crt_key [MAX_INPUTLINE];
-       size_t value_ind, key_ind;
-       char c;
-
-       if (*infostring++ != '\\')
-               return NULL;
-
-       value_ind = 0;
-       for (;;)
-       {
-               key_ind = 0;
-
-               // Get the key name
-               for (;;)
-               {
-                       c = *infostring++;
-
-                       if (c == '\0')
-                               return NULL;
-                       if (c == '\\' || key_ind == sizeof (crt_key) - 1)
-                       {
-                               crt_key[key_ind] = '\0';
-                               break;
-                       }
-
-                       crt_key[key_ind++] = c;
-               }
-
-               // If it's the key we are looking for, save it in "value"
-               if (!strcmp(crt_key, key))
-               {
-                       for (;;)
-                       {
-                               c = *infostring++;
-
-                               if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
-                               {
-                                       value[value_ind] = '\0';
-                                       return value;
-                               }
-
-                               value[value_ind++] = c;
-                       }
-               }
-
-               // Else, skip the value
-               for (;;)
-               {
-                       c = *infostring++;
-
-                       if (c == '\0')
-                               return NULL;
-                       if (c == '\\')
-                               break;
-               }
-       }
-}
-
-void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
+char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
 {
        int pos = 0, j;
        size_t keylength;
        if (!key)
                key = "";
 {
        int pos = 0, j;
        size_t keylength;
        if (!key)
                key = "";
-       if (!value)
-               value = "";
        keylength = strlen(key);
        if (valuelength < 1 || !value)
        {
                Con_Printf("InfoString_GetValue: no room in value\n");
        keylength = strlen(key);
        if (valuelength < 1 || !value)
        {
                Con_Printf("InfoString_GetValue: no room in value\n");
-               return;
+               return NULL;
        }
        value[0] = 0;
        if (strchr(key, '\\'))
        {
                Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
        }
        value[0] = 0;
        if (strchr(key, '\\'))
        {
                Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
-               return;
+               return NULL;
        }
        if (strchr(key, '\"'))
        {
                Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
        }
        if (strchr(key, '\"'))
        {
                Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
-               return;
+               return NULL;
        }
        if (!key[0])
        {
                Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
        }
        if (!key[0])
        {
                Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
-               return;
+               return NULL;
        }
        while (buffer[pos] == '\\')
        {
        }
        while (buffer[pos] == '\\')
        {
@@ -1953,12 +1999,13 @@ void InfoString_GetValue(const char *buffer, const char *key, char *value, size_
                        for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
                                value[j] = buffer[pos+j];
                        value[j] = 0;
                        for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
                                value[j] = buffer[pos+j];
                        value[j] = 0;
-                       return;
+                       return value;
                }
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
        }
        // if we reach this point the key was not found
                }
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
        }
        // if we reach this point the key was not found
+       return NULL;
 }
 
 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
 }
 
 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
@@ -2007,9 +2054,9 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
        if (value && value[0])
        {
                // set the key/value and append the remaining text
        if (value && value[0])
        {
                // set the key/value and append the remaining text
-               char tempbuffer[4096];
+               char tempbuffer[MAX_INPUTLINE];
                strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
                strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
-               sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
+               dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
        }
        else
        {
        }
        else
        {
@@ -2021,8 +2068,8 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
 void InfoString_Print(char *buffer)
 {
        int i;
 void InfoString_Print(char *buffer)
 {
        int i;
-       char key[2048];
-       char value[2048];
+       char key[MAX_INPUTLINE];
+       char value[MAX_INPUTLINE];
        while (*buffer)
        {
                if (*buffer != '\\')
        while (*buffer)
        {
                if (*buffer != '\\')
@@ -2130,3 +2177,84 @@ strlcpy(char *dst, const char *src, size_t siz)
 }
 
 #endif  // #ifndef HAVE_STRLCPY
 }
 
 #endif  // #ifndef HAVE_STRLCPY
+
+void FindFraction(double val, int *num, int *denom, int denomMax)
+{
+       int i;
+       double bestdiff;
+       // initialize
+       bestdiff = fabs(val);
+       *num = 0;
+       *denom = 1;
+
+       for(i = 1; i <= denomMax; ++i)
+       {
+               int inum = (int) floor(0.5 + val * i);
+               double diff = fabs(val - inum / (double)i);
+               if(diff < bestdiff)
+               {
+                       bestdiff = diff;
+                       *num = inum;
+                       *denom = i;
+               }
+       }
+}
+
+// decodes an XPM from C syntax
+char **XPM_DecodeString(const char *in)
+{
+       static char *tokens[257];
+       static char lines[257][512];
+       size_t line = 0;
+
+       // skip until "{" token
+       while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
+
+       // now, read in succession: string, comma-or-}
+       while(COM_ParseToken_QuakeC(&in, false))
+       {
+               tokens[line] = lines[line];
+               strlcpy(lines[line++], com_token, sizeof(lines[0]));
+               if(!COM_ParseToken_QuakeC(&in, false))
+                       return NULL;
+               if(!strcmp(com_token, "}"))
+                       break;
+               if(strcmp(com_token, ","))
+                       return NULL;
+               if(line >= sizeof(tokens) / sizeof(tokens[0]))
+                       return NULL;
+       }
+
+       return tokens;
+}
+
+static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
+{
+       unsigned char i0 = (bytes > 0) ? in[0] : 0;
+       unsigned char i1 = (bytes > 1) ? in[1] : 0;
+       unsigned char i2 = (bytes > 2) ? in[2] : 0;
+       unsigned char o0 = base64[i0 >> 2];
+       unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
+       unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
+       unsigned char o3 = base64[i2 & 077];
+       out[0] = (bytes > 0) ? o0 : '?';
+       out[1] = (bytes > 0) ? o1 : '?';
+       out[2] = (bytes > 1) ? o2 : '=';
+       out[3] = (bytes > 2) ? o3 : '=';
+}
+
+size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
+{
+       size_t blocks, i;
+       // expand the out-buffer
+       blocks = (buflen + 2) / 3;
+       if(blocks*4 > outbuflen)
+               return 0;
+       for(i = blocks; i > 0; )
+       {
+               --i;
+               base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
+       }
+       return blocks * 4;
+}