]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
changed qfile_t->buff_ind and qfile_t->buff_len to fs_offset_t to reduce number of...
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0"};
31 cvar_t cmdline = {0, "cmdline","0"};
32
33 extern qboolean fs_modified;   // set true if using non-id files
34
35 char com_token[1024];
36 int com_argc;
37 const char **com_argv;
38
39 // LordHavoc: made commandline 1024 characters instead of 256
40 #define CMDLINE_LENGTH  1024
41 char com_cmdline[CMDLINE_LENGTH];
42
43 gamemode_t gamemode;
44 const char *gamename;
45 const char *gamedirname1;
46 const char *gamedirname2;
47 const char *gamescreenshotname;
48 const char *gameuserdirname;
49 char com_modname[MAX_OSPATH] = "";
50
51
52 /*
53 ============================================================================
54
55                                         BYTE ORDER FUNCTIONS
56
57 ============================================================================
58 */
59
60 short   ShortSwap (short l)
61 {
62         qbyte    b1,b2;
63
64         b1 = l&255;
65         b2 = (l>>8)&255;
66
67         return (b1<<8) + b2;
68 }
69
70 int    LongSwap (int l)
71 {
72         qbyte    b1,b2,b3,b4;
73
74         b1 = l&255;
75         b2 = (l>>8)&255;
76         b3 = (l>>16)&255;
77         b4 = (l>>24)&255;
78
79         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
80 }
81
82 float FloatSwap (float f)
83 {
84         union
85         {
86                 float   f;
87                 qbyte    b[4];
88         } dat1, dat2;
89
90
91         dat1.f = f;
92         dat2.b[0] = dat1.b[3];
93         dat2.b[1] = dat1.b[2];
94         dat2.b[2] = dat1.b[1];
95         dat2.b[3] = dat1.b[0];
96         return dat2.f;
97 }
98
99
100 // Extract integers from buffers
101
102 unsigned int BuffBigLong (const qbyte *buffer)
103 {
104         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
105 }
106
107 unsigned short BuffBigShort (const qbyte *buffer)
108 {
109         return (buffer[0] << 8) | buffer[1];
110 }
111
112 unsigned int BuffLittleLong (const qbyte *buffer)
113 {
114         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
115 }
116
117 unsigned short BuffLittleShort (const qbyte *buffer)
118 {
119         return (buffer[1] << 8) | buffer[0];
120 }
121
122
123 /*
124 ============================================================================
125
126                                         CRC FUNCTIONS
127
128 ============================================================================
129 */
130
131 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
132 // and the initial and final xor values shown below...  in other words, the
133 // CCITT standard CRC used by XMODEM
134
135 #define CRC_INIT_VALUE  0xffff
136 #define CRC_XOR_VALUE   0x0000
137
138 static unsigned short crctable[256] =
139 {
140         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
141         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
142         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
143         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
144         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
145         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
146         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
147         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
148         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
149         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
150         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
151         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
152         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
153         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
154         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
155         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
156         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
157         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
158         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
159         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
160         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
161         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
162         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
163         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
164         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
165         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
166         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
167         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
168         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
169         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
170         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
171         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
172 };
173
174 unsigned short CRC_Block(const qbyte *data, size_t size)
175 {
176         unsigned short crc = CRC_INIT_VALUE;
177         while (size--)
178                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
179         return crc ^ CRC_XOR_VALUE;
180 }
181
182
183 /*
184 ==============================================================================
185
186                         MESSAGE IO FUNCTIONS
187
188 Handles byte ordering and avoids alignment errors
189 ==============================================================================
190 */
191
192 //
193 // writing functions
194 //
195
196 void MSG_WriteChar (sizebuf_t *sb, int c)
197 {
198         qbyte    *buf;
199
200         buf = SZ_GetSpace (sb, 1);
201         buf[0] = c;
202 }
203
204 void MSG_WriteByte (sizebuf_t *sb, int c)
205 {
206         qbyte    *buf;
207
208         buf = SZ_GetSpace (sb, 1);
209         buf[0] = c;
210 }
211
212 void MSG_WriteShort (sizebuf_t *sb, int c)
213 {
214         qbyte    *buf;
215
216         buf = SZ_GetSpace (sb, 2);
217         buf[0] = c&0xff;
218         buf[1] = c>>8;
219 }
220
221 void MSG_WriteLong (sizebuf_t *sb, int c)
222 {
223         qbyte    *buf;
224
225         buf = SZ_GetSpace (sb, 4);
226         buf[0] = c&0xff;
227         buf[1] = (c>>8)&0xff;
228         buf[2] = (c>>16)&0xff;
229         buf[3] = c>>24;
230 }
231
232 void MSG_WriteFloat (sizebuf_t *sb, float f)
233 {
234         union
235         {
236                 float   f;
237                 int     l;
238         } dat;
239
240
241         dat.f = f;
242         dat.l = LittleLong (dat.l);
243
244         SZ_Write (sb, &dat.l, 4);
245 }
246
247 void MSG_WriteString (sizebuf_t *sb, const char *s)
248 {
249         if (!s)
250                 SZ_Write (sb, "", 1);
251         else
252                 SZ_Write (sb, s, (int)strlen(s)+1);
253 }
254
255 void MSG_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                 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 (gamemode == GAME_NORMAL && !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 /*
1005 ================
1006 COM_Init
1007 ================
1008 */
1009 void COM_Init_Commands (void)
1010 {
1011         Cvar_RegisterVariable (&registered);
1012         Cvar_RegisterVariable (&cmdline);
1013 }
1014
1015 /*
1016 ============
1017 va
1018
1019 does a varargs printf into a temp buffer, so I don't need to have
1020 varargs versions of all text functions.
1021 FIXME: make this buffer size safe someday
1022 ============
1023 */
1024 char *va(const char *format, ...)
1025 {
1026         va_list argptr;
1027         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1028         static char string[8][1024], *s;
1029         static int stringindex = 0;
1030
1031         s = string[stringindex];
1032         stringindex = (stringindex + 1) & 7;
1033         va_start (argptr, format);
1034         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1035         va_end (argptr);
1036
1037         return s;
1038 }
1039
1040
1041 //======================================
1042
1043 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1044
1045 #undef snprintf
1046 #undef vsnprintf
1047
1048 #ifdef WIN32
1049 # define snprintf _snprintf
1050 # define vsnprintf _vsnprintf
1051 #endif
1052
1053
1054 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1055 {
1056         va_list args;
1057         int result;
1058
1059         va_start (args, format);
1060         result = dpvsnprintf (buffer, buffersize, format, args);
1061         va_end (args);
1062
1063         return result;
1064 }
1065
1066
1067 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1068 {
1069         int result;
1070
1071         result = vsnprintf (buffer, buffersize, format, args);
1072         if (result < 0 || (size_t)result >= buffersize)
1073         {
1074                 buffer[buffersize - 1] = '\0';
1075                 return -1;
1076         }
1077
1078         return result;
1079 }
1080
1081
1082 //======================================
1083
1084 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1085 {
1086         if (size_out == 0)
1087                 return;
1088
1089         while (*in && size_out > 1)
1090         {
1091                 if (*in >= 'A' && *in <= 'Z')
1092                         *out++ = *in++ + 'a' - 'A';
1093                 else
1094                         *out++ = *in++;
1095                 size_out--;
1096         }
1097         *out = '\0';
1098 }
1099
1100 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1101 {
1102         if (size_out == 0)
1103                 return;
1104
1105         while (*in && size_out > 1)
1106         {
1107                 if (*in >= 'a' && *in <= 'z')
1108                         *out++ = *in++ + 'A' - 'a';
1109                 else
1110                         *out++ = *in++;
1111                 size_out--;
1112         }
1113         *out = '\0';
1114 }
1115
1116 int COM_StringBeginsWith(const char *s, const char *match)
1117 {
1118         for (;*s && *match;s++, match++)
1119                 if (*s != *match)
1120                         return false;
1121         return true;
1122 }
1123
1124 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1125 {
1126         int argc, commentprefixlength;
1127         char *tokenbufend;
1128         const char *l;
1129         argc = 0;
1130         tokenbufend = tokenbuf + tokenbufsize;
1131         l = *text;
1132         commentprefixlength = 0;
1133         if (commentprefix)
1134                 commentprefixlength = (int)strlen(commentprefix);
1135         while (*l && *l != '\n' && *l != '\r')
1136         {
1137                 if (*l > ' ')
1138                 {
1139                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1140                         {
1141                                 while (*l && *l != '\n' && *l != '\r')
1142                                         l++;
1143                                 break;
1144                         }
1145                         if (argc >= maxargc)
1146                                 return -1;
1147                         argv[argc++] = tokenbuf;
1148                         if (*l == '"')
1149                         {
1150                                 l++;
1151                                 while (*l && *l != '"')
1152                                 {
1153                                         if (tokenbuf >= tokenbufend)
1154                                                 return -1;
1155                                         *tokenbuf++ = *l++;
1156                                 }
1157                                 if (*l == '"')
1158                                         l++;
1159                         }
1160                         else
1161                         {
1162                                 while (*l > ' ')
1163                                 {
1164                                         if (tokenbuf >= tokenbufend)
1165                                                 return -1;
1166                                         *tokenbuf++ = *l++;
1167                                 }
1168                         }
1169                         if (tokenbuf >= tokenbufend)
1170                                 return -1;
1171                         *tokenbuf++ = 0;
1172                 }
1173                 else
1174                         l++;
1175         }
1176         // line endings:
1177         // UNIX: \n
1178         // Mac: \r
1179         // Windows: \r\n
1180         if (*l == '\r')
1181                 l++;
1182         if (*l == '\n')
1183                 l++;
1184         *text = l;
1185         return argc;
1186 }
1187
1188 // written by Elric, thanks Elric!
1189 char *SearchInfostring(const char *infostring, const char *key)
1190 {
1191         static char value [256];
1192         char crt_key [256];
1193         size_t value_ind, key_ind;
1194         char c;
1195
1196         if (*infostring++ != '\\')
1197                 return NULL;
1198
1199         value_ind = 0;
1200         for (;;)
1201         {
1202                 key_ind = 0;
1203
1204                 // Get the key name
1205                 for (;;)
1206                 {
1207                         c = *infostring++;
1208
1209                         if (c == '\0')
1210                                 return NULL;
1211                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1212                         {
1213                                 crt_key[key_ind] = '\0';
1214                                 break;
1215                         }
1216
1217                         crt_key[key_ind++] = c;
1218                 }
1219
1220                 // If it's the key we are looking for, save it in "value"
1221                 if (!strcmp(crt_key, key))
1222                 {
1223                         for (;;)
1224                         {
1225                                 c = *infostring++;
1226
1227                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1228                                 {
1229                                         value[value_ind] = '\0';
1230                                         return value;
1231                                 }
1232
1233                                 value[value_ind++] = c;
1234                         }
1235                 }
1236
1237                 // Else, skip the value
1238                 for (;;)
1239                 {
1240                         c = *infostring++;
1241
1242                         if (c == '\0')
1243                                 return NULL;
1244                         if (c == '\\')
1245                                 break;
1246                 }
1247         }
1248 }
1249
1250
1251 //========================================================
1252 // strlcat and strlcpy, from OpenBSD
1253
1254 /*
1255  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1256  *
1257  * Permission to use, copy, modify, and distribute this software for any
1258  * purpose with or without fee is hereby granted, provided that the above
1259  * copyright notice and this permission notice appear in all copies.
1260  *
1261  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1262  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1263  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1264  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1265  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1266  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1267  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1268  */
1269
1270 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1271 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1272
1273
1274 #ifndef HAVE_STRLCAT
1275 size_t
1276 strlcat(char *dst, const char *src, size_t siz)
1277 {
1278         register char *d = dst;
1279         register const char *s = src;
1280         register size_t n = siz;
1281         size_t dlen;
1282
1283         /* Find the end of dst and adjust bytes left but don't go past end */
1284         while (n-- != 0 && *d != '\0')
1285                 d++;
1286         dlen = d - dst;
1287         n = siz - dlen;
1288
1289         if (n == 0)
1290                 return(dlen + strlen(s));
1291         while (*s != '\0') {
1292                 if (n != 1) {
1293                         *d++ = *s;
1294                         n--;
1295                 }
1296                 s++;
1297         }
1298         *d = '\0';
1299
1300         return(dlen + (s - src));       /* count does not include NUL */
1301 }
1302 #endif  // #ifndef HAVE_STRLCAT
1303
1304
1305 #ifndef HAVE_STRLCPY
1306 size_t
1307 strlcpy(char *dst, const char *src, size_t siz)
1308 {
1309         register char *d = dst;
1310         register const char *s = src;
1311         register size_t n = siz;
1312
1313         /* Copy as many bytes as will fit */
1314         if (n != 0 && --n != 0) {
1315                 do {
1316                         if ((*d++ = *s++) == 0)
1317                                 break;
1318                 } while (--n != 0);
1319         }
1320
1321         /* Not enough room in dst, add NUL and traverse rest of src */
1322         if (n == 0) {
1323                 if (siz != 0)
1324                         *d = '\0';              /* NUL-terminate dst */
1325                 while (*s++)
1326                         ;
1327         }
1328
1329         return(s - src - 1);    /* count does not include NUL */
1330 }
1331
1332 #endif  // #ifndef HAVE_STRLCPY