]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
Apparently this shuts 'assignment in conditional' warnings up
[xonotic/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, size_t 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, (int)strlen(s)+1);
253 }
254
255 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
256 {
257         if (s)
258                 SZ_Write (sb, s, (int)strlen(s));
259 }
260
261 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
262 {
263         if (f >= 0)
264                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
265         else
266                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
267 }
268
269 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
270 {
271         if (f >= 0)
272                 MSG_WriteShort (sb, (int)(f + 0.5));
273         else
274                 MSG_WriteShort (sb, (int)(f - 0.5));
275 }
276
277 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
278 {
279         MSG_WriteFloat (sb, f);
280 }
281
282 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
283 {
284         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
285                 MSG_WriteCoord13i (sb, f);
286         else if (protocol == PROTOCOL_DARKPLACES1)
287                 MSG_WriteCoord32f (sb, f);
288         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
289                 MSG_WriteCoord16i (sb, f);
290         else
291                 MSG_WriteCoord32f (sb, f);
292 }
293
294 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
295 {
296         MSG_WriteCoord (sb, v[0], protocol);
297         MSG_WriteCoord (sb, v[1], protocol);
298         MSG_WriteCoord (sb, v[2], protocol);
299 }
300
301 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
302 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
303 {
304         if (f >= 0)
305                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
306         else
307                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
308 }
309
310 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
311 {
312         if (f >= 0)
313                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
314         else
315                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
316 }
317
318 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
319 {
320         MSG_WriteFloat (sb, f);
321 }
322
323 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
324 {
325         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
326                 MSG_WriteAngle8i (sb, f);
327         else
328                 MSG_WriteAngle16i (sb, f);
329 }
330
331 //
332 // reading functions
333 //
334 int msg_readcount;
335 qboolean msg_badread;
336
337 void MSG_BeginReading (void)
338 {
339         msg_readcount = 0;
340         msg_badread = false;
341 }
342
343 int MSG_ReadLittleShort (void)
344 {
345         if (msg_readcount+2 > net_message.cursize)
346         {
347                 msg_badread = true;
348                 return -1;
349         }
350         msg_readcount += 2;
351         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
352 }
353
354 int MSG_ReadBigShort (void)
355 {
356         if (msg_readcount+2 > net_message.cursize)
357         {
358                 msg_badread = true;
359                 return -1;
360         }
361         msg_readcount += 2;
362         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
363 }
364
365 int MSG_ReadLittleLong (void)
366 {
367         if (msg_readcount+4 > net_message.cursize)
368         {
369                 msg_badread = true;
370                 return -1;
371         }
372         msg_readcount += 4;
373         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);
374 }
375
376 int MSG_ReadBigLong (void)
377 {
378         if (msg_readcount+4 > net_message.cursize)
379         {
380                 msg_badread = true;
381                 return -1;
382         }
383         msg_readcount += 4;
384         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];
385 }
386
387 float MSG_ReadLittleFloat (void)
388 {
389         union
390         {
391                 float f;
392                 int l;
393         } dat;
394         if (msg_readcount+4 > net_message.cursize)
395         {
396                 msg_badread = true;
397                 return -1;
398         }
399         msg_readcount += 4;
400         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);
401         return dat.f;
402 }
403
404 float MSG_ReadBigFloat (void)
405 {
406         union
407         {
408                 float f;
409                 int l;
410         } dat;
411         if (msg_readcount+4 > net_message.cursize)
412         {
413                 msg_badread = true;
414                 return -1;
415         }
416         msg_readcount += 4;
417         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];
418         return dat.f;
419 }
420
421 char *MSG_ReadString (void)
422 {
423         static char string[2048];
424         int l,c;
425         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
426                 string[l] = c;
427         string[l] = 0;
428         return string;
429 }
430
431 int MSG_ReadBytes (int numbytes, unsigned char *out)
432 {
433         int l, c;
434         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
435                 out[l] = c;
436         return l;
437 }
438
439 float MSG_ReadCoord13i (void)
440 {
441         return MSG_ReadLittleShort() * (1.0/8.0);
442 }
443
444 float MSG_ReadCoord16i (void)
445 {
446         return (signed short) MSG_ReadLittleShort();
447 }
448
449 float MSG_ReadCoord32f (void)
450 {
451         return MSG_ReadLittleFloat();
452 }
453
454 float MSG_ReadCoord (protocolversion_t protocol)
455 {
456         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE)
457                 return MSG_ReadCoord13i();
458         else if (protocol == PROTOCOL_DARKPLACES1)
459                 return MSG_ReadCoord32f();
460         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
461                 return MSG_ReadCoord16i();
462         else
463                 return MSG_ReadCoord32f();
464 }
465
466 void MSG_ReadVector (float *v, protocolversion_t protocol)
467 {
468         v[0] = MSG_ReadCoord(protocol);
469         v[1] = MSG_ReadCoord(protocol);
470         v[2] = MSG_ReadCoord(protocol);
471 }
472
473 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
474 float MSG_ReadAngle8i (void)
475 {
476         return (signed char) MSG_ReadByte () * (360.0/256.0);
477 }
478
479 float MSG_ReadAngle16i (void)
480 {
481         return (signed short)MSG_ReadShort () * (360.0/65536.0);
482 }
483
484 float MSG_ReadAngle32f (void)
485 {
486         return MSG_ReadFloat ();
487 }
488
489 float MSG_ReadAngle (protocolversion_t protocol)
490 {
491         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
492                 return MSG_ReadAngle8i ();
493         else
494                 return MSG_ReadAngle16i ();
495 }
496
497
498 //===========================================================================
499
500 void SZ_Clear (sizebuf_t *buf)
501 {
502         buf->cursize = 0;
503 }
504
505 void *SZ_GetSpace (sizebuf_t *buf, int length)
506 {
507         void *data;
508
509         if (buf->cursize + length > buf->maxsize)
510         {
511                 if (!buf->allowoverflow)
512                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
513
514                 if (length > buf->maxsize)
515                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
516
517                 buf->overflowed = true;
518                 Con_Print("SZ_GetSpace: overflow\n");
519                 SZ_Clear (buf);
520         }
521
522         data = buf->data + buf->cursize;
523         buf->cursize += length;
524
525         return data;
526 }
527
528 void SZ_Write (sizebuf_t *buf, const void *data, int length)
529 {
530         memcpy (SZ_GetSpace(buf,length),data,length);
531 }
532
533 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
534 // attention, it has been eradicated from here, its only (former) use in
535 // all of darkplaces.
536
537 static char *hexchar = "0123456789ABCDEF";
538 void Com_HexDumpToConsole(const qbyte *data, int size)
539 {
540         int i, j, n;
541         char text[1024];
542         char *cur, *flushpointer;
543         const qbyte *d;
544         cur = text;
545         flushpointer = text + 512;
546         for (i = 0;i < size;)
547         {
548                 n = 16;
549                 if (n > size - i)
550                         n = size - i;
551                 d = data + i;
552                 // print offset
553                 *cur++ = hexchar[(i >> 12) & 15];
554                 *cur++ = hexchar[(i >>  8) & 15];
555                 *cur++ = hexchar[(i >>  4) & 15];
556                 *cur++ = hexchar[(i >>  0) & 15];
557                 *cur++ = ':';
558                 // print hex
559                 for (j = 0;j < 16;j++)
560                 {
561                         if (j < n)
562                         {
563                                 *cur++ = hexchar[(d[j] >> 4) & 15];
564                                 *cur++ = hexchar[(d[j] >> 0) & 15];
565                         }
566                         else
567                         {
568                                 *cur++ = ' ';
569                                 *cur++ = ' ';
570                         }
571                         if ((j & 3) == 3)
572                                 *cur++ = ' ';
573                 }
574                 // print text
575                 for (j = 0;j < 16;j++)
576                 {
577                         if (j < n)
578                         {
579                                 if (d[j] >= ' ' && d[j] <= 127)
580                                         *cur++ = d[j];
581                                 else
582                                         *cur++ = '.';
583                         }
584                         else
585                                 *cur++ = ' ';
586                 }
587                 *cur++ = '\n';
588                 i += n;
589                 if (cur >= flushpointer || i >= size)
590                 {
591                         *cur++ = 0;
592                         Con_Print(text);
593                         cur = text;
594                 }
595         }
596 }
597
598 void SZ_HexDumpToConsole(const sizebuf_t *buf)
599 {
600         Com_HexDumpToConsole(buf->data, buf->cursize);
601 }
602
603
604 //============================================================================
605
606
607 /*
608 ==============
609 COM_ParseToken
610
611 Parse a token out of a string
612 ==============
613 */
614 int COM_ParseToken(const char **datapointer, int returnnewline)
615 {
616         int len;
617         const char *data = *datapointer;
618
619         len = 0;
620         com_token[0] = 0;
621
622         if (!data)
623         {
624                 *datapointer = NULL;
625                 return false;
626         }
627
628 // skip whitespace
629 skipwhite:
630         // line endings:
631         // UNIX: \n
632         // Mac: \r
633         // Windows: \r\n
634         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
635         {
636                 if (*data == 0)
637                 {
638                         // end of file
639                         *datapointer = NULL;
640                         return false;
641                 }
642         }
643
644         // handle Windows line ending
645         if (data[0] == '\r' && data[1] == '\n')
646                 data++;
647
648         if (data[0] == '/' && data[1] == '/')
649         {
650                 // comment
651                 while (*data && *data != '\n' && *data != '\r')
652                         data++;
653                 goto skipwhite;
654         }
655         else if (data[0] == '/' && data[1] == '*')
656         {
657                 // comment
658                 data++;
659                 while (*data && (data[0] != '*' || data[1] != '/'))
660                         data++;
661                 data += 2;
662                 goto skipwhite;
663         }
664         else if (*data == '\"')
665         {
666                 // quoted string
667                 for (data++;*data != '\"';data++)
668                 {
669                         if (*data == '\\' && data[1] == '"' )
670                                 data++;
671                         if (!*data || len >= (int)sizeof(com_token) - 1)
672                         {
673                                 com_token[0] = 0;
674                                 *datapointer = NULL;
675                                 return false;
676                         }
677                         com_token[len++] = *data;
678                 }
679                 com_token[len] = 0;
680                 *datapointer = data+1;
681                 return true;
682         }
683         else if (*data == '\'')
684         {
685                 // quoted string
686                 for (data++;*data != '\'';data++)
687                 {
688                         if (*data == '\\' && data[1] == '\'' )
689                                 data++;
690                         if (!*data || len >= (int)sizeof(com_token) - 1)
691                         {
692                                 com_token[0] = 0;
693                                 *datapointer = NULL;
694                                 return false;
695                         }
696                         com_token[len++] = *data;
697                 }
698                 com_token[len] = 0;
699                 *datapointer = data+1;
700                 return true;
701         }
702         else if (*data == '\r')
703         {
704                 // translate Mac line ending to UNIX
705                 com_token[len++] = '\n';
706                 com_token[len] = 0;
707                 *datapointer = data;
708                 return true;
709         }
710         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
711         {
712                 // single character
713                 com_token[len++] = *data++;
714                 com_token[len] = 0;
715                 *datapointer = data;
716                 return true;
717         }
718         else
719         {
720                 // regular word
721                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';' && *data != '\'' && *data != '"';data++)
722                 {
723                         if (len >= (int)sizeof(com_token) - 1)
724                         {
725                                 com_token[0] = 0;
726                                 *datapointer = NULL;
727                                 return false;
728                         }
729                         com_token[len++] = *data;
730                 }
731                 com_token[len] = 0;
732                 *datapointer = data;
733                 return true;
734         }
735 }
736
737 /*
738 ==============
739 COM_ParseTokenConsole
740
741 Parse a token out of a string, behaving like the qwcl console
742 ==============
743 */
744 int COM_ParseTokenConsole(const char **datapointer)
745 {
746         int len;
747         const char *data = *datapointer;
748
749         len = 0;
750         com_token[0] = 0;
751
752         if (!data)
753         {
754                 *datapointer = NULL;
755                 return false;
756         }
757
758 // skip whitespace
759 skipwhite:
760         for (;*data <= ' ';data++)
761         {
762                 if (*data == 0)
763                 {
764                         // end of file
765                         *datapointer = NULL;
766                         return false;
767                 }
768         }
769
770         if (*data == '/' && data[1] == '/')
771         {
772                 // comment
773                 while (*data && *data != '\n' && *data != '\r')
774                         data++;
775                 goto skipwhite;
776         }
777         else if (*data == '\"')
778         {
779                 // quoted string
780                 for (data++;*data != '\"';data++)
781                 {
782                         if (!*data || len >= (int)sizeof(com_token) - 1)
783                         {
784                                 com_token[0] = 0;
785                                 *datapointer = NULL;
786                                 return false;
787                         }
788                         com_token[len++] = *data;
789                 }
790                 com_token[len] = 0;
791                 *datapointer = data+1;
792         }
793         else
794         {
795                 // regular word
796                 for (;*data > ' ';data++)
797                 {
798                         if (len >= (int)sizeof(com_token) - 1)
799                         {
800                                 com_token[0] = 0;
801                                 *datapointer = NULL;
802                                 return false;
803                         }
804                         com_token[len++] = *data;
805                 }
806                 com_token[len] = 0;
807                 *datapointer = data;
808         }
809
810         return true;
811 }
812
813
814 /*
815 ================
816 COM_CheckParm
817
818 Returns the position (1 to argc-1) in the program's argument list
819 where the given parameter apears, or 0 if not present
820 ================
821 */
822 int COM_CheckParm (const char *parm)
823 {
824         int i;
825
826         for (i=1 ; i<com_argc ; i++)
827         {
828                 if (!com_argv[i])
829                         continue;               // NEXTSTEP sometimes clears appkit vars.
830                 if (!strcmp (parm,com_argv[i]))
831                         return i;
832         }
833
834         return 0;
835 }
836
837 /*
838 ================
839 COM_CheckRegistered
840
841 Looks for the pop.txt file and verifies it.
842 Sets the "registered" cvar.
843 Immediately exits out if an alternate game was attempted to be started without
844 being registered.
845 ================
846 */
847 void COM_CheckRegistered (void)
848 {
849         Cvar_Set ("cmdline", com_cmdline);
850
851         if (gamemode == GAME_NORMAL && !FS_FileExists("gfx/pop.lmp"))
852         {
853                 if (fs_modified)
854                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
855                 else
856                         Con_Print("Playing shareware version.\n");
857                 return;
858         }
859
860         Cvar_Set ("registered", "1");
861         Con_Print("Playing registered version.\n");
862 }
863
864
865 /*
866 ================
867 COM_InitArgv
868 ================
869 */
870 void COM_InitArgv (void)
871 {
872         int i, j, n;
873         // reconstitute the command line for the cmdline externally visible cvar
874         n = 0;
875         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
876         {
877                 i = 0;
878                 if (strstr(com_argv[j], " "))
879                 {
880                         // arg contains whitespace, store quotes around it
881                         com_cmdline[n++] = '\"';
882                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
883                                 com_cmdline[n++] = com_argv[j][i++];
884                         com_cmdline[n++] = '\"';
885                 }
886                 else
887                 {
888                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
889                                 com_cmdline[n++] = com_argv[j][i++];
890                 }
891                 if (n < (CMDLINE_LENGTH - 1))
892                         com_cmdline[n++] = ' ';
893                 else
894                         break;
895         }
896         com_cmdline[n] = 0;
897 }
898
899
900 //===========================================================================
901
902 // Game mods
903
904 typedef struct
905 {
906         const char* prog_name;
907         const char* cmdline;
908         const char* gamename;
909         const char* gamedirname1;
910         const char* gamedirname2;
911         const char* gamescreenshotname;
912         const char* gameuserdirname;
913 } gamemode_info_t;
914
915 static const gamemode_info_t gamemode_info [] =
916 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
917
918 // GAME_NORMAL
919 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
920 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
921 // GAME_HIPNOTIC
922 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
923 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
924 // GAME_ROGUE
925 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
926 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
927 // GAME_NEHAHRA
928 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
929 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
930 // GAME_NEXUIZ
931 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
932 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
933 // GAME_TRANSFUSION
934 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
935 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
936 // GAME_GOODVSBAD2
937 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
938 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
939 // GAME_TEU
940 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
941 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
942 // GAME_BATTLEMECH
943 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
944 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
945 // GAME_ZYMOTIC
946 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
947 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         NULL,                   "zymotic",              "zymotic" },
948 // GAME_FNIGGIUM
949 // COMMANDLINEOPTION: Game: -fniggium runs the post apocalyptic melee RPG Fniggium
950 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         NULL,                   "fniggium",             "fniggium" },
951 // GAME_SETHERAL
952 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
953 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
954 // GAME_SOM
955 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
956 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
957 // GAME_TENEBRAE
958 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
959 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
960 // GAME_NEOTERIC
961 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
962 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
963 // GAME_OPENQUARTZ
964 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
965 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces"},
966 // GAME_PRYDON
967 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
968 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"},
969 // GAME_NETHERWORLD
970 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master
971 { "netherworld",        "-netherworld", "Netherworld: Dark Master",     "id1",          "netherworld",  "nw",                   "darkplaces"},
972 // GAME_THEHUNTED
973 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
974 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"},
975 };
976
977 void COM_InitGameType (void)
978 {
979         char name [MAX_OSPATH];
980         unsigned int i;
981
982         FS_StripExtension (com_argv[0], name, sizeof (name));
983         COM_ToLowerString (name, name, sizeof (name));
984
985         // Check the binary name; default to GAME_NORMAL (0)
986         gamemode = GAME_NORMAL;
987         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
988                 if (strstr (name, gamemode_info[i].prog_name))
989                 {
990                         gamemode = i;
991                         break;
992                 }
993
994         // Look for a command-line option
995         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
996                 if (COM_CheckParm (gamemode_info[i].cmdline))
997                 {
998                         gamemode = i;
999                         break;
1000                 }
1001
1002         gamename = gamemode_info[gamemode].gamename;
1003         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1004         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1005         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1006         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1007 }
1008
1009
1010 /*
1011 ================
1012 COM_Init
1013 ================
1014 */
1015 void COM_Init_Commands (void)
1016 {
1017         Cvar_RegisterVariable (&registered);
1018         Cvar_RegisterVariable (&cmdline);
1019 }
1020
1021 /*
1022 ============
1023 va
1024
1025 does a varargs printf into a temp buffer, so I don't need to have
1026 varargs versions of all text functions.
1027 FIXME: make this buffer size safe someday
1028 ============
1029 */
1030 char *va(const char *format, ...)
1031 {
1032         va_list argptr;
1033         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1034         static char string[8][1024], *s;
1035         static int stringindex = 0;
1036
1037         s = string[stringindex];
1038         stringindex = (stringindex + 1) & 7;
1039         va_start (argptr, format);
1040         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1041         va_end (argptr);
1042
1043         return s;
1044 }
1045
1046
1047 //======================================
1048
1049 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1050
1051 #undef snprintf
1052 #undef vsnprintf
1053
1054 #ifdef WIN32
1055 # define snprintf _snprintf
1056 # define vsnprintf _vsnprintf
1057 #endif
1058
1059
1060 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1061 {
1062         va_list args;
1063         int result;
1064
1065         va_start (args, format);
1066         result = dpvsnprintf (buffer, buffersize, format, args);
1067         va_end (args);
1068
1069         return result;
1070 }
1071
1072
1073 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1074 {
1075         int result;
1076
1077         result = vsnprintf (buffer, buffersize, format, args);
1078         if (result < 0 || (size_t)result >= buffersize)
1079         {
1080                 buffer[buffersize - 1] = '\0';
1081                 return -1;
1082         }
1083
1084         return result;
1085 }
1086
1087
1088 //======================================
1089
1090 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1091 {
1092         if (size_out == 0)
1093                 return;
1094
1095         while (*in && size_out > 1)
1096         {
1097                 if (*in >= 'A' && *in <= 'Z')
1098                         *out++ = *in++ + 'a' - 'A';
1099                 else
1100                         *out++ = *in++;
1101                 size_out--;
1102         }
1103         *out = '\0';
1104 }
1105
1106 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1107 {
1108         if (size_out == 0)
1109                 return;
1110
1111         while (*in && size_out > 1)
1112         {
1113                 if (*in >= 'a' && *in <= 'z')
1114                         *out++ = *in++ + 'A' - 'a';
1115                 else
1116                         *out++ = *in++;
1117                 size_out--;
1118         }
1119         *out = '\0';
1120 }
1121
1122 int COM_StringBeginsWith(const char *s, const char *match)
1123 {
1124         for (;*s && *match;s++, match++)
1125                 if (*s != *match)
1126                         return false;
1127         return true;
1128 }
1129
1130 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1131 {
1132         int argc, commentprefixlength;
1133         char *tokenbufend;
1134         const char *l;
1135         argc = 0;
1136         tokenbufend = tokenbuf + tokenbufsize;
1137         l = *text;
1138         commentprefixlength = 0;
1139         if (commentprefix)
1140                 commentprefixlength = (int)strlen(commentprefix);
1141         while (*l && *l != '\n' && *l != '\r')
1142         {
1143                 if (*l > ' ')
1144                 {
1145                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1146                         {
1147                                 while (*l && *l != '\n' && *l != '\r')
1148                                         l++;
1149                                 break;
1150                         }
1151                         if (argc >= maxargc)
1152                                 return -1;
1153                         argv[argc++] = tokenbuf;
1154                         if (*l == '"')
1155                         {
1156                                 l++;
1157                                 while (*l && *l != '"')
1158                                 {
1159                                         if (tokenbuf >= tokenbufend)
1160                                                 return -1;
1161                                         *tokenbuf++ = *l++;
1162                                 }
1163                                 if (*l == '"')
1164                                         l++;
1165                         }
1166                         else
1167                         {
1168                                 while (*l > ' ')
1169                                 {
1170                                         if (tokenbuf >= tokenbufend)
1171                                                 return -1;
1172                                         *tokenbuf++ = *l++;
1173                                 }
1174                         }
1175                         if (tokenbuf >= tokenbufend)
1176                                 return -1;
1177                         *tokenbuf++ = 0;
1178                 }
1179                 else
1180                         l++;
1181         }
1182         // line endings:
1183         // UNIX: \n
1184         // Mac: \r
1185         // Windows: \r\n
1186         if (*l == '\r')
1187                 l++;
1188         if (*l == '\n')
1189                 l++;
1190         *text = l;
1191         return argc;
1192 }
1193
1194 // written by Elric, thanks Elric!
1195 char *SearchInfostring(const char *infostring, const char *key)
1196 {
1197         static char value [256];
1198         char crt_key [256];
1199         size_t value_ind, key_ind;
1200         char c;
1201
1202         if (*infostring++ != '\\')
1203                 return NULL;
1204
1205         value_ind = 0;
1206         for (;;)
1207         {
1208                 key_ind = 0;
1209
1210                 // Get the key name
1211                 for (;;)
1212                 {
1213                         c = *infostring++;
1214
1215                         if (c == '\0')
1216                                 return NULL;
1217                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1218                         {
1219                                 crt_key[key_ind] = '\0';
1220                                 break;
1221                         }
1222
1223                         crt_key[key_ind++] = c;
1224                 }
1225
1226                 // If it's the key we are looking for, save it in "value"
1227                 if (!strcmp(crt_key, key))
1228                 {
1229                         for (;;)
1230                         {
1231                                 c = *infostring++;
1232
1233                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1234                                 {
1235                                         value[value_ind] = '\0';
1236                                         return value;
1237                                 }
1238
1239                                 value[value_ind++] = c;
1240                         }
1241                 }
1242
1243                 // Else, skip the value
1244                 for (;;)
1245                 {
1246                         c = *infostring++;
1247
1248                         if (c == '\0')
1249                                 return NULL;
1250                         if (c == '\\')
1251                                 break;
1252                 }
1253         }
1254 }
1255
1256
1257 //========================================================
1258 // strlcat and strlcpy, from OpenBSD
1259
1260 /*
1261  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1262  *
1263  * Permission to use, copy, modify, and distribute this software for any
1264  * purpose with or without fee is hereby granted, provided that the above
1265  * copyright notice and this permission notice appear in all copies.
1266  *
1267  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1268  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1269  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1270  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1271  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1272  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1273  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1274  */
1275
1276 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1277 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1278
1279
1280 #ifndef HAVE_STRLCAT
1281 size_t
1282 strlcat(char *dst, const char *src, size_t siz)
1283 {
1284         register char *d = dst;
1285         register const char *s = src;
1286         register size_t n = siz;
1287         size_t dlen;
1288
1289         /* Find the end of dst and adjust bytes left but don't go past end */
1290         while (n-- != 0 && *d != '\0')
1291                 d++;
1292         dlen = d - dst;
1293         n = siz - dlen;
1294
1295         if (n == 0)
1296                 return(dlen + strlen(s));
1297         while (*s != '\0') {
1298                 if (n != 1) {
1299                         *d++ = *s;
1300                         n--;
1301                 }
1302                 s++;
1303         }
1304         *d = '\0';
1305
1306         return(dlen + (s - src));       /* count does not include NUL */
1307 }
1308 #endif  // #ifndef HAVE_STRLCAT
1309
1310
1311 #ifndef HAVE_STRLCPY
1312 size_t
1313 strlcpy(char *dst, const char *src, size_t siz)
1314 {
1315         register char *d = dst;
1316         register const char *s = src;
1317         register size_t n = siz;
1318
1319         /* Copy as many bytes as will fit */
1320         if (n != 0 && --n != 0) {
1321                 do {
1322                         if ((*d++ = *s++) == 0)
1323                                 break;
1324                 } while (--n != 0);
1325         }
1326
1327         /* Not enough room in dst, add NUL and traverse rest of src */
1328         if (n == 0) {
1329                 if (siz != 0)
1330                         *d = '\0';              /* NUL-terminate dst */
1331                 while (*s++)
1332                         ;
1333         }
1334
1335         return(s - src - 1);    /* count does not include NUL */
1336 }
1337
1338 #endif  // #ifndef HAVE_STRLCPY