rewrote protocol version system (including splitting PROTOCOL_QUAKE into PROTOCOL_QUA...
[divverent/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0"};
31 cvar_t cmdline = {0, "cmdline","0"};
32
33 extern qboolean fs_modified;   // set true if using non-id files
34
35 char com_token[1024];
36 int com_argc;
37 const char **com_argv;
38
39 // LordHavoc: made commandline 1024 characters instead of 256
40 #define CMDLINE_LENGTH  1024
41 char com_cmdline[CMDLINE_LENGTH];
42
43 gamemode_t gamemode;
44 const char *gamename;
45 const char *gamedirname1;
46 const char *gamedirname2;
47 const char *gamescreenshotname;
48 const char *gameuserdirname;
49 char com_modname[MAX_OSPATH] = "";
50
51
52 /*
53 ============================================================================
54
55                                         BYTE ORDER FUNCTIONS
56
57 ============================================================================
58 */
59
60 short   ShortSwap (short l)
61 {
62         qbyte    b1,b2;
63
64         b1 = l&255;
65         b2 = (l>>8)&255;
66
67         return (b1<<8) + b2;
68 }
69
70 int    LongSwap (int l)
71 {
72         qbyte    b1,b2,b3,b4;
73
74         b1 = l&255;
75         b2 = (l>>8)&255;
76         b3 = (l>>16)&255;
77         b4 = (l>>24)&255;
78
79         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
80 }
81
82 float FloatSwap (float f)
83 {
84         union
85         {
86                 float   f;
87                 qbyte    b[4];
88         } dat1, dat2;
89
90
91         dat1.f = f;
92         dat2.b[0] = dat1.b[3];
93         dat2.b[1] = dat1.b[2];
94         dat2.b[2] = dat1.b[1];
95         dat2.b[3] = dat1.b[0];
96         return dat2.f;
97 }
98
99
100 // Extract integers from buffers
101
102 unsigned int BuffBigLong (const qbyte *buffer)
103 {
104         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
105 }
106
107 unsigned short BuffBigShort (const qbyte *buffer)
108 {
109         return (buffer[0] << 8) | buffer[1];
110 }
111
112 unsigned int BuffLittleLong (const qbyte *buffer)
113 {
114         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
115 }
116
117 unsigned short BuffLittleShort (const qbyte *buffer)
118 {
119         return (buffer[1] << 8) | buffer[0];
120 }
121
122
123 /*
124 ============================================================================
125
126                                         CRC FUNCTIONS
127
128 ============================================================================
129 */
130
131 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
132 // and the initial and final xor values shown below...  in other words, the
133 // CCITT standard CRC used by XMODEM
134
135 #define CRC_INIT_VALUE  0xffff
136 #define CRC_XOR_VALUE   0x0000
137
138 static unsigned short crctable[256] =
139 {
140         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
141         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
142         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
143         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
144         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
145         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
146         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
147         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
148         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
149         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
150         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
151         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
152         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
153         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
154         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
155         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
156         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
157         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
158         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
159         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
160         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
161         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
162         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
163         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
164         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
165         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
166         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
167         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
168         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
169         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
170         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
171         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
172 };
173
174 unsigned short CRC_Block(const qbyte *data, int size)
175 {
176         unsigned short crc = CRC_INIT_VALUE;
177         while (size--)
178                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
179         return crc ^ CRC_XOR_VALUE;
180 }
181
182
183 /*
184 ==============================================================================
185
186                         MESSAGE IO FUNCTIONS
187
188 Handles byte ordering and avoids alignment errors
189 ==============================================================================
190 */
191
192 //
193 // writing functions
194 //
195
196 void MSG_WriteChar (sizebuf_t *sb, int c)
197 {
198         qbyte    *buf;
199
200         buf = SZ_GetSpace (sb, 1);
201         buf[0] = c;
202 }
203
204 void MSG_WriteByte (sizebuf_t *sb, int c)
205 {
206         qbyte    *buf;
207
208         buf = SZ_GetSpace (sb, 1);
209         buf[0] = c;
210 }
211
212 void MSG_WriteShort (sizebuf_t *sb, int c)
213 {
214         qbyte    *buf;
215
216         buf = SZ_GetSpace (sb, 2);
217         buf[0] = c&0xff;
218         buf[1] = c>>8;
219 }
220
221 void MSG_WriteLong (sizebuf_t *sb, int c)
222 {
223         qbyte    *buf;
224
225         buf = SZ_GetSpace (sb, 4);
226         buf[0] = c&0xff;
227         buf[1] = (c>>8)&0xff;
228         buf[2] = (c>>16)&0xff;
229         buf[3] = c>>24;
230 }
231
232 void MSG_WriteFloat (sizebuf_t *sb, float f)
233 {
234         union
235         {
236                 float   f;
237                 int     l;
238         } dat;
239
240
241         dat.f = f;
242         dat.l = LittleLong (dat.l);
243
244         SZ_Write (sb, &dat.l, 4);
245 }
246
247 void MSG_WriteString (sizebuf_t *sb, const char *s)
248 {
249         if (!s)
250                 SZ_Write (sb, "", 1);
251         else
252                 SZ_Write (sb, s, strlen(s)+1);
253 }
254
255 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
256 {
257         if (f >= 0)
258                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
259         else
260                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
261 }
262
263 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
264 {
265         if (f >= 0)
266                 MSG_WriteShort (sb, (int)(f + 0.5));
267         else
268                 MSG_WriteShort (sb, (int)(f - 0.5));
269 }
270
271 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
272 {
273         MSG_WriteFloat (sb, f);
274 }
275
276 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
277 {
278         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
279                 MSG_WriteCoord13i (sb, f);
280         else if (protocol == PROTOCOL_DARKPLACES1)
281                 MSG_WriteCoord32f (sb, f);
282         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
283                 MSG_WriteCoord16i (sb, f);
284         else
285                 MSG_WriteCoord32f (sb, f);
286         //else
287         //      Host_Error("MSG_WriteCoord: unknown protocol\n");
288 }
289
290 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
291 {
292         MSG_WriteCoord (sb, v[0], protocol);
293         MSG_WriteCoord (sb, v[1], protocol);
294         MSG_WriteCoord (sb, v[2], protocol);
295 }
296
297 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
298 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
299 {
300         if (f >= 0)
301                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
302         else
303                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
304 }
305
306 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
307 {
308         if (f >= 0)
309                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
310         else
311                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
312 }
313
314 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
315 {
316         MSG_WriteFloat (sb, f);
317 }
318
319 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
320 {
321         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
322                 MSG_WriteAngle8i (sb, f);
323         else
324                 MSG_WriteAngle16i (sb, f);
325 }
326
327 //
328 // reading functions
329 //
330 int msg_readcount;
331 qboolean msg_badread;
332
333 void MSG_BeginReading (void)
334 {
335         msg_readcount = 0;
336         msg_badread = false;
337 }
338
339 int MSG_ReadLittleShort (void)
340 {
341         if (msg_readcount+2 > net_message.cursize)
342         {
343                 msg_badread = true;
344                 return -1;
345         }
346         msg_readcount += 2;
347         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
348 }
349
350 int MSG_ReadBigShort (void)
351 {
352         if (msg_readcount+2 > net_message.cursize)
353         {
354                 msg_badread = true;
355                 return -1;
356         }
357         msg_readcount += 2;
358         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
359 }
360
361 int MSG_ReadLittleLong (void)
362 {
363         if (msg_readcount+4 > net_message.cursize)
364         {
365                 msg_badread = true;
366                 return -1;
367         }
368         msg_readcount += 4;
369         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);
370 }
371
372 int MSG_ReadBigLong (void)
373 {
374         if (msg_readcount+4 > net_message.cursize)
375         {
376                 msg_badread = true;
377                 return -1;
378         }
379         msg_readcount += 4;
380         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];
381 }
382
383 float MSG_ReadLittleFloat (void)
384 {
385         union
386         {
387                 float f;
388                 int l;
389         } dat;
390         if (msg_readcount+4 > net_message.cursize)
391         {
392                 msg_badread = true;
393                 return -1;
394         }
395         msg_readcount += 4;
396         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);
397         return dat.f;
398 }
399
400 float MSG_ReadBigFloat (void)
401 {
402         union
403         {
404                 float f;
405                 int l;
406         } dat;
407         if (msg_readcount+4 > net_message.cursize)
408         {
409                 msg_badread = true;
410                 return -1;
411         }
412         msg_readcount += 4;
413         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];
414         return dat.f;
415 }
416
417 char *MSG_ReadString (void)
418 {
419         static char string[2048];
420         int l,c;
421         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
422                 string[l] = c;
423         string[l] = 0;
424         return string;
425 }
426
427 int MSG_ReadBytes (int numbytes, unsigned char *out)
428 {
429         int l, c;
430         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
431                 out[l] = c;
432         return l;
433 }
434
435 float MSG_ReadCoord13i (void)
436 {
437         return MSG_ReadLittleShort() * (1.0/8.0);
438 }
439
440 float MSG_ReadCoord16i (void)
441 {
442         return (signed short) MSG_ReadLittleShort();
443 }
444
445 float MSG_ReadCoord32f (void)
446 {
447         return MSG_ReadLittleFloat();
448 }
449
450 float MSG_ReadCoord (protocolversion_t protocol)
451 {
452         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
453                 return MSG_ReadCoord13i();
454         else if (protocol == PROTOCOL_DARKPLACES1)
455                 return MSG_ReadCoord32f();
456         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
457                 return MSG_ReadCoord16i();
458         else
459                 return MSG_ReadCoord32f();
460 }
461
462 void MSG_ReadVector (float *v, protocolversion_t protocol)
463 {
464         v[0] = MSG_ReadCoord(protocol);
465         v[1] = MSG_ReadCoord(protocol);
466         v[2] = MSG_ReadCoord(protocol);
467 }
468
469 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
470 float MSG_ReadAngle8i (void)
471 {
472         return (signed char) MSG_ReadByte () * (360.0/256.0);
473 }
474
475 float MSG_ReadAngle16i (void)
476 {
477         return (signed short)MSG_ReadShort () * (360.0/65536.0);
478 }
479
480 float MSG_ReadAngle32f (void)
481 {
482         return MSG_ReadFloat ();
483 }
484
485 float MSG_ReadAngle (protocolversion_t protocol)
486 {
487         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
488                 return MSG_ReadAngle8i ();
489         else
490                 return MSG_ReadAngle16i ();
491 }
492
493
494 //===========================================================================
495
496 void SZ_Clear (sizebuf_t *buf)
497 {
498         buf->cursize = 0;
499 }
500
501 void *SZ_GetSpace (sizebuf_t *buf, int length)
502 {
503         void *data;
504
505         if (buf->cursize + length > buf->maxsize)
506         {
507                 if (!buf->allowoverflow)
508                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
509
510                 if (length > buf->maxsize)
511                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
512
513                 buf->overflowed = true;
514                 Con_Print("SZ_GetSpace: overflow\n");
515                 SZ_Clear (buf);
516         }
517
518         data = buf->data + buf->cursize;
519         buf->cursize += length;
520
521         return data;
522 }
523
524 void SZ_Write (sizebuf_t *buf, const void *data, int length)
525 {
526         memcpy (SZ_GetSpace(buf,length),data,length);
527 }
528
529 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
530 // attention, it has been eradicated from here, its only (former) use in
531 // all of darkplaces.
532
533 static char *hexchar = "0123456789ABCDEF";
534 void Com_HexDumpToConsole(const qbyte *data, int size)
535 {
536         int i, j, n;
537         char text[1024];
538         char *cur, *flushpointer;
539         const qbyte *d;
540         cur = text;
541         flushpointer = text + 512;
542         for (i = 0;i < size;)
543         {
544                 n = 16;
545                 if (n > size - i)
546                         n = size - i;
547                 d = data + i;
548                 // print offset
549                 *cur++ = hexchar[(i >> 12) & 15];
550                 *cur++ = hexchar[(i >>  8) & 15];
551                 *cur++ = hexchar[(i >>  4) & 15];
552                 *cur++ = hexchar[(i >>  0) & 15];
553                 *cur++ = ':';
554                 // print hex
555                 for (j = 0;j < 16;j++)
556                 {
557                         if (j < n)
558                         {
559                                 *cur++ = hexchar[(d[j] >> 4) & 15];
560                                 *cur++ = hexchar[(d[j] >> 0) & 15];
561                         }
562                         else
563                         {
564                                 *cur++ = ' ';
565                                 *cur++ = ' ';
566                         }
567                         if ((j & 3) == 3)
568                                 *cur++ = ' ';
569                 }
570                 // print text
571                 for (j = 0;j < 16;j++)
572                 {
573                         if (j < n)
574                         {
575                                 if (d[j] >= ' ' && d[j] <= 127)
576                                         *cur++ = d[j];
577                                 else
578                                         *cur++ = '.';
579                         }
580                         else
581                                 *cur++ = ' ';
582                 }
583                 *cur++ = '\n';
584                 i += n;
585                 if (cur >= flushpointer || i >= size)
586                 {
587                         *cur++ = 0;
588                         Con_Print(text);
589                         cur = text;
590                 }
591         }
592 }
593
594 void SZ_HexDumpToConsole(const sizebuf_t *buf)
595 {
596         Com_HexDumpToConsole(buf->data, buf->cursize);
597 }
598
599
600 //============================================================================
601
602
603 /*
604 ==============
605 COM_ParseToken
606
607 Parse a token out of a string
608 ==============
609 */
610 int COM_ParseToken(const char **datapointer, int returnnewline)
611 {
612         int len;
613         const char *data = *datapointer;
614
615         len = 0;
616         com_token[0] = 0;
617
618         if (!data)
619         {
620                 *datapointer = NULL;
621                 return false;
622         }
623
624 // skip whitespace
625 skipwhite:
626         // line endings:
627         // UNIX: \n
628         // Mac: \r
629         // Windows: \r\n
630         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
631         {
632                 if (*data == 0)
633                 {
634                         // end of file
635                         *datapointer = NULL;
636                         return false;
637                 }
638         }
639
640         // handle Windows line ending
641         if (data[0] == '\r' && data[1] == '\n')
642                 data++;
643
644         if (data[0] == '/' && data[1] == '/')
645         {
646                 // comment
647                 while (*data && *data != '\n' && *data != '\r')
648                         data++;
649                 goto skipwhite;
650         }
651         else if (data[0] == '/' && data[1] == '*')
652         {
653                 // comment
654                 data++;
655                 while (*data && (data[0] != '*' || data[1] != '/'))
656                         data++;
657                 data += 2;
658                 goto skipwhite;
659         }
660         else if (*data == '\"')
661         {
662                 // quoted string
663                 for (data++;*data != '\"';data++)
664                 {
665                         if (*data == '\\' && data[1] == '"' )
666                                 data++;
667                         if (!*data || len >= (int)sizeof(com_token) - 1)
668                         {
669                                 com_token[0] = 0;
670                                 *datapointer = NULL;
671                                 return false;
672                         }
673                         com_token[len++] = *data;
674                 }
675                 com_token[len] = 0;
676                 *datapointer = data+1;
677                 return true;
678         }
679         else if (*data == '\'')
680         {
681                 // quoted string
682                 for (data++;*data != '\'';data++)
683                 {
684                         if (*data == '\\' && data[1] == '\'' )
685                                 data++;
686                         if (!*data || len >= (int)sizeof(com_token) - 1)
687                         {
688                                 com_token[0] = 0;
689                                 *datapointer = NULL;
690                                 return false;
691                         }
692                         com_token[len++] = *data;
693                 }
694                 com_token[len] = 0;
695                 *datapointer = data+1;
696                 return true;
697         }
698         else if (*data == '\r')
699         {
700                 // translate Mac line ending to UNIX
701                 com_token[len++] = '\n';
702                 com_token[len] = 0;
703                 *datapointer = data;
704                 return true;
705         }
706         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
707         {
708                 // single character
709                 com_token[len++] = *data++;
710                 com_token[len] = 0;
711                 *datapointer = data;
712                 return true;
713         }
714         else
715         {
716                 // regular word
717                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';' && *data != '\'' && *data != '"';data++)
718                 {
719                         if (len >= (int)sizeof(com_token) - 1)
720                         {
721                                 com_token[0] = 0;
722                                 *datapointer = NULL;
723                                 return false;
724                         }
725                         com_token[len++] = *data;
726                 }
727                 com_token[len] = 0;
728                 *datapointer = data;
729                 return true;
730         }
731 }
732
733 /*
734 ==============
735 COM_ParseTokenConsole
736
737 Parse a token out of a string, behaving like the qwcl console
738 ==============
739 */
740 int COM_ParseTokenConsole(const char **datapointer)
741 {
742         int len;
743         const char *data = *datapointer;
744
745         len = 0;
746         com_token[0] = 0;
747
748         if (!data)
749         {
750                 *datapointer = NULL;
751                 return false;
752         }
753
754 // skip whitespace
755 skipwhite:
756         for (;*data <= ' ';data++)
757         {
758                 if (*data == 0)
759                 {
760                         // end of file
761                         *datapointer = NULL;
762                         return false;
763                 }
764         }
765
766         if (*data == '/' && data[1] == '/')
767         {
768                 // comment
769                 while (*data && *data != '\n' && *data != '\r')
770                         data++;
771                 goto skipwhite;
772         }
773         else if (*data == '\"')
774         {
775                 // quoted string
776                 for (data++;*data != '\"';data++)
777                 {
778                         if (!*data || len >= (int)sizeof(com_token) - 1)
779                         {
780                                 com_token[0] = 0;
781                                 *datapointer = NULL;
782                                 return false;
783                         }
784                         com_token[len++] = *data;
785                 }
786                 com_token[len] = 0;
787                 *datapointer = data+1;
788                 return true;
789         }
790         else
791         {
792                 // regular word
793                 for (;*data > ' ';data++)
794                 {
795                         if (len >= (int)sizeof(com_token) - 1)
796                         {
797                                 com_token[0] = 0;
798                                 *datapointer = NULL;
799                                 return false;
800                         }
801                         com_token[len++] = *data;
802                 }
803                 com_token[len] = 0;
804                 *datapointer = data;
805                 return true;
806         }
807 }
808
809
810 /*
811 ================
812 COM_CheckParm
813
814 Returns the position (1 to argc-1) in the program's argument list
815 where the given parameter apears, or 0 if not present
816 ================
817 */
818 int COM_CheckParm (const char *parm)
819 {
820         int i;
821
822         for (i=1 ; i<com_argc ; i++)
823         {
824                 if (!com_argv[i])
825                         continue;               // NEXTSTEP sometimes clears appkit vars.
826                 if (!strcmp (parm,com_argv[i]))
827                         return i;
828         }
829
830         return 0;
831 }
832
833 /*
834 ================
835 COM_CheckRegistered
836
837 Looks for the pop.txt file and verifies it.
838 Sets the "registered" cvar.
839 Immediately exits out if an alternate game was attempted to be started without
840 being registered.
841 ================
842 */
843 void COM_CheckRegistered (void)
844 {
845         Cvar_Set ("cmdline", com_cmdline);
846
847         if (!FS_FileExists("gfx/pop.lmp"))
848         {
849                 if (fs_modified)
850                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
851                 else
852                         Con_Print("Playing shareware version.\n");
853                 return;
854         }
855
856         Cvar_Set ("registered", "1");
857         Con_Print("Playing registered version.\n");
858 }
859
860
861 /*
862 ================
863 COM_InitArgv
864 ================
865 */
866 void COM_InitArgv (void)
867 {
868         int i, j, n;
869         // reconstitute the command line for the cmdline externally visible cvar
870         n = 0;
871         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
872         {
873                 i = 0;
874                 if (strstr(com_argv[j], " "))
875                 {
876                         // arg contains whitespace, store quotes around it
877                         com_cmdline[n++] = '\"';
878                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
879                                 com_cmdline[n++] = com_argv[j][i++];
880                         com_cmdline[n++] = '\"';
881                 }
882                 else
883                 {
884                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
885                                 com_cmdline[n++] = com_argv[j][i++];
886                 }
887                 if (n < (CMDLINE_LENGTH - 1))
888                         com_cmdline[n++] = ' ';
889                 else
890                         break;
891         }
892         com_cmdline[n] = 0;
893 }
894
895
896 //===========================================================================
897
898 // Game mods
899
900 typedef struct
901 {
902         const char* prog_name;
903         const char* cmdline;
904         const char* gamename;
905         const char* gamedirname1;
906         const char* gamedirname2;
907         const char* gamescreenshotname;
908         const char* gameuserdirname;
909 } gamemode_info_t;
910
911 static const gamemode_info_t gamemode_info [] =
912 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
913
914 // GAME_NORMAL
915 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
916 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
917 // GAME_HIPNOTIC
918 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
919 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
920 // GAME_ROGUE
921 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
922 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
923 // GAME_NEHAHRA
924 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
925 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
926 // GAME_NEXUIZ
927 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
928 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
929 // GAME_TRANSFUSION
930 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
931 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
932 // GAME_GOODVSBAD2
933 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
934 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
935 // GAME_TEU
936 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
937 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
938 // GAME_BATTLEMECH
939 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
940 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
941 // GAME_ZYMOTIC
942 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
943 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         NULL,                   "zymotic",              "zymotic" },
944 // GAME_FNIGGIUM
945 // COMMANDLINEOPTION: Game: -fniggium runs the post apocalyptic melee RPG Fniggium
946 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         NULL,                   "fniggium",             "fniggium" },
947 // GAME_SETHERAL
948 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
949 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
950 // GAME_SOM
951 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
952 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
953 // GAME_TENEBRAE
954 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
955 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
956 // GAME_NEOTERIC
957 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
958 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
959 // GAME_OPENQUARTZ
960 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
961 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces"},
962 // GAME_PRYDON
963 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
964 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"},
965 // GAME_NETHERWORLD
966 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Masters
967 { "netherworld",        "-netherworld", "Dark Masters",                 "id1",          "netherworld",  "nw",                   "darkplaces"},
968 // GAME_THEHUNTED
969 // COMMANDLINEOPTION: Game: -netherworld runs the game The Hunted
970 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"},
971 };
972
973 void COM_InitGameType (void)
974 {
975         char name [MAX_OSPATH];
976         unsigned int i;
977
978         FS_StripExtension (com_argv[0], name, sizeof (name));
979         COM_ToLowerString (name, name, sizeof (name));
980
981         // Check the binary name; default to GAME_NORMAL (0)
982         gamemode = GAME_NORMAL;
983         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
984                 if (strstr (name, gamemode_info[i].prog_name))
985                 {
986                         gamemode = i;
987                         break;
988                 }
989
990         // Look for a command-line option
991         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
992                 if (COM_CheckParm (gamemode_info[i].cmdline))
993                 {
994                         gamemode = i;
995                         break;
996                 }
997
998         gamename = gamemode_info[gamemode].gamename;
999         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1000         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1001         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1002         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1003 }
1004
1005
1006 /*
1007 ================
1008 COM_Init
1009 ================
1010 */
1011 void COM_Init_Commands (void)
1012 {
1013         Cvar_RegisterVariable (&registered);
1014         Cvar_RegisterVariable (&cmdline);
1015 }
1016
1017 /*
1018 ============
1019 va
1020
1021 does a varargs printf into a temp buffer, so I don't need to have
1022 varargs versions of all text functions.
1023 FIXME: make this buffer size safe someday
1024 ============
1025 */
1026 char *va(const char *format, ...)
1027 {
1028         va_list argptr;
1029         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1030         static char string[8][1024], *s;
1031         static int stringindex = 0;
1032
1033         s = string[stringindex];
1034         stringindex = (stringindex + 1) & 7;
1035         va_start (argptr, format);
1036         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1037         va_end (argptr);
1038
1039         return s;
1040 }
1041
1042
1043 //======================================
1044
1045 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1046
1047 #undef snprintf
1048 #undef vsnprintf
1049
1050 #ifdef WIN32
1051 # define snprintf _snprintf
1052 # define vsnprintf _vsnprintf
1053 #endif
1054
1055
1056 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1057 {
1058         va_list args;
1059         int result;
1060
1061         va_start (args, format);
1062         result = dpvsnprintf (buffer, buffersize, format, args);
1063         va_end (args);
1064
1065         return result;
1066 }
1067
1068
1069 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1070 {
1071         int result;
1072
1073         result = vsnprintf (buffer, buffersize, format, args);
1074         if (result < 0 || (size_t)result >= buffersize)
1075         {
1076                 buffer[buffersize - 1] = '\0';
1077                 return -1;
1078         }
1079
1080         return result;
1081 }
1082
1083
1084 //======================================
1085
1086 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1087 {
1088         if (size_out == 0)
1089                 return;
1090
1091         while (*in && size_out > 1)
1092         {
1093                 if (*in >= 'A' && *in <= 'Z')
1094                         *out++ = *in++ + 'a' - 'A';
1095                 else
1096                         *out++ = *in++;
1097                 size_out--;
1098         }
1099         *out = '\0';
1100 }
1101
1102 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1103 {
1104         if (size_out == 0)
1105                 return;
1106
1107         while (*in && size_out > 1)
1108         {
1109                 if (*in >= 'a' && *in <= 'z')
1110                         *out++ = *in++ + 'A' - 'a';
1111                 else
1112                         *out++ = *in++;
1113                 size_out--;
1114         }
1115         *out = '\0';
1116 }
1117
1118 int COM_StringBeginsWith(const char *s, const char *match)
1119 {
1120         for (;*s && *match;s++, match++)
1121                 if (*s != *match)
1122                         return false;
1123         return true;
1124 }
1125
1126 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1127 {
1128         int argc, commentprefixlength;
1129         char *tokenbufend;
1130         const char *l;
1131         argc = 0;
1132         tokenbufend = tokenbuf + tokenbufsize;
1133         l = *text;
1134         commentprefixlength = 0;
1135         if (commentprefix)
1136                 commentprefixlength = strlen(commentprefix);
1137         while (*l && *l != '\n' && *l != '\r')
1138         {
1139                 if (*l > ' ')
1140                 {
1141                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1142                         {
1143                                 while (*l && *l != '\n' && *l != '\r')
1144                                         l++;
1145                                 break;
1146                         }
1147                         if (argc >= maxargc)
1148                                 return -1;
1149                         argv[argc++] = tokenbuf;
1150                         if (*l == '"')
1151                         {
1152                                 l++;
1153                                 while (*l && *l != '"')
1154                                 {
1155                                         if (tokenbuf >= tokenbufend)
1156                                                 return -1;
1157                                         *tokenbuf++ = *l++;
1158                                 }
1159                                 if (*l == '"')
1160                                         l++;
1161                         }
1162                         else
1163                         {
1164                                 while (*l > ' ')
1165                                 {
1166                                         if (tokenbuf >= tokenbufend)
1167                                                 return -1;
1168                                         *tokenbuf++ = *l++;
1169                                 }
1170                         }
1171                         if (tokenbuf >= tokenbufend)
1172                                 return -1;
1173                         *tokenbuf++ = 0;
1174                 }
1175                 else
1176                         l++;
1177         }
1178         // line endings:
1179         // UNIX: \n
1180         // Mac: \r
1181         // Windows: \r\n
1182         if (*l == '\r')
1183                 l++;
1184         if (*l == '\n')
1185                 l++;
1186         *text = l;
1187         return argc;
1188 }
1189
1190 // written by Elric, thanks Elric!
1191 char *SearchInfostring(const char *infostring, const char *key)
1192 {
1193         static char value [256];
1194         char crt_key [256];
1195         size_t value_ind, key_ind;
1196         char c;
1197
1198         if (*infostring++ != '\\')
1199                 return NULL;
1200
1201         value_ind = 0;
1202         for (;;)
1203         {
1204                 key_ind = 0;
1205
1206                 // Get the key name
1207                 for (;;)
1208                 {
1209                         c = *infostring++;
1210
1211                         if (c == '\0')
1212                                 return NULL;
1213                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1214                         {
1215                                 crt_key[key_ind] = '\0';
1216                                 break;
1217                         }
1218
1219                         crt_key[key_ind++] = c;
1220                 }
1221
1222                 // If it's the key we are looking for, save it in "value"
1223                 if (!strcmp(crt_key, key))
1224                 {
1225                         for (;;)
1226                         {
1227                                 c = *infostring++;
1228
1229                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1230                                 {
1231                                         value[value_ind] = '\0';
1232                                         return value;
1233                                 }
1234
1235                                 value[value_ind++] = c;
1236                         }
1237                 }
1238
1239                 // Else, skip the value
1240                 for (;;)
1241                 {
1242                         c = *infostring++;
1243
1244                         if (c == '\0')
1245                                 return NULL;
1246                         if (c == '\\')
1247                                 break;
1248                 }
1249         }
1250 }
1251
1252
1253 //========================================================
1254 // strlcat and strlcpy, from OpenBSD
1255
1256 /*
1257  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1258  *
1259  * Permission to use, copy, modify, and distribute this software for any
1260  * purpose with or without fee is hereby granted, provided that the above
1261  * copyright notice and this permission notice appear in all copies.
1262  *
1263  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1264  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1265  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1266  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1267  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1268  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1269  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1270  */
1271
1272 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1273 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1274
1275
1276 #ifndef HAVE_STRLCAT
1277 size_t
1278 strlcat(char *dst, const char *src, size_t siz)
1279 {
1280         register char *d = dst;
1281         register const char *s = src;
1282         register size_t n = siz;
1283         size_t dlen;
1284
1285         /* Find the end of dst and adjust bytes left but don't go past end */
1286         while (n-- != 0 && *d != '\0')
1287                 d++;
1288         dlen = d - dst;
1289         n = siz - dlen;
1290
1291         if (n == 0)
1292                 return(dlen + strlen(s));
1293         while (*s != '\0') {
1294                 if (n != 1) {
1295                         *d++ = *s;
1296                         n--;
1297                 }
1298                 s++;
1299         }
1300         *d = '\0';
1301
1302         return(dlen + (s - src));       /* count does not include NUL */
1303 }
1304 #endif  // #ifndef HAVE_STRLCAT
1305
1306
1307 #ifndef HAVE_STRLCPY
1308 size_t
1309 strlcpy(char *dst, const char *src, size_t siz)
1310 {
1311         register char *d = dst;
1312         register const char *s = src;
1313         register size_t n = siz;
1314
1315         /* Copy as many bytes as will fit */
1316         if (n != 0 && --n != 0) {
1317                 do {
1318                         if ((*d++ = *s++) == 0)
1319                                 break;
1320                 } while (--n != 0);
1321         }
1322
1323         /* Not enough room in dst, add NUL and traverse rest of src */
1324         if (n == 0) {
1325                 if (siz != 0)
1326                         *d = '\0';              /* NUL-terminate dst */
1327                 while (*s++)
1328                         ;
1329         }
1330
1331         return(s - src - 1);    /* count does not include NUL */
1332 }
1333
1334 #endif  // #ifndef HAVE_STRLCPY