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