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