consolidated many mempools to make memlist more readable (and very slightly reduce...
[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(qbyte *data, int size)
175 {
176         unsigned short crc = CRC_INIT_VALUE;
177         while (size--)
178                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
179         return crc ^ CRC_XOR_VALUE;
180 }
181
182
183 /*
184 ==============================================================================
185
186                         MESSAGE IO FUNCTIONS
187
188 Handles byte ordering and avoids alignment errors
189 ==============================================================================
190 */
191
192 //
193 // writing functions
194 //
195
196 void MSG_WriteChar (sizebuf_t *sb, int c)
197 {
198         qbyte    *buf;
199
200         buf = SZ_GetSpace (sb, 1);
201         buf[0] = c;
202 }
203
204 void MSG_WriteByte (sizebuf_t *sb, int c)
205 {
206         qbyte    *buf;
207
208         buf = SZ_GetSpace (sb, 1);
209         buf[0] = c;
210 }
211
212 void MSG_WriteShort (sizebuf_t *sb, int c)
213 {
214         qbyte    *buf;
215
216         buf = SZ_GetSpace (sb, 2);
217         buf[0] = c&0xff;
218         buf[1] = c>>8;
219 }
220
221 void MSG_WriteLong (sizebuf_t *sb, int c)
222 {
223         qbyte    *buf;
224
225         buf = SZ_GetSpace (sb, 4);
226         buf[0] = c&0xff;
227         buf[1] = (c>>8)&0xff;
228         buf[2] = (c>>16)&0xff;
229         buf[3] = c>>24;
230 }
231
232 void MSG_WriteFloat (sizebuf_t *sb, float f)
233 {
234         union
235         {
236                 float   f;
237                 int     l;
238         } dat;
239
240
241         dat.f = f;
242         dat.l = LittleLong (dat.l);
243
244         SZ_Write (sb, &dat.l, 4);
245 }
246
247 void MSG_WriteString (sizebuf_t *sb, const char *s)
248 {
249         if (!s)
250                 SZ_Write (sb, "", 1);
251         else
252                 SZ_Write (sb, s, strlen(s)+1);
253 }
254
255 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
256 {
257         if (f >= 0)
258                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
259         else
260                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
261 }
262
263 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
264 {
265         if (f >= 0)
266                 MSG_WriteShort (sb, (int)(f + 0.5));
267         else
268                 MSG_WriteShort (sb, (int)(f - 0.5));
269 }
270
271 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
272 {
273         MSG_WriteFloat (sb, f);
274 }
275
276 void MSG_WriteCoord (sizebuf_t *sb, float f, int protocol)
277 {
278         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_NEHAHRAMOVIE)
279                 MSG_WriteCoord13i (sb, f);
280         else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6)
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                 Host_Error("MSG_WriteCoord: unknown protocol\n");
286 }
287
288 void MSG_WriteVector (sizebuf_t *sb, float *v, int 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, int protocol)
318 {
319         if (protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6)
320                 MSG_WriteAngle16i (sb, f);
321         else
322                 MSG_WriteAngle8i (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 (int protocol)
449 {
450         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_NEHAHRAMOVIE)
451                 return MSG_ReadCoord13i();
452         else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6)
453                 return MSG_ReadCoord32f();
454         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
455                 return MSG_ReadCoord16i();
456         Host_Error("MSG_ReadCoord: unknown protocol\n");
457         return 0;
458 }
459
460 void MSG_ReadVector (float *v, int 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 (int protocol)
484 {
485         if (protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6)
486                 return MSG_ReadAngle16i ();
487         else
488                 return MSG_ReadAngle8i ();
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                 return true;
787         }
788         else
789         {
790                 // regular word
791                 for (;*data > ' ';data++)
792                 {
793                         if (len >= (int)sizeof(com_token) - 1)
794                         {
795                                 com_token[0] = 0;
796                                 *datapointer = NULL;
797                                 return false;
798                         }
799                         com_token[len++] = *data;
800                 }
801                 com_token[len] = 0;
802                 *datapointer = data;
803                 return true;
804         }
805 }
806
807
808 /*
809 ================
810 COM_CheckParm
811
812 Returns the position (1 to argc-1) in the program's argument list
813 where the given parameter apears, or 0 if not present
814 ================
815 */
816 int COM_CheckParm (const char *parm)
817 {
818         int i;
819
820         for (i=1 ; i<com_argc ; i++)
821         {
822                 if (!com_argv[i])
823                         continue;               // NEXTSTEP sometimes clears appkit vars.
824                 if (!strcmp (parm,com_argv[i]))
825                         return i;
826         }
827
828         return 0;
829 }
830
831 /*
832 ================
833 COM_CheckRegistered
834
835 Looks for the pop.txt file and verifies it.
836 Sets the "registered" cvar.
837 Immediately exits out if an alternate game was attempted to be started without
838 being registered.
839 ================
840 */
841 void COM_CheckRegistered (void)
842 {
843         Cvar_Set ("cmdline", com_cmdline);
844
845         if (!FS_FileExists("gfx/pop.lmp"))
846         {
847                 if (fs_modified)
848                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
849                 else
850                         Con_Print("Playing shareware version.\n");
851                 return;
852         }
853
854         Cvar_Set ("registered", "1");
855         Con_Print("Playing registered version.\n");
856 }
857
858
859 /*
860 ================
861 COM_InitArgv
862 ================
863 */
864 void COM_InitArgv (void)
865 {
866         int i, j, n;
867         // reconstitute the command line for the cmdline externally visible cvar
868         n = 0;
869         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
870         {
871                 i = 0;
872                 if (strstr(com_argv[j], " "))
873                 {
874                         // arg contains whitespace, store quotes around it
875                         com_cmdline[n++] = '\"';
876                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
877                                 com_cmdline[n++] = com_argv[j][i++];
878                         com_cmdline[n++] = '\"';
879                 }
880                 else
881                 {
882                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
883                                 com_cmdline[n++] = com_argv[j][i++];
884                 }
885                 if (n < (CMDLINE_LENGTH - 1))
886                         com_cmdline[n++] = ' ';
887                 else
888                         break;
889         }
890         com_cmdline[n] = 0;
891 }
892
893
894 //===========================================================================
895
896 // Game mods
897
898 typedef struct
899 {
900         const char* prog_name;
901         const char* cmdline;
902         const char* gamename;
903         const char* gamedirname1;
904         const char* gamedirname2;
905         const char* gamescreenshotname;
906         const char* gameuserdirname;
907 } gamemode_info_t;
908
909 static const gamemode_info_t gamemode_info [] =
910 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
911
912 // GAME_NORMAL
913 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
914 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
915 // GAME_HIPNOTIC
916 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
917 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
918 // GAME_ROGUE
919 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
920 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
921 // GAME_NEHAHRA
922 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
923 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
924 // GAME_NEXUIZ
925 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
926 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
927 // GAME_TRANSFUSION
928 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
929 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
930 // GAME_GOODVSBAD2
931 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
932 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
933 // GAME_TEU
934 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
935 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
936 // GAME_BATTLEMECH
937 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
938 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
939 // GAME_ZYMOTIC
940 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
941 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         NULL,                   "zymotic",              "zymotic" },
942 // GAME_FNIGGIUM
943 // COMMANDLINEOPTION: Game: -fniggium runs the post apocalyptic melee RPG Fniggium
944 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         NULL,                   "fniggium",             "fniggium" },
945 // GAME_SETHERAL
946 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
947 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
948 // GAME_SOM
949 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
950 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
951 // GAME_TENEBRAE
952 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
953 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
954 // GAME_NEOTERIC
955 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
956 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
957 // GAME_OPENQUARTZ
958 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
959 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces"},
960 // GAME_PRYDON
961 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
962 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces"},
963 // GAME_NETHERWORLD
964 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Masters
965 { "netherworld",        "-netherworld", "Dark Masters",                 "id1",          "netherworld",  "nw",                   "darkplaces"},
966 // GAME_THEHUNTED
967 // COMMANDLINEOPTION: Game: -netherworld runs the game The Hunted
968 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted"},
969 };
970
971 void COM_InitGameType (void)
972 {
973         char name [MAX_OSPATH];
974         unsigned int i;
975
976         FS_StripExtension (com_argv[0], name, sizeof (name));
977         COM_ToLowerString (name, name, sizeof (name));
978
979         // Check the binary name; default to GAME_NORMAL (0)
980         gamemode = GAME_NORMAL;
981         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
982                 if (strstr (name, gamemode_info[i].prog_name))
983                 {
984                         gamemode = i;
985                         break;
986                 }
987
988         // Look for a command-line option
989         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
990                 if (COM_CheckParm (gamemode_info[i].cmdline))
991                 {
992                         gamemode = i;
993                         break;
994                 }
995
996         gamename = gamemode_info[gamemode].gamename;
997         gamedirname1 = gamemode_info[gamemode].gamedirname1;
998         gamedirname2 = gamemode_info[gamemode].gamedirname2;
999         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1000         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1001 }
1002
1003
1004 extern void Mathlib_Init(void);
1005 extern void FS_Init (void);
1006
1007 /*
1008 ================
1009 COM_Init
1010 ================
1011 */
1012 void COM_Init (void)
1013 {
1014         Cvar_RegisterVariable (&registered);
1015         Cvar_RegisterVariable (&cmdline);
1016
1017         Mathlib_Init();
1018
1019         FS_Init ();
1020         COM_CheckRegistered ();
1021 }
1022
1023 extern void FS_Shutdown (void);
1024
1025 /*
1026 ================
1027 COM_Shutdown
1028 ================
1029 */
1030 void COM_Shutdown (void)
1031 {
1032         FS_Shutdown ();
1033 }
1034
1035 /*
1036 ============
1037 va
1038
1039 does a varargs printf into a temp buffer, so I don't need to have
1040 varargs versions of all text functions.
1041 FIXME: make this buffer size safe someday
1042 ============
1043 */
1044 char *va(const char *format, ...)
1045 {
1046         va_list argptr;
1047         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1048         static char string[8][1024], *s;
1049         static int stringindex = 0;
1050
1051         s = string[stringindex];
1052         stringindex = (stringindex + 1) & 7;
1053         va_start (argptr, format);
1054         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1055         va_end (argptr);
1056
1057         return s;
1058 }
1059
1060
1061 //======================================
1062
1063 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1064
1065 #undef snprintf
1066 #undef vsnprintf
1067
1068 #ifdef WIN32
1069 # define snprintf _snprintf
1070 # define vsnprintf _vsnprintf
1071 #endif
1072
1073
1074 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1075 {
1076         va_list args;
1077         int result;
1078
1079         va_start (args, format);
1080         result = dpvsnprintf (buffer, buffersize, format, args);
1081         va_end (args);
1082
1083         return result;
1084 }
1085
1086
1087 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1088 {
1089         int result;
1090
1091         result = vsnprintf (buffer, buffersize, format, args);
1092         if (result < 0 || (size_t)result >= buffersize)
1093         {
1094                 buffer[buffersize - 1] = '\0';
1095                 return -1;
1096         }
1097
1098         return result;
1099 }
1100
1101
1102 //======================================
1103
1104 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1105 {
1106         if (size_out == 0)
1107                 return;
1108
1109         while (*in && size_out > 1)
1110         {
1111                 if (*in >= 'A' && *in <= 'Z')
1112                         *out++ = *in++ + 'a' - 'A';
1113                 else
1114                         *out++ = *in++;
1115                 size_out--;
1116         }
1117         *out = '\0';
1118 }
1119
1120 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1121 {
1122         if (size_out == 0)
1123                 return;
1124
1125         while (*in && size_out > 1)
1126         {
1127                 if (*in >= 'a' && *in <= 'z')
1128                         *out++ = *in++ + 'A' - 'a';
1129                 else
1130                         *out++ = *in++;
1131                 size_out--;
1132         }
1133         *out = '\0';
1134 }
1135
1136 int COM_StringBeginsWith(const char *s, const char *match)
1137 {
1138         for (;*s && *match;s++, match++)
1139                 if (*s != *match)
1140                         return false;
1141         return true;
1142 }
1143
1144 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1145 {
1146         int argc, commentprefixlength;
1147         char *tokenbufend;
1148         const char *l;
1149         argc = 0;
1150         tokenbufend = tokenbuf + tokenbufsize;
1151         l = *text;
1152         commentprefixlength = 0;
1153         if (commentprefix)
1154                 commentprefixlength = strlen(commentprefix);
1155         while (*l && *l != '\n' && *l != '\r')
1156         {
1157                 if (*l > ' ')
1158                 {
1159                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1160                         {
1161                                 while (*l && *l != '\n' && *l != '\r')
1162                                         l++;
1163                                 break;
1164                         }
1165                         if (argc >= maxargc)
1166                                 return -1;
1167                         argv[argc++] = tokenbuf;
1168                         if (*l == '"')
1169                         {
1170                                 l++;
1171                                 while (*l && *l != '"')
1172                                 {
1173                                         if (tokenbuf >= tokenbufend)
1174                                                 return -1;
1175                                         *tokenbuf++ = *l++;
1176                                 }
1177                                 if (*l == '"')
1178                                         l++;
1179                         }
1180                         else
1181                         {
1182                                 while (*l > ' ')
1183                                 {
1184                                         if (tokenbuf >= tokenbufend)
1185                                                 return -1;
1186                                         *tokenbuf++ = *l++;
1187                                 }
1188                         }
1189                         if (tokenbuf >= tokenbufend)
1190                                 return -1;
1191                         *tokenbuf++ = 0;
1192                 }
1193                 else
1194                         l++;
1195         }
1196         // line endings:
1197         // UNIX: \n
1198         // Mac: \r
1199         // Windows: \r\n
1200         if (*l == '\r')
1201                 l++;
1202         if (*l == '\n')
1203                 l++;
1204         *text = l;
1205         return argc;
1206 }
1207
1208 // written by Elric, thanks Elric!
1209 char *SearchInfostring(const char *infostring, const char *key)
1210 {
1211         static char value [256];
1212         char crt_key [256];
1213         size_t value_ind, key_ind;
1214         char c;
1215
1216         if (*infostring++ != '\\')
1217                 return NULL;
1218
1219         value_ind = 0;
1220         for (;;)
1221         {
1222                 key_ind = 0;
1223
1224                 // Get the key name
1225                 for (;;)
1226                 {
1227                         c = *infostring++;
1228
1229                         if (c == '\0')
1230                                 return NULL;
1231                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1232                         {
1233                                 crt_key[key_ind] = '\0';
1234                                 break;
1235                         }
1236
1237                         crt_key[key_ind++] = c;
1238                 }
1239
1240                 // If it's the key we are looking for, save it in "value"
1241                 if (!strcmp(crt_key, key))
1242                 {
1243                         for (;;)
1244                         {
1245                                 c = *infostring++;
1246
1247                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1248                                 {
1249                                         value[value_ind] = '\0';
1250                                         return value;
1251                                 }
1252
1253                                 value[value_ind++] = c;
1254                         }
1255                 }
1256
1257                 // Else, skip the value
1258                 for (;;)
1259                 {
1260                         c = *infostring++;
1261
1262                         if (c == '\0')
1263                                 return NULL;
1264                         if (c == '\\')
1265                                 break;
1266                 }
1267         }
1268 }
1269
1270
1271 //========================================================
1272 // strlcat and strlcpy, from OpenBSD
1273
1274 /*
1275  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1276  *
1277  * Permission to use, copy, modify, and distribute this software for any
1278  * purpose with or without fee is hereby granted, provided that the above
1279  * copyright notice and this permission notice appear in all copies.
1280  *
1281  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1282  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1283  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1284  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1285  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1286  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1287  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1288  */
1289
1290 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1291 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1292
1293
1294 #ifndef HAVE_STRLCAT
1295 size_t
1296 strlcat(char *dst, const char *src, size_t siz)
1297 {
1298         register char *d = dst;
1299         register const char *s = src;
1300         register size_t n = siz;
1301         size_t dlen;
1302
1303         /* Find the end of dst and adjust bytes left but don't go past end */
1304         while (n-- != 0 && *d != '\0')
1305                 d++;
1306         dlen = d - dst;
1307         n = siz - dlen;
1308
1309         if (n == 0)
1310                 return(dlen + strlen(s));
1311         while (*s != '\0') {
1312                 if (n != 1) {
1313                         *d++ = *s;
1314                         n--;
1315                 }
1316                 s++;
1317         }
1318         *d = '\0';
1319
1320         return(dlen + (s - src));       /* count does not include NUL */
1321 }
1322 #endif  // #ifndef HAVE_STRLCAT
1323
1324
1325 #ifndef HAVE_STRLCPY
1326 size_t
1327 strlcpy(char *dst, const char *src, size_t siz)
1328 {
1329         register char *d = dst;
1330         register const char *s = src;
1331         register size_t n = siz;
1332
1333         /* Copy as many bytes as will fit */
1334         if (n != 0 && --n != 0) {
1335                 do {
1336                         if ((*d++ = *s++) == 0)
1337                                 break;
1338                 } while (--n != 0);
1339         }
1340
1341         /* Not enough room in dst, add NUL and traverse rest of src */
1342         if (n == 0) {
1343                 if (siz != 0)
1344                         *d = '\0';              /* NUL-terminate dst */
1345                 while (*s++)
1346                         ;
1347         }
1348
1349         return(s - src - 1);    /* count does not include NUL */
1350 }
1351
1352 #endif  // #ifndef HAVE_STRLCPY