]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
changed the Sys_Error to a Con_Printf. LordHavoc, please check if the equation REALLY...
[xonotic/darkplaces.git] / common.c
index 2c9c8cc11c07c6d42eba517bb339d6b651abeaca..c9248464517a1642835cd06effb11b7af39b76c5 100644 (file)
--- a/common.c
+++ b/common.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -21,479 +21,224 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
-#define NUM_SAFE_ARGVS  7
-
-static char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
-static char     *argvdummy = " ";
-
-static char     *safeargvs[NUM_SAFE_ARGVS] =
-       {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
-
-cvar_t  registered = {"registered","0"};
-cvar_t  cmdline = {"cmdline","0", false, true};
-
-qboolean        com_modified;   // set true if using non-id files
-
-qboolean               proghack;
-
-int             static_registered = 1;  // only for startup check, then set
-
-qboolean               msg_suppress_1 = 0;
-
-void COM_InitFilesystem (void);
-
-// if a packfile directory differs from this, it is assumed to be hacked
-#define PAK0_COUNT              339
-#define PAK0_CRC                32981
-
-char   com_token[1024];
-int            com_argc;
-char   **com_argv;
-
-#define CMDLINE_LENGTH 256
-char   com_cmdline[CMDLINE_LENGTH];
-
-qboolean               standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
-
-// this graphic needs to be in the pak file to use registered features
-unsigned short pop[] =
-{
- 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
-,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
-,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
-,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
-,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
-,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
-,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
-,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
-,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
-,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
-,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
-,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
-,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
-,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
-,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
-,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
-};
-
-/*
-
-
-All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
-
-The "base directory" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.  The base directory is
-only used during filesystem initialization.
-
-The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to.  This can be overridden with the "-game" command line parameter.  The game directory can never be changed while quake is executing.  This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
-
-The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
-specified, when a file is found by the normal search path, it will be mirrored
-into the cache directory, then opened there.
-
-
-
-FIXME:
-The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently.  This could be used to add a "-sspeed 22050" for the high quality sound edition.  Because they are added at the end, they will not override an explicit setting on the original command line.
-       
-*/
-
-//============================================================================
+#include <stdlib.h>
+#include <fcntl.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
 
+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"};
 
-// ClearLink is used for new headnodes
-void ClearLink (link_t *l)
-{
-       l->prev = l->next = l;
-}
+char com_token[MAX_INPUTLINE];
+int com_argc;
+const char **com_argv;
 
-void RemoveLink (link_t *l)
-{
-       l->next->prev = l->prev;
-       l->prev->next = l->next;
-}
+gamemode_t gamemode;
+const char *gamename;
+const char *gamedirname1;
+const char *gamedirname2;
+const char *gamescreenshotname;
+const char *gameuserdirname;
+char com_modname[MAX_OSPATH] = "";
 
-void InsertLinkBefore (link_t *l, link_t *before)
-{
-       l->next = before;
-       l->prev = before->prev;
-       l->prev->next = l;
-       l->next->prev = l;
-}
-void InsertLinkAfter (link_t *l, link_t *after)
-{
-       l->next = after->next;
-       l->prev = after;
-       l->prev->next = l;
-       l->next->prev = l;
-}
 
 /*
 ============================================================================
 
-                                       LIBRARY REPLACEMENT FUNCTIONS
+                                       BYTE ORDER FUNCTIONS
 
 ============================================================================
 */
 
-/*
-void Q_memset (void *dest, int fill, int count)
+short   ShortSwap (short l)
 {
-       int             i;
-       
-       if ( (((long)dest | count) & 3) == 0)
-       {
-               count >>= 2;
-               fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
-               for (i=0 ; i<count ; i++)
-                       ((int *)dest)[i] = fill;
-       }
-       else
-               for (i=0 ; i<count ; i++)
-                       ((byte *)dest)[i] = fill;
-}
+       unsigned char    b1,b2;
 
-void Q_memcpy (void *dest, void *src, int count)
-{
-       int             i;
-       
-       if (( ( (long)dest | (long)src | count) & 3) == 0 )
-       {
-               count>>=2;
-               for (i=0 ; i<count ; i++)
-                       ((int *)dest)[i] = ((int *)src)[i];
-       }
-       else
-               for (i=0 ; i<count ; i++)
-                       ((byte *)dest)[i] = ((byte *)src)[i];
-}
+       b1 = l&255;
+       b2 = (l>>8)&255;
 
-int Q_memcmp (void *m1, void *m2, int count)
-{
-       while(count)
-       {
-               count--;
-               if (((byte *)m1)[count] != ((byte *)m2)[count])
-                       return -1;
-       }
-       return 0;
+       return (b1<<8) + b2;
 }
 
-void Q_strcpy (char *dest, char *src)
+int    LongSwap (int l)
 {
-       while (*src)
-       {
-               *dest++ = *src++;
-       }
-       *dest++ = 0;
+       unsigned char    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
 }
 
-void Q_strncpy (char *dest, char *src, int count)
+float FloatSwap (float f)
 {
-       while (*src && count--)
+       union
        {
-               *dest++ = *src++;
-       }
-       if (count)
-               *dest++ = 0;
-}
+               float   f;
+               unsigned char    b[4];
+       } dat1, dat2;
 
-int Q_strlen (char *str)
-{
-       int             count;
-       
-       count = 0;
-       while (str[count])
-               count++;
 
-       return count;
+       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;
 }
 
-char *Q_strrchr(char *s, char c)
-{
-    int len = Q_strlen(s);
-    s += len;
-    while (len--)
-       if (*--s == c) return s;
-    return 0;
-}
 
-void Q_strcat (char *dest, char *src)
-{
-       dest += Q_strlen(dest);
-       Q_strcpy (dest, src);
-}
+// Extract integers from buffers
 
-int Q_strcmp (char *s1, char *s2)
+unsigned int BuffBigLong (const unsigned char *buffer)
 {
-       while (1)
-       {
-               if (*s1 != *s2)
-                       return -1;              // strings not equal    
-               if (!*s1)
-                       return 0;               // strings are equal
-               s1++;
-               s2++;
-       }
-       
-       return -1;
+       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
 }
 
-int Q_strncmp (char *s1, char *s2, int count)
-{
-       while (1)
-       {
-               if (!count--)
-                       return 0;
-               if (*s1 != *s2)
-                       return -1;              // strings not equal    
-               if (!*s1)
-                       return 0;               // strings are equal
-               s1++;
-               s2++;
-       }
-       
-       return -1;
-}
-*/
-int Q_strncasecmp (char *s1, char *s2, int n)
+unsigned short BuffBigShort (const unsigned char *buffer)
 {
-       int             c1, c2;
-       
-       while (1)
-       {
-               c1 = *s1++;
-               c2 = *s2++;
-
-               if (!n--)
-                       return 0;               // strings are equal until end point
-               
-               if (c1 != c2)
-               {
-                       if (c1 >= 'a' && c1 <= 'z')
-                               c1 -= ('a' - 'A');
-                       if (c2 >= 'a' && c2 <= 'z')
-                               c2 -= ('a' - 'A');
-                       if (c1 != c2)
-                               return -1;              // strings not equal
-               }
-               if (!c1)
-                       return 0;               // strings are equal
-//              s1++;
-//              s2++;
-       }
-       
-       return -1;
+       return (buffer[0] << 8) | buffer[1];
 }
 
-int Q_strcasecmp (char *s1, char *s2)
+unsigned int BuffLittleLong (const unsigned char *buffer)
 {
-       return Q_strncasecmp (s1, s2, 99999);
+       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
 }
-/*
-int Q_atoi (char *str)
-{
-       int             val;
-       int             sign;
-       int             c;
-       
-       if (*str == '-')
-       {
-               sign = -1;
-               str++;
-       }
-       else
-               sign = 1;
-               
-       val = 0;
-
-//
-// check for hex
-//
-       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
-       {
-               str += 2;
-               while (1)
-               {
-                       c = *str++;
-                       if (c >= '0' && c <= '9')
-                               val = (val<<4) + c - '0';
-                       else if (c >= 'a' && c <= 'f')
-                               val = (val<<4) + c - 'a' + 10;
-                       else if (c >= 'A' && c <= 'F')
-                               val = (val<<4) + c - 'A' + 10;
-                       else
-                               return val*sign;
-               }
-       }
-       
-//
-// check for character
-//
-       if (str[0] == '\'')
-       {
-               return sign * str[1];
-       }
-       
-//
-// assume decimal
-//
-       while (1)
-       {
-               c = *str++;
-               if (c <'0' || c > '9')
-                       return val*sign;
-               val = val*10 + c - '0';
-       }
-       
-       return 0;
-}
-
 
-float Q_atof (char *str)
+unsigned short BuffLittleShort (const unsigned char *buffer)
 {
-       double                  val;
-       int             sign;
-       int             c;
-       int             decimal, total;
-       
-       if (*str == '-')
-       {
-               sign = -1;
-               str++;
-       }
-       else
-               sign = 1;
-               
-       val = 0;
-
-//
-// check for hex
-//
-       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
-       {
-               str += 2;
-               while (1)
-               {
-                       c = *str++;
-                       if (c >= '0' && c <= '9')
-                               val = (val*16) + c - '0';
-                       else if (c >= 'a' && c <= 'f')
-                               val = (val*16) + c - 'a' + 10;
-                       else if (c >= 'A' && c <= 'F')
-                               val = (val*16) + c - 'A' + 10;
-                       else
-                               return val*sign;
-               }
-       }
-       
-//
-// check for character
-//
-       if (str[0] == '\'')
-       {
-               return sign * str[1];
-       }
-       
-//
-// assume decimal
-//
-       decimal = -1;
-       total = 0;
-       while (1)
-       {
-               c = *str++;
-               if (c == '.')
-               {
-                       decimal = total;
-                       continue;
-               }
-               if (c <'0' || c > '9')
-                       break;
-               val = val*10 + c - '0';
-               total++;
-       }
-
-       if (decimal == -1)
-               return val*sign;
-       while (total > decimal)
-       {
-               val /= 10;
-               total--;
-       }
-       
-       return val*sign;
+       return (buffer[1] << 8) | buffer[0];
 }
-*/
+
 
 /*
 ============================================================================
 
-                                       BYTE ORDER FUNCTIONS
+                                       CRC FUNCTIONS
 
 ============================================================================
 */
 
-#ifndef WIN32
-short   (*BigShort) (short l);
-short   (*LittleShort) (short l);
-int     (*BigLong) (int l);
-int     (*LittleLong) (int l);
-float   (*BigFloat) (float l);
-float   (*LittleFloat) (float l);
-#endif
-
-short   ShortSwap (short l)
-{
-       byte    b1,b2;
-
-       b1 = l&255;
-       b2 = (l>>8)&255;
-
-       return (b1<<8) + b2;
-}
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below...  in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE 0xffff
+#define CRC_XOR_VALUE  0x0000
+
+static unsigned short crctable[256] =
+{
+       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+       0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+       0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+       0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+       0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+       0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+       0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+       0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+       0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+       0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+       0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+       0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+       0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+       0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+       0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+       0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+       0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+       0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+       0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+       0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+       0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+       0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+       0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+       0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+       0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+       0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+       0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+       0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+       0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+       0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+       0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+       0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
 
-short   ShortNoSwap (short l)
-{
-       return l;
-}
+unsigned short CRC_Block(const unsigned char *data, size_t size)
+{
+       unsigned short crc = CRC_INIT_VALUE;
+       while (size--)
+               crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
+       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] =
+{
+       0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
+       0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
+       0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
+       0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
+       0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
+       0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
+       0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
+       0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
+       0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
+       0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
+       0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
+       0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
+       0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
+       0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
+       0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
+       0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
+       0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
+       0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
+       0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
+       0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
+       0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
+       0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
+       0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
+       0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
+       0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
+       0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
+       0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
+       0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
+       0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
+       0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
+       0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
+       0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
+
+       // map checksum goes here
+       0x00,0x00,0x00,0x00
+};
 
-int    LongSwap (int l)
+// QuakeWorld
+unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
 {
-       byte    b1,b2,b3,b4;
-
-       b1 = l&255;
-       b2 = (l>>8)&255;
-       b3 = (l>>16)&255;
-       b4 = (l>>24)&255;
+       unsigned char *p;
+       unsigned char chkb[60 + 4];
 
-       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
-}
+       p = chktbl + (sequence % (sizeof(chktbl) - 8));
 
-int     LongNoSwap (int l)
-{
-       return l;
-}
+       if (length > 60)
+               length = 60;
+       memcpy(chkb, base, length);
 
-float FloatSwap (float f)
-{
-       union
-       {
-               float   f;
-               byte    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;
-}
+       chkb[length] = (sequence & 0xff) ^ p[0];
+       chkb[length+1] = p[1];
+       chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
+       chkb[length+3] = p[3];
 
-float FloatNoSwap (float f)
-{
-       return f;
+       return CRC_Block(chkb, length + 4) & 0xff;
 }
 
 /*
@@ -511,12 +256,7 @@ Handles byte ordering and avoids alignment errors
 
 void MSG_WriteChar (sizebuf_t *sb, int c)
 {
-       byte    *buf;
-       
-//#ifdef PARANOID
-//     if (c < -128 || c > 127)
-//             Sys_Error ("MSG_WriteChar: range error");
-//#endif
+       unsigned char    *buf;
 
        buf = SZ_GetSpace (sb, 1);
        buf[0] = c;
@@ -524,12 +264,7 @@ void MSG_WriteChar (sizebuf_t *sb, int c)
 
 void MSG_WriteByte (sizebuf_t *sb, int c)
 {
-       byte    *buf;
-       
-//#ifdef PARANOID
-//     if (c < 0 || c > 255)
-//             Sys_Error ("MSG_WriteByte: range error");
-//#endif
+       unsigned char    *buf;
 
        buf = SZ_GetSpace (sb, 1);
        buf[0] = c;
@@ -537,12 +272,7 @@ void MSG_WriteByte (sizebuf_t *sb, int c)
 
 void MSG_WriteShort (sizebuf_t *sb, int c)
 {
-       byte    *buf;
-       
-//#ifdef PARANOID
-//     if (c < ((short)0x8000) || c > (short)0x7fff)
-//             Sys_Error ("MSG_WriteShort: range error");
-//#endif
+       unsigned char    *buf;
 
        buf = SZ_GetSpace (sb, 2);
        buf[0] = c&0xff;
@@ -551,8 +281,8 @@ void MSG_WriteShort (sizebuf_t *sb, int c)
 
 void MSG_WriteLong (sizebuf_t *sb, int c)
 {
-       byte    *buf;
-       
+       unsigned char    *buf;
+
        buf = SZ_GetSpace (sb, 4);
        buf[0] = c&0xff;
        buf[1] = (c>>8)&0xff;
@@ -567,45 +297,103 @@ void MSG_WriteFloat (sizebuf_t *sb, float f)
                float   f;
                int     l;
        } dat;
-       
-       
+
+
        dat.f = f;
        dat.l = LittleLong (dat.l);
-       
-       SZ_Write (sb, &dat.l, 4);
+
+       SZ_Write (sb, (unsigned char *)&dat.l, 4);
+}
+
+void MSG_WriteString (sizebuf_t *sb, const char *s)
+{
+       if (!s || !*s)
+               MSG_WriteChar (sb, 0);
+       else
+               SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
+}
+
+void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
+{
+       if (s && *s)
+               SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
+}
+
+void MSG_WriteCoord13i (sizebuf_t *sb, float f)
+{
+       if (f >= 0)
+               MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
+       else
+               MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
+}
+
+void MSG_WriteCoord16i (sizebuf_t *sb, float f)
+{
+       if (f >= 0)
+               MSG_WriteShort (sb, (int)(f + 0.5));
+       else
+               MSG_WriteShort (sb, (int)(f - 0.5));
 }
 
-void MSG_WriteString (sizebuf_t *sb, char *s)
+void MSG_WriteCoord32f (sizebuf_t *sb, float f)
 {
-       if (!s)
-               SZ_Write (sb, "", 1);
+       MSG_WriteFloat (sb, f);
+}
+
+void MSG_WriteCoord (sizebuf_t *sb, float f, 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)
+               MSG_WriteCoord13i (sb, f);
+       else if (protocol == PROTOCOL_DARKPLACES1)
+               MSG_WriteCoord32f (sb, f);
+       else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
+               MSG_WriteCoord16i (sb, f);
+       else
+               MSG_WriteCoord32f (sb, f);
+}
+
+void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
+{
+       MSG_WriteCoord (sb, v[0], protocol);
+       MSG_WriteCoord (sb, v[1], protocol);
+       MSG_WriteCoord (sb, v[2], protocol);
+}
+
+// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
+void MSG_WriteAngle8i (sizebuf_t *sb, float f)
+{
+       if (f >= 0)
+               MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
        else
-               SZ_Write (sb, s, strlen(s)+1);
+               MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
 }
 
-void MSG_WriteCoord (sizebuf_t *sb, float f)
+void MSG_WriteAngle16i (sizebuf_t *sb, float f)
 {
-       if (dpprotocol)
-               MSG_WriteFloat(sb, f);
+       if (f >= 0)
+               MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
        else
-               MSG_WriteShort (sb, (int)(f*8));
+               MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
 }
 
-void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
+void MSG_WriteAngle32f (sizebuf_t *sb, float f)
 {
-       MSG_WriteShort (sb, (int) (f*65536.0f/360) & 65535);
+       MSG_WriteFloat (sb, f);
 }
 
-void MSG_WriteAngle (sizebuf_t *sb, float f)
+void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
 {
-       MSG_WriteByte (sb, ((int)f*256/360) & 255);
+       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)
+               MSG_WriteAngle8i (sb, f);
+       else
+               MSG_WriteAngle16i (sb, f);
 }
 
 //
 // reading functions
 //
-int                     msg_readcount;
-qboolean        msg_badread;
+int msg_readcount;
+qboolean msg_badread;
 
 void MSG_BeginReading (void)
 {
@@ -613,1311 +401,1753 @@ void MSG_BeginReading (void)
        msg_badread = false;
 }
 
-/*
-// returns -1 and sets msg_badread if no more characters are available
-int MSG_ReadChar (void)
+int MSG_ReadLittleShort (void)
 {
-       int     c;
-       
-       // LordHavoc: minor optimization
-       if (msg_readcount >= net_message.cursize)
-//     if (msg_readcount+1 > net_message.cursize)
+       if (msg_readcount+2 > net_message.cursize)
        {
                msg_badread = true;
                return -1;
        }
-               
-       c = (signed char)net_message.data[msg_readcount];
-       msg_readcount++;
-       
-       return c;
+       msg_readcount += 2;
+       return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
 }
 
-int MSG_ReadByte (void)
+int MSG_ReadBigShort (void)
 {
-       int     c;
-       
-       // LordHavoc: minor optimization
-       if (msg_readcount >= net_message.cursize)
-//     if (msg_readcount+1 > net_message.cursize)
+       if (msg_readcount+2 > net_message.cursize)
        {
                msg_badread = true;
                return -1;
        }
-               
-       c = (unsigned char)net_message.data[msg_readcount];
-       msg_readcount++;
-       
-       return c;
+       msg_readcount += 2;
+       return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
 }
-*/
 
-int MSG_ReadShort (void)
+int MSG_ReadLittleLong (void)
 {
-       int     c;
-       
-       if (msg_readcount+2 > net_message.cursize)
+       if (msg_readcount+4 > net_message.cursize)
        {
                msg_badread = true;
                return -1;
        }
-               
-       c = (short)(net_message.data[msg_readcount]
-       + (net_message.data[msg_readcount+1]<<8));
-       
-       msg_readcount += 2;
-       
-       return c;
+       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);
 }
 
-int MSG_ReadLong (void)
+int MSG_ReadBigLong (void)
 {
-       int     c;
-       
        if (msg_readcount+4 > net_message.cursize)
        {
                msg_badread = true;
                return -1;
        }
-               
-       c = net_message.data[msg_readcount]
-       + (net_message.data[msg_readcount+1]<<8)
-       + (net_message.data[msg_readcount+2]<<16)
-       + (net_message.data[msg_readcount+3]<<24);
-       
        msg_readcount += 4;
-       
-       return c;
+       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];
 }
 
-float MSG_ReadFloat (void)
+float MSG_ReadLittleFloat (void)
 {
        union
        {
-               byte    b[4];
-               float   f;
-               int     l;
+               float f;
+               int l;
        } dat;
-       
-       dat.b[0] =      net_message.data[msg_readcount];
-       dat.b[1] =      net_message.data[msg_readcount+1];
-       dat.b[2] =      net_message.data[msg_readcount+2];
-       dat.b[3] =      net_message.data[msg_readcount+3];
+       if (msg_readcount+4 > net_message.cursize)
+       {
+               msg_badread = true;
+               return -1;
+       }
        msg_readcount += 4;
-       
-       dat.l = LittleLong (dat.l);
+       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);
+       return dat.f;
+}
 
-       return dat.f;   
+float MSG_ReadBigFloat (void)
+{
+       union
+       {
+               float f;
+               int l;
+       } dat;
+       if (msg_readcount+4 > net_message.cursize)
+       {
+               msg_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];
+       return dat.f;
 }
 
 char *MSG_ReadString (void)
 {
-       static char     string[2048];
-       int             l,c;
-       
-       l = 0;
-       do
-       {
-               c = MSG_ReadChar ();
-               if (c == -1 || c == 0)
-                       break;
+       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;
-               l++;
-       } while (l < sizeof(string)-1);
-       
        string[l] = 0;
-       
        return string;
 }
 
-float MSG_ReadCoord (void)
+int MSG_ReadBytes (int numbytes, unsigned char *out)
 {
-       if (dpprotocol)
-               return MSG_ReadFloat();
-       else
-               return MSG_ReadShort() * (1.0f/8.0f);
+       int l, c;
+       for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
+               out[l] = c;
+       return l;
 }
 
-/*
-float MSG_ReadCoord (void)
+float MSG_ReadCoord13i (void)
 {
-       return MSG_ReadShort() * (1.0f/8.0f);
+       return MSG_ReadLittleShort() * (1.0/8.0);
 }
 
-float MSG_ReadAngle (void)
+float MSG_ReadCoord16i (void)
 {
-       return MSG_ReadChar() * (360.0f/256.0f);
+       return (signed short) MSG_ReadLittleShort();
 }
 
-float MSG_ReadPreciseAngle (void)
+float MSG_ReadCoord32f (void)
 {
-       return MSG_ReadShort() * (360.0f/65536);
+       return MSG_ReadLittleFloat();
 }
-*/
-
 
-//===========================================================================
+float MSG_ReadCoord (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();
+       else if (protocol == PROTOCOL_DARKPLACES1)
+               return MSG_ReadCoord32f();
+       else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
+               return MSG_ReadCoord16i();
+       else
+               return MSG_ReadCoord32f();
+}
 
-void SZ_Alloc (sizebuf_t *buf, int startsize)
+void MSG_ReadVector (float *v, protocolversion_t protocol)
 {
-       if (startsize < 256)
-               startsize = 256;
-       buf->data = Hunk_AllocName (startsize, "sizebuf");
-       buf->maxsize = startsize;
-       buf->cursize = 0;
+       v[0] = MSG_ReadCoord(protocol);
+       v[1] = MSG_ReadCoord(protocol);
+       v[2] = MSG_ReadCoord(protocol);
 }
 
+// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
+float MSG_ReadAngle8i (void)
+{
+       return (signed char) MSG_ReadByte () * (360.0/256.0);
+}
 
-void SZ_Free (sizebuf_t *buf)
+float MSG_ReadAngle16i (void)
 {
-//      Z_Free (buf->data);
-//      buf->data = NULL;
-//      buf->maxsize = 0;
-       buf->cursize = 0;
+       return (signed short)MSG_ReadShort () * (360.0/65536.0);
 }
 
+float MSG_ReadAngle32f (void)
+{
+       return MSG_ReadFloat ();
+}
+
+float MSG_ReadAngle (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 ();
+       else
+               return MSG_ReadAngle16i ();
+}
+
+
+//===========================================================================
+
 void SZ_Clear (sizebuf_t *buf)
 {
        buf->cursize = 0;
 }
 
-void *SZ_GetSpace (sizebuf_t *buf, int length)
+unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
 {
-       void    *data;
-       
+       unsigned char *data;
+
        if (buf->cursize + length > buf->maxsize)
        {
                if (!buf->allowoverflow)
-                       Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 512k (quake original default was 48k)");
-               
+                       Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
+
                if (length > buf->maxsize)
                        Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
-                       
+
                buf->overflowed = true;
-               Con_Printf ("SZ_GetSpace: overflow");
-               SZ_Clear (buf); 
+               Con_Print("SZ_GetSpace: overflow\n");
+               SZ_Clear (buf);
        }
 
        data = buf->data + buf->cursize;
        buf->cursize += length;
-       
+
        return data;
 }
 
-void SZ_Write (sizebuf_t *buf, void *data, int length)
+void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
 {
-       memcpy (SZ_GetSpace(buf,length),data,length);         
+       memcpy (SZ_GetSpace(buf,length),data,length);
 }
 
-void SZ_Print (sizebuf_t *buf, char *data)
+// LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
+// attention, it has been eradicated from here, its only (former) use in
+// all of darkplaces.
+
+static char *hexchar = "0123456789ABCDEF";
+void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
-       int             len;
-       
-       len = strlen(data)+1;
+       int i, j, n;
+       char text[1024];
+       char *cur, *flushpointer;
+       const unsigned char *d;
+       cur = text;
+       flushpointer = text + 512;
+       for (i = 0;i < size;)
+       {
+               n = 16;
+               if (n > size - i)
+                       n = size - i;
+               d = data + i;
+               // print offset
+               *cur++ = hexchar[(i >> 12) & 15];
+               *cur++ = hexchar[(i >>  8) & 15];
+               *cur++ = hexchar[(i >>  4) & 15];
+               *cur++ = hexchar[(i >>  0) & 15];
+               *cur++ = ':';
+               // print hex
+               for (j = 0;j < 16;j++)
+               {
+                       if (j < n)
+                       {
+                               *cur++ = hexchar[(d[j] >> 4) & 15];
+                               *cur++ = hexchar[(d[j] >> 0) & 15];
+                       }
+                       else
+                       {
+                               *cur++ = ' ';
+                               *cur++ = ' ';
+                       }
+                       if ((j & 3) == 3)
+                               *cur++ = ' ';
+               }
+               // print text
+               for (j = 0;j < 16;j++)
+               {
+                       if (j < n)
+                       {
+                               // color change prefix character has to be treated specially
+                               if (d[j] == STRING_COLOR_TAG)
+                               {
+                                       *cur++ = STRING_COLOR_TAG;
+                                       *cur++ = STRING_COLOR_TAG;
+                               }
+                               else if (d[j] >= ' ')
+                                       *cur++ = d[j];
+                               else
+                                       *cur++ = '.';
+                       }
+                       else
+                               *cur++ = ' ';
+               }
+               *cur++ = '\n';
+               i += n;
+               if (cur >= flushpointer || i >= size)
+               {
+                       *cur++ = 0;
+                       Con_Print(text);
+                       cur = text;
+               }
+       }
+}
 
-// byte * cast to keep VC++ happy
-       if (buf->data[buf->cursize-1])
-               memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
-       else
-               memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
+void SZ_HexDumpToConsole(const sizebuf_t *buf)
+{
+       Com_HexDumpToConsole(buf->data, buf->cursize);
 }
 
 
 //============================================================================
 
-
 /*
-============
-COM_SkipPath
-============
+==============
+COM_Wordwrap
+
+Word wraps a string. The wordWidth function is guaranteed to be called exactly
+once for each word in the string, so it may be stateful, no idea what that
+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.
+
+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.
+==============
 */
-char *COM_SkipPath (char *pathname)
-{
-       char    *last;
-       
-       last = pathname;
-       while (*pathname)
+int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
+{
+       // Logic is as follows:
+       //
+       // For each word or whitespace:
+       //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
+       //   Space found? Always add it to the current line, no matter if it fits.
+       //   Word found? Check if current line + current word fits.
+       //     If it fits, append it. Continue.
+       //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
+
+       qboolean isContinuation = false;
+       float spaceWidth;
+       const char *startOfLine = string;
+       const char *cursor = string;
+       const char *end = string + length;
+       float spaceUsedInLine = 0;
+       float spaceUsedForWord;
+       int result = 0;
+       size_t wordLen;
+       size_t dummy;
+
+       dummy = 0;
+       wordWidth(passthroughCW, NULL, &dummy, -1);
+       dummy = 1;
+       spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
+
+       for(;;)
        {
-               if (*pathname=='/')
-                       last = pathname+1;
-               pathname++;
+               char ch = (cursor < end) ? *cursor : 0;
+               switch(ch)
+               {
+                       case 0: // end of string
+                               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;
+                               ++cursor;
+                               startOfLine = cursor;
+                               break;
+                       case ' ': // space
+                               ++cursor;
+                               spaceUsedInLine += spaceWidth;
+                               break;
+                       default: // word
+                               wordLen = 1;
+                               while(cursor + wordLen < end)
+                               {
+                                       switch(cursor[wordLen])
+                                       {
+                                               case 0:
+                                               case '\n':
+                                               case ' ':
+                                                       goto out_inner;
+                                               default:
+                                                       ++wordLen;
+                                                       break;
+                                       }
+                               }
+                               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)
+                               {
+                                       wordLen = 1;
+                                       spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
+                               }
+                               if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
+                               {
+                                       // we can simply append it
+                                       cursor += wordLen;
+                                       spaceUsedInLine += spaceUsedForWord;
+                               }
+                               else
+                               {
+                                       // output current line
+                                       result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
+                                       isContinuation = true;
+                                       startOfLine = cursor;
+                                       cursor += wordLen;
+                                       spaceUsedInLine = continuationWidth + spaceUsedForWord;
+                               }
+               }
        }
-       return last;
-}
+       out:
 
-/*
-============
-COM_StripExtension
-============
-*/
-void COM_StripExtension (char *in, char *out)
-{
-       while (*in && *in != '.')
-               *out++ = *in++;
-       *out = 0;
-}
+       return result;
 
 /*
-============
-COM_FileExtension
-============
-*/
-char *COM_FileExtension (char *in)
-{
-       static char exten[8];
-       int             i;
+       qboolean isContinuation = false;
+       float currentWordSpace = 0;
+       const char *currentWord = 0;
+       float minReserve = 0;
 
-       while (*in && *in != '.')
-               in++;
-       if (!*in)
-               return "";
-       in++;
-       for (i=0 ; i<7 && *in ; i++,in++)
-               exten[i] = *in;
-       exten[i] = 0;
-       return exten;
-}
+       float spaceUsedInLine = 0;
+       const char *currentLine = 0;
+       const char *currentLineEnd = 0;
+       float currentLineFinalWhitespace = 0;
+       const char *p;
 
-/*
-============
-COM_FileBase
-============
-*/
-void COM_FileBase (char *in, char *out)
-{
-       char *s, *s2;
-       
-       s = in + strlen(in) - 1;
-       
-       while (s != in && *s != '.')
-               s--;
-       
-       for (s2 = s ; *s2 && *s2 != '/' ; s2--)
-       ;
-       
-       if (s-s2 < 2)
-               strcpy (out,"?model?");
-       else
+       int result = 0;
+       minReserve = charWidth(passthroughCW, 0);
+       minReserve += charWidth(passthroughCW, ' ');
+
+       if(maxWidth < continuationWidth + minReserve)
+               maxWidth = continuationWidth + minReserve;
+
+       charWidth(passthroughCW, 0);
+
+       for(p = string; p < string + length; ++p)
        {
-               s--;
-               strncpy (out,s2+1, s-s2);
-               out[s-s2] = 0;
-       }
-}
+               char c = *p;
+               float w = charWidth(passthroughCW, c);
 
+               if(!currentWord)
+               {
+                       currentWord = p;
+                       currentWordSpace = 0;
+               }
 
-/*
-==================
-COM_DefaultExtension
-==================
-*/
-void COM_DefaultExtension (char *path, char *extension)
-{
-       char    *src;
-//
-// if path doesn't have a .EXT, append extension
-// (extension should include the .)
-//
-       src = path + strlen(path) - 1;
+               if(!currentLine)
+               {
+                       currentLine = p;
+                       spaceUsedInLine = isContinuation ? continuationWidth : 0;
+                       currentLineEnd = 0;
+               }
+
+               if(c == ' ')
+               {
+                       // 1. I can add the word AND a space - then just append it.
+                       if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
+                       {
+                               currentLineEnd = p; // note: space not included here
+                               currentLineFinalWhitespace = w;
+                               spaceUsedInLine += currentWordSpace + w;
+                       }
+                       // 2. I can just add the word - then append it, output current line and go to next one.
+                       else if(spaceUsedInLine + currentWordSpace <= maxWidth)
+                       {
+                               result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
+                               currentLine = 0;
+                               isContinuation = true;
+                       }
+                       // 3. Otherwise, output current line and go to next one, where I can add the word.
+                       else if(continuationWidth + currentWordSpace + w <= maxWidth)
+                       {
+                               if(currentLineEnd)
+                                       result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
+                               currentLine = currentWord;
+                               spaceUsedInLine = continuationWidth + currentWordSpace + w;
+                               currentLineEnd = p;
+                               currentLineFinalWhitespace = w;
+                               isContinuation = true;
+                       }
+                       // 4. We can't even do that? Then output both current and next word as new lines.
+                       else
+                       {
+                               if(currentLineEnd)
+                               {
+                                       result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
+                                       isContinuation = true;
+                               }
+                               result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
+                               currentLine = 0;
+                               isContinuation = true;
+                       }
+                       currentWord = 0;
+               }
+               else if(c == '\n')
+               {
+                       // 1. I can add the word - then do it.
+                       if(spaceUsedInLine + currentWordSpace <= maxWidth)
+                       {
+                               result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
+                       }
+                       // 2. Otherwise, output current line, next one and make tabula rasa.
+                       else
+                       {
+                               if(currentLineEnd)
+                               {
+                                       processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
+                                       isContinuation = true;
+                               }
+                               result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
+                       }
+                       currentWord = 0;
+                       currentLine = 0;
+                       isContinuation = false;
+               }
+               else
+               {
+                       currentWordSpace += w;
+                       if(
+                               spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
+                               &&
+                               continuationWidth + currentWordSpace > maxWidth // can't join any other line...
+                       )
+                       {
+                               // this word cannot join ANY line...
+                               // so output the current line...
+                               if(currentLineEnd)
+                               {
+                                       result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
+                                       isContinuation = true;
+                               }
+
+                               // then this word's beginning...
+                               if(isContinuation)
+                               {
+                                       // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
+                                       float pieceWidth = maxWidth - continuationWidth;
+                                       const char *pos = currentWord;
+                                       currentWordSpace = 0;
+
+                                       // reset the char width function to a state where no kerning occurs (start of word)
+                                       charWidth(passthroughCW, ' ');
+                                       while(pos <= p)
+                                       {
+                                               float w = charWidth(passthroughCW, *pos);
+                                               if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
+                                               {
+                                                       // print everything until it
+                                                       result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
+                                                       // go to here
+                                                       currentWord = pos;
+                                                       currentWordSpace = 0;
+                                               }
+                                               currentWordSpace += w;
+                                               ++pos;
+                                       }
+                                       // now we have a currentWord that fits... set up its next line
+                                       // currentWordSpace has been set
+                                       // currentWord has been set
+                                       spaceUsedInLine = continuationWidth;
+                                       currentLine = currentWord;
+                                       currentLineEnd = 0;
+                                       isContinuation = true;
+                               }
+                               else
+                               {
+                                       // we have a guarantee that it will fix (see if clause)
+                                       result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
+
+                                       // and use the rest of this word as new start of a line
+                                       currentWordSpace = w;
+                                       currentWord = p;
+                                       spaceUsedInLine = continuationWidth;
+                                       currentLine = p;
+                                       currentLineEnd = 0;
+                                       isContinuation = true;
+                               }
+                       }
+               }
+       }
 
-       while (*src != '/' && src != path)
+       if(!currentWord)
        {
-               if (*src == '.')
-                       return;                 // it has an extension
-               src--;
+               currentWord = p;
+               currentWordSpace = 0;
        }
 
-       strcat (path, extension);
-}
+       if(currentLine) // Same procedure as \n
+       {
+               // Can I append the current word?
+               if(spaceUsedInLine + currentWordSpace <= maxWidth)
+                       result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
+               else
+               {
+                       if(currentLineEnd)
+                       {
+                               result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
+                               isContinuation = true;
+                       }
+                       result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
+               }
+       }
 
+       return result;
+*/
+}
 
 /*
 ==============
-COM_Parse
+COM_ParseToken_Simple
 
 Parse a token out of a string
 ==============
 */
-char *COM_Parse (char *data)
+int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
 {
-       int             c;
-       int             len;
-       
+       int len;
+       int c;
+       const char *data = *datapointer;
+
        len = 0;
        com_token[0] = 0;
-       
+
        if (!data)
-               return NULL;
-               
+       {
+               *datapointer = NULL;
+               return false;
+       }
+
 // skip whitespace
 skipwhite:
-       while ( (c = *data) <= ' ')
+       // line endings:
+       // UNIX: \n
+       // Mac: \r
+       // Windows: \r\n
+       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
-               if (c == 0)
-                       return NULL;                    // end of file;
-               data++;
+               if (*data == 0)
+               {
+                       // end of file
+                       *datapointer = NULL;
+                       return false;
+               }
        }
-       
-// skip // comments
-       if (c=='/' && data[1] == '/')
+
+       // handle Windows line ending
+       if (data[0] == '\r' && data[1] == '\n')
+               data++;
+
+       if (data[0] == '/' && data[1] == '/')
        {
-               while (*data && *data != '\n')
+               // comment
+               while (*data && *data != '\n' && *data != '\r')
                        data++;
                goto skipwhite;
        }
-       
-
-// handle quoted strings specially
-       if (c == '\"')
+       else if (data[0] == '/' && data[1] == '*')
        {
+               // comment
                data++;
-               while (1)
+               while (*data && (data[0] != '*' || data[1] != '/'))
+                       data++;
+               if (*data)
+                       data++;
+               if (*data)
+                       data++;
+               goto skipwhite;
+       }
+       else if (*data == '\"')
+       {
+               // quoted string
+               for (data++;*data && *data != '\"';data++)
                {
-                       c = *data++;
-                       if (c=='\"' || !c)
+                       c = *data;
+                       if (*data == '\\' && parsebackslash)
                        {
-                               com_token[len] = 0;
-                               return data;
+                               data++;
+                               c = *data;
+                               if (c == 'n')
+                                       c = '\n';
+                               else if (c == 't')
+                                       c = '\t';
                        }
-                       com_token[len] = c;
-                       len++;
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = c;
                }
+               com_token[len] = 0;
+               if (*data == '\"')
+                       data++;
+               *datapointer = data;
+               return true;
        }
-
-// parse single characters
-       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+       else if (*data == '\r')
        {
-               com_token[len] = c;
-               len++;
+               // translate Mac line ending to UNIX
+               com_token[len++] = '\n';data++;
                com_token[len] = 0;
-               return data+1;
+               *datapointer = data;
+               return true;
        }
-
-// parse a regular word
-       do
+       else if (*data == '\n')
        {
-               com_token[len] = c;
-               data++;
-               len++;
-               c = *data;
-       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
-                       break;
-       } while (c>32);
-       
-       com_token[len] = 0;
-       return data;
-}
-
-
-/*
-================
-COM_CheckParm
-
-Returns the position (1 to argc-1) in the program's argument list
-where the given parameter apears, or 0 if not present
-================
-*/
-int COM_CheckParm (char *parm)
-{
-       int             i;
-       
-       for (i=1 ; i<com_argc ; i++)
+               // single character
+               com_token[len++] = *data++;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
+       }
+       else
        {
-               if (!com_argv[i])
-                       continue;               // NEXTSTEP sometimes clears appkit vars.
-               if (!strcmp (parm,com_argv[i]))
-                       return i;
+               // regular word
+               for (;*data > ' ';data++)
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = *data;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
        }
-               
-       return 0;
 }
 
 /*
-================
-COM_CheckRegistered
+==============
+COM_ParseToken_QuakeC
 
-Looks for the pop.txt file and verifies it.
-Sets the "registered" cvar.
-Immediately exits out if an alternate game was attempted to be started without
-being registered.
-================
+Parse a token out of a string
+==============
 */
-void COM_CheckRegistered (void)
+int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
 {
-       int             h;
-       unsigned short  check[128];
-       int                     i;
-
-       Cvar_Set ("cmdline", com_cmdline);
+       int len;
+       int c;
+       const char *data = *datapointer;
 
-       COM_OpenFile("gfx/pop.lmp", &h, false);
-       static_registered = 0;
+       len = 0;
+       com_token[0] = 0;
 
-       if (h == -1)
+       if (!data)
        {
-               if (com_modified)
-                       Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
-               else
-                       Con_Printf ("Playing shareware version.\n");
-//#if WINDED
-//     Sys_Error ("This dedicated server requires a full registered copy of Quake");
-//#endif
-//             Con_Printf ("Playing shareware version.\n");
-//             if (com_modified)
-//                     Sys_Error ("You must have the registered version to use modified games");
-               return;
+               *datapointer = NULL;
+               return false;
        }
 
-       Sys_FileRead (h, check, sizeof(check));
-       COM_CloseFile (h);
-       
-       for (i=0 ; i<128 ; i++)
-               if (pop[i] != (unsigned short)BigShort (check[i]))
-                       Sys_Error ("Corrupted data file.");
-       
-//     Cvar_Set ("cmdline", com_cmdline);
-       Cvar_Set ("registered", "1");
-       static_registered = 1;
-       Con_Printf ("Playing registered version.\n");
-}
-
-
-void COM_Path_f (void);
-
-
-/*
-================
-COM_InitArgv
-================
-*/
-void COM_InitArgv (int argc, char **argv)
-{
-       qboolean        safe;
-       int             i, j, n;
-
-// reconstitute the command line for the cmdline externally visible cvar
-       n = 0;
-
-       for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
+// skip whitespace
+skipwhite:
+       // line endings:
+       // UNIX: \n
+       // Mac: \r
+       // Windows: \r\n
+       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
-               i = 0;
-
-               while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
+               if (*data == 0)
                {
-                       com_cmdline[n++] = argv[j][i++];
+                       // end of file
+                       *datapointer = NULL;
+                       return false;
                }
-
-               if (n < (CMDLINE_LENGTH - 1))
-                       com_cmdline[n++] = ' ';
-               else
-                       break;
        }
 
-       com_cmdline[n] = 0;
-
-       safe = false;
+       // handle Windows line ending
+       if (data[0] == '\r' && data[1] == '\n')
+               data++;
 
-       for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
-                com_argc++)
+       if (data[0] == '/' && data[1] == '/')
        {
-               largv[com_argc] = argv[com_argc];
-               if (!strcmp ("-safe", argv[com_argc]))
-                       safe = true;
+               // comment
+               while (*data && *data != '\n' && *data != '\r')
+                       data++;
+               goto skipwhite;
        }
-
-       if (safe)
+       else if (data[0] == '/' && data[1] == '*')
+       {
+               // comment
+               data++;
+               while (*data && (data[0] != '*' || data[1] != '/'))
+                       data++;
+               if (*data)
+                       data++;
+               if (*data)
+                       data++;
+               goto skipwhite;
+       }
+       else if (*data == '\"' || *data == '\'')
        {
-       // force all the safe-mode switches. Note that we reserved extra space in
-       // case we need to add these, so we don't need an overflow check
-               for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
+               // quoted string
+               char quote = *data;
+               for (data++;*data && *data != quote;data++)
                {
-                       largv[com_argc] = safeargvs[i];
-                       com_argc++;
+                       c = *data;
+                       if (*data == '\\')
+                       {
+                               data++;
+                               c = *data;
+                               if (c == 'n')
+                                       c = '\n';
+                               else if (c == 't')
+                                       c = '\t';
+                       }
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = c;
                }
+               com_token[len] = 0;
+               if (*data == quote)
+                       data++;
+               *datapointer = data;
+               return true;
        }
-
-       largv[com_argc] = argvdummy;
-       com_argv = largv;
-
-#ifdef NEHAHRA
-       nehahra = true;
-       standard_quake = false;
-#else
-       if (COM_CheckParm ("-rogue"))
+       else if (*data == '\r')
        {
-               rogue = true;
-               standard_quake = false;
+               // translate Mac line ending to UNIX
+               com_token[len++] = '\n';data++;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
        }
-
-       if (COM_CheckParm ("-hipnotic"))
+       else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
        {
-               hipnotic = true;
-               standard_quake = false;
+               // single character
+               com_token[len++] = *data++;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
        }
-
-       if (COM_CheckParm ("-nehahra"))
+       else
        {
-               nehahra = true;
-               standard_quake = false;
+               // regular word
+               for (;*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;
+               *datapointer = data;
+               return true;
        }
-#endif
 }
 
-
 /*
-================
-COM_Init
-================
+==============
+COM_ParseToken_VM_Tokenize
+
+Parse a token out of a string
+==============
 */
-void COM_Init (char *basedir)
+int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
 {
-#ifndef WIN32
-       byte    swaptest[2] = {1,0};
+       int len;
+       int c;
+       const char *data = *datapointer;
+
+       len = 0;
+       com_token[0] = 0;
 
-// set the byte swapping variables in a portable manner 
-       if ( *(short *)swaptest == 1)
+       if (!data)
        {
-               BigShort = ShortSwap;
-               LittleShort = ShortNoSwap;
-               BigLong = LongSwap;
-               LittleLong = LongNoSwap;
-               BigFloat = FloatSwap;
-               LittleFloat = FloatNoSwap;
+               *datapointer = NULL;
+               return false;
        }
-       else
+
+// skip whitespace
+skipwhite:
+       // line endings:
+       // UNIX: \n
+       // Mac: \r
+       // Windows: \r\n
+       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
-               BigShort = ShortNoSwap;
-               LittleShort = ShortSwap;
-               BigLong = LongNoSwap;
-               LittleLong = LongSwap;
-               BigFloat = FloatNoSwap;
-               LittleFloat = FloatSwap;
+               if (*data == 0)
+               {
+                       // end of file
+                       *datapointer = NULL;
+                       return false;
+               }
        }
-#endif
 
-       Cvar_RegisterVariable (&registered);
-       Cvar_RegisterVariable (&cmdline);
-       Cmd_AddCommand ("path", COM_Path_f);
+       // handle Windows line ending
+       if (data[0] == '\r' && data[1] == '\n')
+               data++;
 
-       COM_InitFilesystem ();
-       COM_CheckRegistered ();
+       if (data[0] == '/' && data[1] == '/')
+       {
+               // comment
+               while (*data && *data != '\n' && *data != '\r')
+                       data++;
+               goto skipwhite;
+       }
+       else if (data[0] == '/' && data[1] == '*')
+       {
+               // comment
+               data++;
+               while (*data && (data[0] != '*' || data[1] != '/'))
+                       data++;
+               if (*data)
+                       data++;
+               if (*data)
+                       data++;
+               goto skipwhite;
+       }
+       else if (*data == '\"' || *data == '\'')
+       {
+               char quote = *data;
+               // quoted string
+               for (data++;*data && *data != quote;data++)
+               {
+                       c = *data;
+                       if (*data == '\\')
+                       {
+                               data++;
+                               c = *data;
+                               if (c == 'n')
+                                       c = '\n';
+                               else if (c == 't')
+                                       c = '\t';
+                       }
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = c;
+               }
+               com_token[len] = 0;
+               if (*data == quote)
+                       data++;
+               *datapointer = data;
+               return true;
+       }
+       else if (*data == '\r')
+       {
+               // translate Mac line ending to UNIX
+               com_token[len++] = '\n';data++;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
+       }
+       else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
+       {
+               // single character
+               com_token[len++] = *data++;
+               com_token[len] = 0;
+               *datapointer = data;
+               return true;
+       }
+       else
+       {
+               // regular word
+               for (;*data > ' ' && *data != ',' && *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;
+               *datapointer = data;
+               return true;
+       }
 }
 
-
 /*
-============
-va
+==============
+COM_ParseToken_Console
 
-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
-============
+Parse a token out of a string, behaving like the qwcl console
+==============
 */
-char    *va(char *format, ...)
+int COM_ParseToken_Console(const char **datapointer)
 {
-       va_list         argptr;
-       static char             string[1024];
-       
-       va_start (argptr, format);
-       vsprintf (string, format,argptr);
-       va_end (argptr);
+       int len;
+       const char *data = *datapointer;
 
-       return string;  
-}
+       len = 0;
+       com_token[0] = 0;
+
+       if (!data)
+       {
+               *datapointer = NULL;
+               return false;
+       }
 
+// skip whitespace
+skipwhite:
+       for (;*data <= ' ';data++)
+       {
+               if (*data == 0)
+               {
+                       // end of file
+                       *datapointer = NULL;
+                       return false;
+               }
+       }
 
-/// just for debugging
-int     memsearch (byte *start, int count, int search)
-{
-       int             i;
-       
-       for (i=0 ; i<count ; i++)
-               if (start[i] == search)
-                       return i;
-       return -1;
+       if (*data == '/' && data[1] == '/')
+       {
+               // comment
+               while (*data && *data != '\n' && *data != '\r')
+                       data++;
+               goto skipwhite;
+       }
+       else if (*data == '\"')
+       {
+               // quoted string
+               for (data++;*data && *data != '\"';data++)
+               {
+                       // allow escaped " and \ case
+                       if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
+                               data++;
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = *data;
+               }
+               com_token[len] = 0;
+               if (*data == '\"')
+                       data++;
+               *datapointer = data;
+       }
+       else
+       {
+               // regular word
+               for (;*data > ' ';data++)
+                       if (len < (int)sizeof(com_token) - 1)
+                               com_token[len++] = *data;
+               com_token[len] = 0;
+               *datapointer = data;
+       }
+
+       return true;
 }
 
-/*
-=============================================================================
 
-QUAKE FILESYSTEM
+/*
+================
+COM_CheckParm
 
-=============================================================================
+Returns the position (1 to argc-1) in the program's argument list
+where the given parameter apears, or 0 if not present
+================
 */
+int COM_CheckParm (const char *parm)
+{
+       int i;
 
-int     com_filesize;
-
+       for (i=1 ; i<com_argc ; i++)
+       {
+               if (!com_argv[i])
+                       continue;               // NEXTSTEP sometimes clears appkit vars.
+               if (!strcmp (parm,com_argv[i]))
+                       return i;
+       }
 
-//
-// in memory
-//
+       return 0;
+}
 
-typedef struct
-{
-       char    name[MAX_QPATH];
-       int             filepos, filelen;
-} packfile_t;
+//===========================================================================
 
-typedef struct pack_s
-{
-       char    filename[MAX_OSPATH];
-       int             handle;
-       int             numfiles;
-       packfile_t      *files;
-} pack_t;
+// Game mods
+
+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_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" },
+};
 
-//
-// on disk
-//
-typedef struct
+void COM_InitGameType (void)
 {
-       char    name[56];
-       int             filepos, filelen;
-} dpackfile_t;
+       char name [MAX_OSPATH];
+       unsigned int i;
 
-typedef struct
-{
-       char    id[4];
-       int             dirofs;
-       int             dirlen;
-} dpackheader_t;
+       FS_StripExtension (com_argv[0], name, sizeof (name));
+       COM_ToLowerString (name, name, sizeof (name));
 
-// LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
-#define MAX_FILES_IN_PACK       16384
+       // 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))
+               {
+                       gamemode = (gamemode_t)i;
+                       break;
+               }
 
-char    com_cachedir[MAX_OSPATH];
-char    com_gamedir[MAX_OSPATH];
+       // 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))
+               {
+                       gamemode = (gamemode_t)i;
+                       break;
+               }
 
-typedef struct searchpath_s
-{
-       char    filename[MAX_OSPATH];
-       pack_t  *pack;          // only one of filename / pack will be used
-       struct searchpath_s *next;
-} searchpath_t;
+       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;
+}
 
-searchpath_t    *com_searchpaths;
 
 /*
-============
-COM_Path_f
-
-============
+================
+COM_Init
+================
 */
-void COM_Path_f (void)
+void COM_Init_Commands (void)
 {
-       searchpath_t    *s;
-       
-       Con_Printf ("Current search path:\n");
-       for (s=com_searchpaths ; s ; s=s->next)
+       int i, j, n;
+       char com_cmdline[MAX_INPUTLINE];
+
+       Cvar_RegisterVariable (&registered);
+       Cvar_RegisterVariable (&cmdline);
+
+       // reconstitute the command line for the cmdline externally visible cvar
+       n = 0;
+       for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
        {
-               if (s->pack)
+               i = 0;
+               if (strstr(com_argv[j], " "))
                {
-                       Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+                       // arg contains whitespace, store quotes around it
+                       com_cmdline[n++] = '\"';
+                       while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
+                               com_cmdline[n++] = com_argv[j][i++];
+                       com_cmdline[n++] = '\"';
                }
                else
-                       Con_Printf ("%s\n", s->filename);
+               {
+                       while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
+                               com_cmdline[n++] = com_argv[j][i++];
+               }
+               if (n < ((int)sizeof(com_cmdline) - 1))
+                       com_cmdline[n++] = ' ';
+               else
+                       break;
        }
+       com_cmdline[n] = 0;
+       Cvar_Set ("cmdline", com_cmdline);
 }
 
 /*
 ============
-COM_WriteFile
+va
 
-The filename will be prefixed by the current game directory
+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
 ============
 */
-void COM_WriteFile (char *filename, void *data, int len)
+char *va(const char *format, ...)
 {
-       int             handle;
-       char    name[MAX_OSPATH];
-       
-       sprintf (name, "%s/%s", com_gamedir, filename);
+       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;
 
-       handle = Sys_FileOpenWrite (name);
-       if (handle == -1)
-       {
-               Sys_Printf ("COM_WriteFile: failed on %s\n", name);
-               return;
-       }
-       
-       Sys_Printf ("COM_WriteFile: %s\n", name);
-       Sys_FileWrite (handle, data, len);
-       Sys_FileClose (handle);
+       s = string[stringindex];
+       stringindex = (stringindex + 1) & 7;
+       va_start (argptr, format);
+       dpvsnprintf (s, sizeof (string[0]), format,argptr);
+       va_end (argptr);
+
+       return s;
 }
 
 
-/*
-============
-COM_CreatePath
+//======================================
 
-Only used for CopyFile
-============
-*/
-void    COM_CreatePath (char *path)
+// snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
+
+#undef snprintf
+#undef vsnprintf
+
+#ifdef WIN32
+# define snprintf _snprintf
+# define vsnprintf _vsnprintf
+#endif
+
+
+int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
+{
+       va_list args;
+       int result;
+
+       va_start (args, format);
+       result = dpvsnprintf (buffer, buffersize, format, args);
+       va_end (args);
+
+       return result;
+}
+
+
+int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
 {
-       char    *ofs;
-       
-       for (ofs = path+1 ; *ofs ; ofs++)
+       int result;
+
+#if _MSC_VER >= 1400
+       result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
+#else
+       result = vsnprintf (buffer, buffersize, format, args);
+#endif
+       if (result < 0 || (size_t)result >= buffersize)
        {
-               if (*ofs == '/')
-               {       // create the directory
-                       *ofs = 0;
-                       Sys_mkdir (path);
-                       *ofs = '/';
-               }
+               buffer[buffersize - 1] = '\0';
+               return -1;
        }
+
+       return result;
 }
 
 
-/*
-===========
-COM_CopyFile
+//======================================
 
-Copies a file over from the net to the local cache, creating any directories
-needed.  This is for the convenience of developers using ISDN from home.
-===========
-*/
-void COM_CopyFile (char *netpath, char *cachepath)
-{
-       int             in, out;
-       int             remaining, count;
-       char    buf[4096];
-       
-       remaining = Sys_FileOpenRead (netpath, &in);            
-       COM_CreatePath (cachepath);     // create directories up to the cache file
-       out = Sys_FileOpenWrite (cachepath);
-       
-       while (remaining)
-       {
-               if (remaining < sizeof(buf))
-                       count = remaining;
+void COM_ToLowerString (const char *in, char *out, size_t size_out)
+{
+       if (size_out == 0)
+               return;
+
+       while (*in && size_out > 1)
+       {
+               if (*in >= 'A' && *in <= 'Z')
+                       *out++ = *in++ + 'a' - 'A';
                else
-                       count = sizeof(buf);
-               Sys_FileRead (in, buf, count);
-               Sys_FileWrite (out, buf, count);
-               remaining -= count;
+                       *out++ = *in++;
+               size_out--;
        }
-
-       Sys_FileClose (in);
-       Sys_FileClose (out);    
+       *out = '\0';
 }
 
-/*
-===========
-COM_FindFile
+void COM_ToUpperString (const char *in, char *out, size_t size_out)
+{
+       if (size_out == 0)
+               return;
 
-Finds the file in the search path.
-Sets com_filesize and one of handle or file
-===========
-*/
-int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
-{
-       searchpath_t    *search;
-       char            netpath[MAX_OSPATH];
-       char            cachepath[MAX_OSPATH];
-       pack_t          *pak;
-       int                     i;
-       int                     findtime, cachetime;
-
-       if (file && handle)
-               Sys_Error ("COM_FindFile: both handle and file set");
-       if (!file && !handle)
-               Sys_Error ("COM_FindFile: neither handle or file set");
-               
-//
-// search through the path, one element at a time
-//
-       search = com_searchpaths;
-       if (proghack)
-       {       // gross hack to use quake 1 progs with quake 2 maps
-               if (!strcmp(filename, "progs.dat"))
-                       search = search->next;
+       while (*in && size_out > 1)
+       {
+               if (*in >= 'a' && *in <= 'z')
+                       *out++ = *in++ + 'A' - 'a';
+               else
+                       *out++ = *in++;
+               size_out--;
        }
+       *out = '\0';
+}
 
-       for ( ; search ; search = search->next)
+int COM_StringBeginsWith(const char *s, const char *match)
+{
+       for (;*s && *match;s++, match++)
+               if (*s != *match)
+                       return false;
+       return true;
+}
+
+int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
+{
+       int argc, commentprefixlength;
+       char *tokenbufend;
+       const char *l;
+       argc = 0;
+       tokenbufend = tokenbuf + tokenbufsize;
+       l = *text;
+       commentprefixlength = 0;
+       if (commentprefix)
+               commentprefixlength = (int)strlen(commentprefix);
+       while (*l && *l != '\n' && *l != '\r')
        {
-       // is the element a pak file?
-               if (search->pack)
+               if (*l > ' ')
                {
-               // look through all the pak file elements
-                       pak = search->pack;
-                       for (i=0 ; i<pak->numfiles ; i++)
-                               if (!strcmp (pak->files[i].name, filename))
-                               {       // found it!
-                                       if (!quiet)
-                                               Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
-                                       if (handle)
-                                       {
-                                               *handle = pak->handle;
-                                               Sys_FileSeek (pak->handle, pak->files[i].filepos);
-                                       }
-                                       else
-                                       {       // open a new file on the pakfile
-                                               *file = fopen (pak->filename, "rb");
-                                               if (*file)
-                                                       fseek (*file, pak->files[i].filepos, SEEK_SET);
-                                       }
-                                       com_filesize = pak->files[i].filelen;
-                                       return com_filesize;
+                       if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
+                       {
+                               while (*l && *l != '\n' && *l != '\r')
+                                       l++;
+                               break;
+                       }
+                       if (argc >= maxargc)
+                               return -1;
+                       argv[argc++] = tokenbuf;
+                       if (*l == '"')
+                       {
+                               l++;
+                               while (*l && *l != '"')
+                               {
+                                       if (tokenbuf >= tokenbufend)
+                                               return -1;
+                                       *tokenbuf++ = *l++;
                                }
-               }
-               else
-               {               
-       // check a file in the directory tree
-//                     if (!static_registered)
-//                     {       // if not a registered version, don't ever go beyond base
-//                             if ( strchr (filename, '/') || strchr (filename,'\\'))
-//                                     continue;
-//                     }
-                       
-                       sprintf (netpath, "%s/%s",search->filename, filename);
-                       
-                       findtime = Sys_FileTime (netpath);
-                       if (findtime == -1)
-                               continue;
-                               
-               // see if the file needs to be updated in the cache
-                       if (!com_cachedir[0])
-                               strcpy (cachepath, netpath);
-                       else
-                       {       
-#if defined(_WIN32)
-                               if ((strlen(netpath) < 2) || (netpath[1] != ':'))
-                                       sprintf (cachepath,"%s%s", com_cachedir, netpath);
-                               else
-                                       sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
-#else
-                               sprintf (cachepath,"%s%s", com_cachedir, netpath);
-#endif
-
-                               cachetime = Sys_FileTime (cachepath);
-                       
-                               if (cachetime < findtime)
-                                       COM_CopyFile (netpath, cachepath);
-                               strcpy (netpath, cachepath);
-                       }       
-
-                       if (!quiet)
-                               Sys_Printf ("FindFile: %s\n",netpath);
-                       com_filesize = Sys_FileOpenRead (netpath, &i);
-                       if (handle)
-                               *handle = i;
+                               if (*l == '"')
+                                       l++;
+                       }
                        else
                        {
-                               Sys_FileClose (i);
-                               *file = fopen (netpath, "rb");
+                               while (*l > ' ')
+                               {
+                                       if (tokenbuf >= tokenbufend)
+                                               return -1;
+                                       *tokenbuf++ = *l++;
+                               }
                        }
-                       return com_filesize;
+                       if (tokenbuf >= tokenbufend)
+                               return -1;
+                       *tokenbuf++ = 0;
                }
-               
-       }
-       
-       if (!quiet)
-               Sys_Printf ("FindFile: can't find %s\n", filename);
-       
-       if (handle)
-               *handle = -1;
-       else
-               *file = NULL;
-       com_filesize = -1;
-       return -1;
+               else
+                       l++;
+       }
+       // line endings:
+       // UNIX: \n
+       // Mac: \r
+       // Windows: \r\n
+       if (*l == '\r')
+               l++;
+       if (*l == '\n')
+               l++;
+       *text = l;
+       return argc;
 }
 
-
 /*
-===========
-COM_OpenFile
+============
+COM_StringLengthNoColors
 
-filename never has a leading slash, but may contain directory walks
-returns a handle and a length
-it may actually be inside a pak file
-===========
-*/
-int COM_OpenFile (char *filename, int *handle, qboolean quiet)
-{
-       return COM_FindFile (filename, handle, NULL, quiet);
-}
+calculates the visible width of a color coded string.
 
-/*
-===========
-COM_FOpenFile
+*valid is filled with TRUE if the string is a valid colored string (that is, if
+it does not end with an unfinished color code). If it gets filled with FALSE, a
+fix would be adding a STRING_COLOR_TAG at the end of the string.
+
+valid can be set to NULL if the caller doesn't care.
 
-If the requested file is inside a packfile, a new FILE * will be opened
-into the file.
-===========
+For size_s, specify the maximum number of characters from s to use, or 0 to use
+all characters until the zero terminator.
+============
 */
-int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
+size_t
+COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
 {
-       return COM_FindFile (filename, NULL, file, quiet);
+       const char *end = size_s ? (s + size_s) : NULL;
+       size_t len = 0;
+       for(;;)
+       {
+               switch((s == end) ? 0 : *s)
+               {
+                       case 0:
+                               if(valid)
+                                       *valid = TRUE;
+                               return len;
+                       case STRING_COLOR_TAG:
+                               ++s;
+                               switch((s == end) ? 0 : *s)
+                               {
+                                       case 0: // ends with unfinished color code!
+                                               ++len;
+                                               if(valid)
+                                                       *valid = FALSE;
+                                               return len;
+                                       case STRING_COLOR_TAG: // escaped ^
+                                               ++len;
+                                               break;
+                                       case '0': case '1': case '2': case '3': case '4':
+                                       case '5': case '6': case '7': case '8': case '9': // color code
+                                               break;
+                                       default: // not a color code
+                                               ++len; // STRING_COLOR_TAG
+                                               ++len; // the character
+                                               break;
+                               }
+                               break;
+                       default:
+                               ++len;
+                               break;
+               }
+               ++s;
+       }
+       // never get here
 }
 
 /*
 ============
-COM_CloseFile
+COM_StringDecolorize
 
-If it is a pak file handle, don't really close it
-============
-*/
-void COM_CloseFile (int h)
-{
-       searchpath_t    *s;
-       
-       for (s = com_searchpaths ; s ; s=s->next)
-               if (s->pack && s->pack->handle == h)
-                       return;
-                       
-       Sys_FileClose (h);
-}
+removes color codes from a string.
 
+If escape_carets is true, the resulting string will be safe for printing. If
+escape_carets is false, the function will just strip color codes (for logging
+for example).
 
-/*
-============
-COM_LoadFile
+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
+bytes. In any case, the function makes sure that the resulting string is
+zero terminated.
 
-Filename are reletive to the quake directory.
-Allways appends a 0 byte.
+For size_in, specify the maximum number of characters from in to use, or 0 to use
+all characters until the zero terminator.
 ============
 */
-cache_user_t *loadcache;
-byte    *loadbuf;
-int             loadsize;
-byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
-{
-       int             h;
-       byte    *buf;
-       char    base[32];
-       int             len;
+qboolean
+COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
+{
+#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
+       const char *end = size_in ? (in + size_in) : NULL;
+       if(size_out < 1)
+               return FALSE;
+       for(;;)
+       {
+               switch((in == end) ? 0 : *in)
+               {
+                       case 0:
+                               *out++ = 0;
+                               return TRUE;
+                       case STRING_COLOR_TAG:
+                               ++in;
+                               switch((in == end) ? 0 : *in)
+                               {
+                                       case 0: // ends with unfinished color code!
+                                               APPEND(STRING_COLOR_TAG);
+                                               // finish the code by appending another caret when escaping
+                                               if(escape_carets)
+                                                       APPEND(STRING_COLOR_TAG);
+                                               *out++ = 0;
+                                               return TRUE;
+                                       case STRING_COLOR_TAG: // escaped ^
+                                               APPEND(STRING_COLOR_TAG);
+                                               // append a ^ twice when escaping
+                                               if(escape_carets)
+                                                       APPEND(STRING_COLOR_TAG);
+                                               break;
+                                       case '0': case '1': case '2': case '3': case '4':
+                                       case '5': case '6': case '7': case '8': case '9': // color code
+                                               break;
+                                       default: // not a color code
+                                               APPEND(STRING_COLOR_TAG);
+                                               APPEND(*in);
+                                               break;
+                               }
+                               break;
+                       default:
+                               APPEND(*in);
+                               break;
+               }
+               ++in;
+       }
+       // never get here
+#undef APPEND
+}
 
-       buf = NULL;     // quiet compiler warning
+// 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;
 
-// look for it in the filesystem or pack files
-       len = COM_OpenFile (path, &h, quiet);
-       if (h == -1)
+       if (*infostring++ != '\\')
                return NULL;
-       
-// extract the filename base name for hunk tag
-       COM_FileBase (path, base);
-       
-       if (usehunk == 1)
-               buf = Hunk_AllocName (len+1, base);
-       else if (usehunk == 2)
-               buf = Hunk_TempAlloc (len+1);
-       else if (usehunk == 0)
-               buf = Z_Malloc (len+1);
-       else if (usehunk == 3)
-               buf = Cache_Alloc (loadcache, len+1, base);
-       else if (usehunk == 4)
-       {
-               if (len+1 > loadsize)
-                       buf = Hunk_TempAlloc (len+1);
-               else
-                       buf = loadbuf;
-       }
-       else if (usehunk == 5)
-               buf = malloc (len+1);
-       else
-               Sys_Error ("COM_LoadFile: bad usehunk");
-
-       if (!buf)
-               Sys_Error ("COM_LoadFile: not enough space for %s", path);
-               
-       ((byte *)buf)[len] = 0;
 
-       Sys_FileRead (h, buf, len);                     
-       COM_CloseFile (h);
+       value_ind = 0;
+       for (;;)
+       {
+               key_ind = 0;
 
-       return buf;
-}
+               // Get the key name
+               for (;;)
+               {
+                       c = *infostring++;
 
-byte *COM_LoadHunkFile (char *path, qboolean quiet)
-{
-       return COM_LoadFile (path, 1, quiet);
-}
+                       if (c == '\0')
+                               return NULL;
+                       if (c == '\\' || key_ind == sizeof (crt_key) - 1)
+                       {
+                               crt_key[key_ind] = '\0';
+                               break;
+                       }
 
-byte *COM_LoadTempFile (char *path, qboolean quiet)
-{
-       return COM_LoadFile (path, 2, quiet);
-}
+                       crt_key[key_ind++] = c;
+               }
 
-// LordHavoc: returns malloc'd memory
-byte *COM_LoadMallocFile (char *path, qboolean quiet)
-{
-       return COM_LoadFile (path, 5, quiet);
-}
+               // If it's the key we are looking for, save it in "value"
+               if (!strcmp(crt_key, key))
+               {
+                       for (;;)
+                       {
+                               c = *infostring++;
 
-void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
-{
-       loadcache = cu;
-       COM_LoadFile (path, 3, quiet);
-}
+                               if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
+                               {
+                                       value[value_ind] = '\0';
+                                       return value;
+                               }
 
-// uses temp hunk if larger than bufsize
-byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, qboolean quiet)
-{
-       byte    *buf;
-       
-       loadbuf = (byte *)buffer;
-       loadsize = bufsize;
-       buf = COM_LoadFile (path, 4, quiet);
-       
-       return buf;
-}
+                               value[value_ind++] = c;
+                       }
+               }
 
-/*
-=================
-COM_LoadPackFile
+               // Else, skip the value
+               for (;;)
+               {
+                       c = *infostring++;
 
-Takes an explicit (not game tree related) path to a pak file.
+                       if (c == '\0')
+                               return NULL;
+                       if (c == '\\')
+                               break;
+               }
+       }
+}
 
-Loads the header and directory, adding the files at the beginning
-of the list so they override previous pack files.
-=================
-*/
-pack_t *COM_LoadPackFile (char *packfile)
+void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
 {
-       dpackheader_t   header;
-       int                             i;
-       packfile_t              *newfiles;
-       int                             numpackfiles;
-       pack_t                  *pack;
-       int                             packhandle;
-       // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
-       dpackfile_t             *info;
-       unsigned short          crc;
-
-       if (Sys_FileOpenRead (packfile, &packhandle) == -1)
+       int pos = 0, j;
+       size_t keylength;
+       if (!key)
+               key = "";
+       if (!value)
+               value = "";
+       keylength = strlen(key);
+       if (valuelength < 1 || !value)
        {
-//              Con_Printf ("Couldn't open %s\n", packfile);
-               return NULL;
+               Con_Printf("InfoString_GetValue: no room in value\n");
+               return;
        }
-       Sys_FileRead (packhandle, (void *)&header, sizeof(header));
-       if (header.id[0] != 'P' || header.id[1] != 'A'
-       || header.id[2] != 'C' || header.id[3] != 'K')
-               Sys_Error ("%s is not a packfile", packfile);
-       header.dirofs = LittleLong (header.dirofs);
-       header.dirlen = LittleLong (header.dirlen);
-
-       numpackfiles = header.dirlen / sizeof(dpackfile_t);
-
-       if (numpackfiles > MAX_FILES_IN_PACK)
-               Sys_Error ("%s has %i files", packfile, numpackfiles);
-
-       if (numpackfiles != PAK0_COUNT)
-               com_modified = true;    // not the original file
-
-       newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
-
-       info = malloc(sizeof(*info)*MAX_FILES_IN_PACK);
-       Sys_FileSeek (packhandle, header.dirofs);
-       Sys_FileRead (packhandle, (void *)info, header.dirlen);
-
-// crc the directory to check for modifications
-       CRC_Init (&crc);
-       // LordHavoc: speedup
-       CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
-//     for (i=0 ; i<header.dirlen ; i++)
-//             CRC_ProcessByte (&crc, ((byte *)info)[i]);
-       if (crc != PAK0_CRC)
-               com_modified = true;
-
-// parse the directory
-       for (i=0 ; i<numpackfiles ; i++)
+       value[0] = 0;
+       if (strchr(key, '\\'))
        {
-               strcpy (newfiles[i].name, info[i].name);
-               newfiles[i].filepos = LittleLong(info[i].filepos);
-               newfiles[i].filelen = LittleLong(info[i].filelen);
+               Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
+               return;
        }
-       free(info);
-
-       pack = Hunk_Alloc (sizeof (pack_t));
-       strcpy (pack->filename, packfile);
-       pack->handle = packhandle;
-       pack->numfiles = numpackfiles;
-       pack->files = newfiles;
-       
-       Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
-       return pack;
+       if (strchr(key, '\"'))
+       {
+               Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
+               return;
+       }
+       if (!key[0])
+       {
+               Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
+               return;
+       }
+       while (buffer[pos] == '\\')
+       {
+               if (!memcmp(buffer + pos+1, key, keylength))
+               {
+                       for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+                       pos++;
+                       for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
+                               value[j] = buffer[pos+j];
+                       value[j] = 0;
+                       return;
+               }
+               for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+               for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+       }
+       // if we reach this point the key was not found
 }
 
-
-/*
-================
-COM_AddGameDirectory
-
-Sets com_gamedir, adds the directory to the head of the path,
-then loads and adds pak1.pak pak2.pak ... 
-================
-*/
-void COM_AddGameDirectory (char *dir)
+void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
 {
-       int                             i;
-       searchpath_t    *search;
-       pack_t                  *pak;
-       char                    pakfile[MAX_OSPATH];
-
-       strcpy (com_gamedir, dir);
-
-//
-// add the directory to the search path
-//
-       search = Hunk_Alloc (sizeof(searchpath_t));
-       strcpy (search->filename, dir);
-       search->next = com_searchpaths;
-       com_searchpaths = search;
-
-//
-// add any pak files in the format pak0.pak pak1.pak, ...
-//
-       for (i=0 ; ; i++)
+       int pos = 0, pos2;
+       size_t keylength;
+       if (!key)
+               key = "";
+       if (!value)
+               value = "";
+       keylength = strlen(key);
+       if (strchr(key, '\\') || strchr(value, '\\'))
+       {
+               Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
+               return;
+       }
+       if (strchr(key, '\"') || strchr(value, '\"'))
+       {
+               Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
+               return;
+       }
+       if (!key[0])
+       {
+               Con_Printf("InfoString_SetValue: can not set a key with no name\n");
+               return;
+       }
+       while (buffer[pos] == '\\')
        {
-               sprintf (pakfile, "%s/pak%i.pak", dir, i);
-               pak = COM_LoadPackFile (pakfile);
-               if (!pak)
+               if (!memcmp(buffer + pos+1, key, keylength))
                        break;
-               search = Hunk_Alloc (sizeof(searchpath_t));
-               search->pack = pak;
-               search->next = com_searchpaths;
-               com_searchpaths = search;               
+               for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+               for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
        }
-
-//
-// add the contents of the parms.txt file to the end of the command line
-//
-
-}
-
-/*
-================
-COM_InitFilesystem
-================
-*/
-void COM_InitFilesystem (void)
-{
-       int             i, j;
-       char    basedir[MAX_OSPATH];
-       searchpath_t    *search;
-
-//
-// -basedir <path>
-// Overrides the system supplied base directory (under GAMENAME)
-//
-       i = COM_CheckParm ("-basedir");
-       if (i && i < com_argc-1)
-               strcpy (basedir, com_argv[i+1]);
-       else
-               strcpy (basedir, host_parms.basedir);
-
-       j = strlen (basedir);
-
-       if (j > 0)
+       // if we found the key, find the end of it because we will be replacing it
+       pos2 = pos;
+       if (buffer[pos] == '\\')
        {
-               if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
-                       basedir[j-1] = 0;
+               for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
+               for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
        }
-
-//
-// -cachedir <path>
-// Overrides the system supplied cache directory (NULL or /qcache)
-// -cachedir - will disable caching.
-//
-       i = COM_CheckParm ("-cachedir");
-       if (i && i < com_argc-1)
+       if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
        {
-               if (com_argv[i+1][0] == '-')
-                       com_cachedir[0] = 0;
-               else
-                       strcpy (com_cachedir, com_argv[i+1]);
+               Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
+               return;
+       }
+       if (value && value[0])
+       {
+               // set the key/value and append the remaining text
+               char tempbuffer[4096];
+               strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
+               dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
        }
-       else if (host_parms.cachedir)
-               strcpy (com_cachedir, host_parms.cachedir);
        else
-               com_cachedir[0] = 0;
-
-//
-// start up with GAMENAME by default (id1)
-//
-       COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
-
-#ifdef NEHAHRA
-       COM_AddGameDirectory (va("%s/nehahra", basedir) );
-#else
-       if (COM_CheckParm ("-rogue"))
-               COM_AddGameDirectory (va("%s/rogue", basedir) );
-       if (COM_CheckParm ("-hipnotic"))
-               COM_AddGameDirectory (va("%s/hipnotic", basedir) );
-       if (COM_CheckParm ("-nehahra"))
-               COM_AddGameDirectory (va("%s/nehahra", basedir) );
-#endif
-
-//
-// -game <gamedir>
-// Adds basedir/gamedir as an override game
-//
-       i = COM_CheckParm ("-game");
-       if (i && i < com_argc-1)
        {
-               com_modified = true;
-               COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
+               // just remove the key from the text
+               strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
        }
+}
 
-//
-// -path <dir or packfile> [<dir or packfile>] ...
-// Fully specifies the exact serach path, overriding the generated one
-//
-       i = COM_CheckParm ("-path");
-       if (i)
+void InfoString_Print(char *buffer)
+{
+       int i;
+       char key[2048];
+       char value[2048];
+       while (*buffer)
        {
-               com_modified = true;
-               com_searchpaths = NULL;
-               while (++i < com_argc)
+               if (*buffer != '\\')
                {
-                       if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
-                               break;
-                       
-                       search = Hunk_Alloc (sizeof(searchpath_t));
-                       if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
-                       {
-                               search->pack = COM_LoadPackFile (com_argv[i]);
-                               if (!search->pack)
-                                       Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
-                       }
-                       else
-                               strcpy (search->filename, com_argv[i]);
-                       search->next = com_searchpaths;
-                       com_searchpaths = search;
+                       Con_Printf("InfoString_Print: corrupt string\n");
+                       return;
+               }
+               for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
+                       if (i < (int)sizeof(key)-1)
+                               key[i++] = *buffer;
+               key[i] = 0;
+               if (*buffer != '\\')
+               {
+                       Con_Printf("InfoString_Print: corrupt string\n");
+                       return;
                }
+               for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
+                       if (i < (int)sizeof(value)-1)
+                               value[i++] = *buffer;
+               value[i] = 0;
+               // empty value is an error case
+               Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
        }
+}
 
-       if (COM_CheckParm ("-proghack"))
-               proghack = true;
+//========================================================
+// strlcat and strlcpy, from OpenBSD
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*     $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
+/*     $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
+
+
+#ifndef HAVE_STRLCAT
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+       register char *d = dst;
+       register const char *s = src;
+       register size_t n = siz;
+       size_t dlen;
+
+       /* Find the end of dst and adjust bytes left but don't go past end */
+       while (n-- != 0 && *d != '\0')
+               d++;
+       dlen = d - dst;
+       n = siz - dlen;
+
+       if (n == 0)
+               return(dlen + strlen(s));
+       while (*s != '\0') {
+               if (n != 1) {
+                       *d++ = *s;
+                       n--;
+               }
+               s++;
+       }
+       *d = '\0';
+
+       return(dlen + (s - src));       /* count does not include NUL */
 }
+#endif  // #ifndef HAVE_STRLCAT
+
 
-int COM_FileExists(char *filename)
+#ifndef HAVE_STRLCPY
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
 {
-       searchpath_t    *search;
-       char                    netpath[MAX_OSPATH];
-       pack_t                  *pak;
-       int                             i;
-       int                             findtime;
+       register char *d = dst;
+       register const char *s = src;
+       register size_t n = siz;
 
-       for (search = com_searchpaths;search;search = search->next)
-       {
-               if (search->pack)
-               {
-                       pak = search->pack;
-                       for (i = 0;i < pak->numfiles;i++)
-                               if (!strcmp (pak->files[i].name, filename))
-                                       return true;
-               }
-               else
-               {
-                       sprintf (netpath, "%s/%s",search->filename, filename);               
-                       findtime = Sys_FileTime (netpath);
-                       if (findtime != -1)
-                               return true;
-               }               
+       /* Copy as many bytes as will fit */
+       if (n != 0 && --n != 0) {
+               do {
+                       if ((*d++ = *s++) == 0)
+                               break;
+               } while (--n != 0);
+       }
+
+       /* Not enough room in dst, add NUL and traverse rest of src */
+       if (n == 0) {
+               if (siz != 0)
+                       *d = '\0';              /* NUL-terminate dst */
+               while (*s++)
+                       ;
        }
 
-       return false;
+       return(s - src - 1);    /* count does not include NUL */
 }
 
+#endif  // #ifndef HAVE_STRLCPY