]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
undo an accidental r_test commit in vid_sdl.c
[xonotic/darkplaces.git] / common.c
index e0cffe48120f4790c34348817b8f509a3fd87321..8e9bf40a40f8273e4476faabdc0fd802c4ffa514 100644 (file)
--- a/common.c
+++ b/common.c
@@ -19,20 +19,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // 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 "quakedef.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;
+int com_selffd = -1;
 
 gamemode_t gamemode;
 const char *gamename;
@@ -51,68 +52,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
        {
-               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;
+}
 
 /*
 ============================================================================
@@ -595,7 +606,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.
 
-static char *hexchar = "0123456789ABCDEF";
+static const char *hexchar = "0123456789ABCDEF";
 void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
        int i, j, n;
@@ -643,7 +654,7 @@ void Com_HexDumpToConsole(const unsigned char *data, int size)
                                        *cur++ = STRING_COLOR_TAG;
                                        *cur++ = STRING_COLOR_TAG;
                                }
-                               else if (d[j] >= ' ')
+                               else if (d[j] >= (unsigned char) ' ')
                                        *cur++ = d[j];
                                else
                                        *cur++ = '.';
@@ -680,6 +691,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.
 
+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.
@@ -721,7 +735,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;
-                               break;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
@@ -749,7 +762,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
-                               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
@@ -996,7 +1009,7 @@ skipwhite:
        // 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)
                {
@@ -1072,7 +1085,7 @@ skipwhite:
        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;
@@ -1109,7 +1122,7 @@ skipwhite:
        // 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)
                {
@@ -1186,7 +1199,7 @@ skipwhite:
        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;
@@ -1223,7 +1236,7 @@ skipwhite:
        // 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)
                {
@@ -1300,7 +1313,7 @@ skipwhite:
        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;
@@ -1332,7 +1345,7 @@ int COM_ParseToken_Console(const char **datapointer)
 
 // skip whitespace
 skipwhite:
-       for (;*data <= ' ';data++)
+       for (;ISWHITESPACE(*data);data++)
        {
                if (*data == 0)
                {
@@ -1368,7 +1381,7 @@ skipwhite:
        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;
@@ -1406,124 +1419,153 @@ int COM_CheckParm (const char *parm)
 
 // Game mods
 
+gamemode_t com_startupgamemode;
+gamemode_t com_startupgamegroup;
+
 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;
 
 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_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_BLADEMASTER
-// COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
-{ "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
-// GAME_PROPHECY
-// COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
-{ "prophecy",                          "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
+{// 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,                    "som",                          "-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",                             "data",         NULL,                   "prophecy",             "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_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];
-       unsigned int i;
+       int i;
+       int index = 0;
 
        FS_StripExtension (com_argv[0], name, sizeof (name));
        COM_ToLowerString (name, name, sizeof (name));
 
-       // 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))
+       // check executable filename for keywords
+       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))
                {
-                       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++)
+       // 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))
                {
-                       gamemode = (gamemode_t)i;
+                       index = i;
                        break;
                }
 
-       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;
+       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]))
+               {
+                       index = i;
+                       break;
+               }
+       }
+       // 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++)
+               {
+                       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)
+{
+       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");
 }
 
 
@@ -1693,7 +1735,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                commentprefixlength = (int)strlen(commentprefix);
        while (*l && *l != '\n' && *l != '\r')
        {
-               if (*l > ' ')
+               if (!ISWHITESPACE(*l))
                {
                        if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
                        {
@@ -1718,7 +1760,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                        }
                        else
                        {
-                               while (*l > ' ')
+                               while (!ISWHITESPACE(*l))
                                {
                                        if (tokenbuf >= tokenbufend)
                                                return -1;
@@ -1777,7 +1819,7 @@ COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
                                ++s;
                                switch((s == end) ? 0 : *s)
                                {
-                                       case STRING_COLOR_RGB_DEFAULT:
+                                       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]) )
@@ -1786,17 +1828,8 @@ COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
                                                        break;
                                                }
                                                ++len; // STRING_COLOR_TAG
-                                               ++len; // STRING_COLOR_RGB_DEFAULT
+                                               ++len; // STRING_COLOR_RGB_TAG_CHAR
                                                break;
-                                       /*case 'a':
-                                               if ( s+1 != end && ( isxdigit(s[1]) || (s[1] == '+' || s[1] == '-') ) )
-                                               {
-                                                       s++;
-                                                       break;
-                                               }
-                                               ++len; // STRING_COLOR_TAG
-                                               ++len; // STRING_COLOR_RGB_DEFAULT
-                                               break;*/
                                        case 0: // ends with unfinished color code!
                                                ++len;
                                                if(valid)
@@ -1835,7 +1868,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
-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.
 
@@ -1861,7 +1894,7 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                ++in;
                                switch((in == end) ? 0 : *in)
                                {
-                                       case STRING_COLOR_RGB_DEFAULT:
+                                       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]) )
@@ -1869,12 +1902,11 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                                        in+=3;
                                                        break;
                                                }
-                                       /*case 'a':
-                                               if ( in+1 != end && ( isxdigit(in[1]) || (in[1] == '+' || in[1] == '-') ) )
-                                               {
-                                                       in++;
-                                                       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
@@ -1975,8 +2007,6 @@ void InfoString_GetValue(const char *buffer, const char *key, char *value, size_
        size_t keylength;
        if (!key)
                key = "";
-       if (!value)
-               value = "";
        keylength = strlen(key);
        if (valuelength < 1 || !value)
        {
@@ -2185,3 +2215,84 @@ strlcpy(char *dst, const char *src, size_t siz)
 }
 
 #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;
+}