]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
Reworked v_isometric code significantly, it now defaults to a proper isometric view...
[xonotic/darkplaces.git] / common.c
index 11a350bfc2dba30daa9d4614657f0e9d873e7fb4..c01e931d798e2a87dbd502e27fec90bfb458fedc 100644 (file)
--- a/common.c
+++ b/common.c
@@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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"};
@@ -37,6 +38,7 @@ int com_selffd = -1;
 
 gamemode_t gamemode;
 const char *gamename;
+const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the 
 const char *gamedirname1;
 const char *gamedirname2;
 const char *gamescreenshotname;
@@ -363,7 +365,7 @@ void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
                MSG_WriteCoord32f (sb, f);
 }
 
-void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
+void MSG_WriteVector (sizebuf_t *sb, const vec3_t v, protocolversion_t protocol)
 {
        MSG_WriteCoord (sb, v[0], protocol);
        MSG_WriteCoord (sb, v[1], protocol);
@@ -403,167 +405,175 @@ void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
 //
 // reading functions
 //
-int msg_readcount;
-qboolean msg_badread;
 
-void MSG_BeginReading (void)
+void MSG_InitReadBuffer (sizebuf_t *buf, unsigned char *data, int size)
 {
-       msg_readcount = 0;
-       msg_badread = false;
+       memset(buf, 0, sizeof(*buf));
+       buf->data = data;
+       buf->maxsize = buf->cursize = size;
+       MSG_BeginReading(buf);
 }
 
-int MSG_ReadLittleShort (void)
+void MSG_BeginReading(sizebuf_t *sb)
 {
-       if (msg_readcount+2 > net_message.cursize)
+       sb->readcount = 0;
+       sb->badread = false;
+}
+
+int MSG_ReadLittleShort(sizebuf_t *sb)
+{
+       if (sb->readcount+2 > sb->cursize)
        {
-               msg_badread = true;
+               sb->badread = true;
                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;
        }
-       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;
        }
-       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;
        }
-       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;
-       if (msg_readcount+4 > net_message.cursize)
+       if (sb->readcount+4 > sb->cursize)
        {
-               msg_badread = true;
+               sb->badread = true;
                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;
 }
 
-float MSG_ReadBigFloat (void)
+float MSG_ReadBigFloat (sizebuf_t *sb)
 {
        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;
        }
-       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;
 }
 
-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;
 }
 
-int MSG_ReadBytes (int numbytes, unsigned char *out)
+int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
 {
        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;
 }
 
-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)
-               return MSG_ReadCoord13i();
+               return MSG_ReadCoord13i(sb);
        else if (protocol == PROTOCOL_DARKPLACES1)
-               return MSG_ReadCoord32f();
+               return MSG_ReadCoord32f(sb);
        else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
-               return MSG_ReadCoord16i();
+               return MSG_ReadCoord16i(sb);
        else
-               return MSG_ReadCoord32f();
+               return MSG_ReadCoord32f(sb);
 }
 
-void MSG_ReadVector (float *v, protocolversion_t protocol)
+void MSG_ReadVector (sizebuf_t *sb, vec3_t 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
-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)
-               return MSG_ReadAngle8i ();
+               return MSG_ReadAngle8i (sb);
        else
-               return MSG_ReadAngle16i ();
+               return MSG_ReadAngle16i (sb);
 }
 
 
@@ -733,7 +743,6 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                {
                        case 0: // end of string
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
-                               isContinuation = false;
                                goto out;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
@@ -988,7 +997,7 @@ COM_ParseToken_Simple
 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;
@@ -1023,14 +1032,14 @@ skipwhite:
        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;
        }
-       else if (data[0] == '/' && data[1] == '*')
+       else if (parsecomments && data[0] == '/' && data[1] == '*')
        {
                // comment
                data++;
@@ -1419,11 +1428,17 @@ int COM_CheckParm (const char *parm)
 
 // Game mods
 
+gamemode_t com_startupgamemode;
+gamemode_t com_startupgamegroup;
+
 typedef struct gamemode_info_s
 {
+       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*     gamenetworkfiltername; // not null
        const char* gamedirname1; // not null
        const char* gamedirname2; // null
        const char* gamescreenshotname; // not nul
@@ -1431,136 +1446,156 @@ typedef struct gamemode_info_s
 } gamemode_info_t;
 
 static const gamemode_info_t gamemode_info [GAME_COUNT] =
-{// prog_name          cmdline                 gamename                                basegame        modgame                 screenshotprefix        userdir
-
-// 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_XONOTIC
-// COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
-{ "xonotic",                   "-xonotic",             "Xonotic",                              "data",         NULL,                   "xonotic",              "xonotic" },
-// 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" },
-// GAME_EDU2P
-// COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
-{ "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
-// GAME_PROPHECY
-// COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
-{ "prophecy",                          "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
-// GAME_BLOODOMNICIDE
-// COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
-{ "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
-// GAME_STEELSTORM
-// COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
-{ "steelstorm",                                "-steelstorm",          "Steel-Storm",          "gamedata",             NULL,                   "ss",                   "steelstorm" },
-// GAME_STRAPBOMB
-// COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
-{ "strapbomb",                         "-strapbomb",           "Strap-on-bomb Car",            "id1",          NULL,                   "strap",                        "strapbomb" },
-// GAME_MOONHELM
-// COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
-{ "moonhelm",                          "-moonhelm",            "MoonHelm",             "data",         NULL,                   "mh",                   "moonhelm" },
+{// game                                               basegame                                        prog_name                               cmdline                                         gamename                                        gamenetworkfilername            basegame        modgame                 screenshot                      userdir                                    // commandline option
+{ GAME_NORMAL,                                 GAME_NORMAL,                            "",                                             "-quake",                                       "DarkPlaces-Quake",                     "DarkPlaces-Quake",                     "id1",          NULL,                   "dp",                           "darkplaces"                    }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
+{ GAME_HIPNOTIC,                               GAME_NORMAL,                            "hipnotic",                             "-hipnotic",                            "Darkplaces-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",                     "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",           "DarkPlaces-Nehahra",           "id1",          "nehahra",              "dp",                           "darkplaces"                    }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
+{ GAME_QUOTH,                                  GAME_NORMAL,                            "quoth",                                "-quoth",                                       "Darkplaces-Quoth",                     "Darkplaces-Quoth",                     "id1",          "quoth",                "dp",                           "darkplaces"                    }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it
+{ GAME_NEXUIZ,                                 GAME_NEXUIZ,                            "nexuiz",                               "-nexuiz",                                      "Nexuiz",                                       "Nexuiz",                                       "data",         NULL,                   "nexuiz",                       "nexuiz"                                }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
+{ GAME_XONOTIC,                                        GAME_XONOTIC,                           "xonotic",                              "-xonotic",                                     "Xonotic",                                      "Xonotic",                                      "data",         NULL,                   "xonotic",                      "xonotic"                               }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
+{ GAME_TRANSFUSION,                            GAME_TRANSFUSION,                       "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",                          "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",                     "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",                           "Battlemech",                           "base",         NULL,                   "battlemech",           "battlemech"                    }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
+{ GAME_ZYMOTIC,                                        GAME_ZYMOTIC,                           "zymotic",                              "-zymotic",                                     "Zymotic",                                      "Zymotic",                                      "basezym",      NULL,                   "zymotic",                      "zymotic"                               }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
+{ GAME_SETHERAL,                               GAME_SETHERAL,                          "setheral",                             "-setheral",                            "Setheral",                                     "Setheral",                                     "data",         NULL,                   "setheral",                     "setheral"                              }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
+{ GAME_TENEBRAE,                               GAME_NORMAL,                            "tenebrae",                             "-tenebrae",                            "DarkPlaces-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",                                     "Neoteric",                                     "id1",          "neobase",              "neo",                          "darkplaces"                    }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
+{ GAME_OPENQUARTZ,                             GAME_NORMAL,                            "openquartz",                   "-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",                           "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",                         "Deluxe_Quake",                         "basedq",       "extradq",              "basedq",                       "dq"                                    }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
+{ GAME_THEHUNTED,                              GAME_THEHUNTED,                         "thehunted",                    "-thehunted",                           "The Hunted",                           "The_Hunted",                           "thdata",       NULL,                   "th",                           "thehunted"                             }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
+{ GAME_DEFEATINDETAIL2,                        GAME_DEFEATINDETAIL2,           "did2",                                 "-did2",                                        "Defeat In Detail 2",           "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",                                      "Darsana",                                      "ddata",        NULL,                   "darsana",                      "darsana"                               }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
+{ GAME_CONTAGIONTHEORY,                        GAME_CONTAGIONTHEORY,           "contagiontheory",              "-contagiontheory",                     "Contagion Theory",                     "Contagion_Theory",                     "ctdata",       NULL,                   "ct",                           "contagiontheory"               }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
+{ GAME_EDU2P,                                  GAME_EDU2P,                                     "edu2p",                                "-edu2p",                                       "EDU2 Prototype",                       "EDU2_Prototype",                       "id1",          "edu2",                 "edu2_p",                       "edu2prototype"                 }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
+{ GAME_PROPHECY,                               GAME_PROPHECY,                          "prophecy",                             "-prophecy",                            "Prophecy",                                     "Prophecy",                                     "gamedata",     NULL,                   "phcy",                         "prophecy"                              }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
+{ GAME_BLOODOMNICIDE,                  GAME_BLOODOMNICIDE,                     "omnicide",                             "-omnicide",                            "Blood Omnicide",                       "Blood_Omnicide",                       "kain",         NULL,                   "omnicide",                     "omnicide"                              }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
+{ GAME_STEELSTORM,                             GAME_STEELSTORM,                        "steelstorm",                   "-steelstorm",                          "Steel-Storm",                          "Steel-Storm",                          "gamedata",     NULL,                   "ss",                           "steelstorm"                    }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ GAME_STEELSTORM2,                            GAME_STEELSTORM2,                       "steelstorm2",                  "-steelstorm2",                         "Steel Storm 2",                        "Steel_Storm_2",                        "gamedata",     NULL,                   "ss2",                          "steelstorm2"                   }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2
+{ GAME_SSAMMO,                                 GAME_SSAMMO,                            "steelstorm-ammo",              "-steelstormammo",                      "Steel Storm A.M.M.O.",         "Steel_Storm_A.M.M.O.",         "gamedata", NULL,                       "ssammo",                       "steelstorm-ammo"               }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O.
+{ GAME_STEELSTORMREVENANTS,            GAME_STEELSTORMREVENANTS,       "steelstorm-revenants", "-steelstormrev",                       "Steel Storm: Revenants",       "Steel_Storm_Revenants",        "base", NULL,                           "ssrev",                        "steelstorm-revenants"  }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
+{ GAME_TOMESOFMEPHISTOPHELES,  GAME_TOMESOFMEPHISTOPHELES,     "tomesofmephistopheles","-tomesofmephistopheles",       "Tomes of Mephistopheles",      "Tomes_of_Mephistopheles",      "gamedata",     NULL,                   "tom",                          "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles
+{ GAME_STRAPBOMB,                              GAME_STRAPBOMB,                         "strapbomb",                    "-strapbomb",                           "Strap-on-bomb Car",            "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",                                     "MoonHelm",                                     "data",         NULL,                   "mh",                           "moonhelm"                              }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
+{ GAME_VORETOURNAMENT,                 GAME_VORETOURNAMENT,            "voretournament",               "-voretournament",                      "Vore Tournament",                      "Vore_Tournament",                      "data",         NULL,                   "voretournament",       "voretournament"                }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
 };
 
+static void COM_SetGameType(int index);
 void COM_InitGameType (void)
 {
        char name [MAX_OSPATH];
-       unsigned int i;
-       int t;
+       int i;
+       int index = 0;
+
+#ifdef FORCEGAME
+       COM_ToLowerString(FORCEGAME, name, sizeof (name));
+#else
+       // 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));
+#endif
+       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;
+
+       // 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;
 
-       FS_StripExtension (com_argv[0], name, sizeof (name));
-       COM_ToLowerString (name, name, sizeof (name));
+       com_startupgamemode = gamemode_info[index].mode;
+       com_startupgamegroup = gamemode_info[index].group;
+       COM_SetGameType(index);
+}
 
-       // 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))
+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;
                }
-
-       // 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);
+}
+
+static void COM_SetGameType(int index)
+{
+       static char gamenetworkfilternamebuffer[64];
+       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;
+       gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
+       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 = gamenetworkfiltername = com_argv[t+1];
+               if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < com_argc)
+                       gamenetworkfiltername = 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");
 
-       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;
+       if (strchr(gamenetworkfiltername, ' '))
+       {
+               char *s;
+               // if there are spaces in the game's network filter name it would
+               // cause parse errors in getservers in dpmaster, so we need to replace
+               // them with _ characters
+               strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
+               while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
+                       *s = '_';
+               gamenetworkfiltername = gamenetworkfilternamebuffer;
+       }
 
-       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];
+       Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
 }
 
 
@@ -1585,13 +1620,22 @@ void COM_Init_Commands (void)
                if (strstr(com_argv[j], " "))
                {
                        // arg contains whitespace, store quotes around it
+                       // This condition checks whether we can allow to put
+                       // in two quote characters.
+                       if (n >= ((int)sizeof(com_cmdline) - 2))
+                               break;
                        com_cmdline[n++] = '\"';
-                       while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
+                       // This condition checks whether we can allow one
+                       // more character and a quote character.
+                       while ((n < ((int)sizeof(com_cmdline) - 2)) && com_argv[j][i])
+                               // FIXME: Doesn't quote special characters.
                                com_cmdline[n++] = com_argv[j][i++];
                        com_cmdline[n++] = '\"';
                }
                else
                {
+                       // This condition checks whether we can allow one
+                       // more character.
                        while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
                                com_cmdline[n++] = com_argv[j][i++];
                }
@@ -1608,25 +1652,18 @@ void COM_Init_Commands (void)
 ============
 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;
-       // 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);
-       dpvsnprintf (s, sizeof (string[0]), format,argptr);
+       dpvsnprintf (buf, buflen, format,argptr);
        va_end (argptr);
 
-       return s;
+       return buf;
 }
 
 
@@ -1682,6 +1719,23 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
        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')
@@ -1698,6 +1752,23 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
        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')
@@ -1934,69 +2005,7 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
 #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;
@@ -2006,23 +2015,23 @@ void InfoString_GetValue(const char *buffer, const char *key, char *value, size_
        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);
-               return;
+               return NULL;
        }
        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");
-               return;
+               return NULL;
        }
        while (buffer[pos] == '\\')
        {
@@ -2033,12 +2042,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;
-                       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
+       return NULL;
 }
 
 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
@@ -2084,10 +2094,10 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
                Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
                return;
        }
-       if (value && value[0])
+       if (value[0])
        {
                // set the key/value and append the remaining text
-               char tempbuffer[4096];
+               char tempbuffer[MAX_INPUTLINE];
                strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
                dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
        }
@@ -2101,8 +2111,8 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
 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 != '\\')
@@ -2287,7 +2297,7 @@ size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
        for(i = blocks; i > 0; )
        {
                --i;
-               base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
+               base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));
        }
        return blocks * 4;
 }