]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
don't allow Log_ConPrint to recurse into itself, as can happen with a memory corrupti...
[divverent/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0"};
31 cvar_t cmdline = {0, "cmdline","0"};
32
33 extern qboolean fs_modified;   // set true if using non-id files
34
35 char com_token[1024];
36 int com_argc;
37 const char **com_argv;
38
39 // LordHavoc: made commandline 1024 characters instead of 256
40 #define CMDLINE_LENGTH  1024
41 char com_cmdline[CMDLINE_LENGTH];
42
43 int gamemode;
44 const char *gamename;
45 const char *gamedirname;
46 const char *gamescreenshotname;
47 char com_modname[MAX_OSPATH] = "";
48
49
50 /*
51 ============================================================================
52
53                                         BYTE ORDER FUNCTIONS
54
55 ============================================================================
56 */
57
58 short   ShortSwap (short l)
59 {
60         qbyte    b1,b2;
61
62         b1 = l&255;
63         b2 = (l>>8)&255;
64
65         return (b1<<8) + b2;
66 }
67
68 int    LongSwap (int l)
69 {
70         qbyte    b1,b2,b3,b4;
71
72         b1 = l&255;
73         b2 = (l>>8)&255;
74         b3 = (l>>16)&255;
75         b4 = (l>>24)&255;
76
77         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
78 }
79
80 float FloatSwap (float f)
81 {
82         union
83         {
84                 float   f;
85                 qbyte    b[4];
86         } dat1, dat2;
87
88
89         dat1.f = f;
90         dat2.b[0] = dat1.b[3];
91         dat2.b[1] = dat1.b[2];
92         dat2.b[2] = dat1.b[1];
93         dat2.b[3] = dat1.b[0];
94         return dat2.f;
95 }
96
97
98 // Extract integers from buffers
99
100 unsigned int BuffBigLong (const qbyte *buffer)
101 {
102         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
103 }
104
105 unsigned short BuffBigShort (const qbyte *buffer)
106 {
107         return (buffer[0] << 8) | buffer[1];
108 }
109
110 unsigned int BuffLittleLong (const qbyte *buffer)
111 {
112         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
113 }
114
115 unsigned short BuffLittleShort (const qbyte *buffer)
116 {
117         return (buffer[1] << 8) | buffer[0];
118 }
119
120
121 /*
122 ==============================================================================
123
124                         MESSAGE IO FUNCTIONS
125
126 Handles byte ordering and avoids alignment errors
127 ==============================================================================
128 */
129
130 //
131 // writing functions
132 //
133
134 void MSG_WriteChar (sizebuf_t *sb, int c)
135 {
136         qbyte    *buf;
137
138         buf = SZ_GetSpace (sb, 1);
139         buf[0] = c;
140 }
141
142 void MSG_WriteByte (sizebuf_t *sb, int c)
143 {
144         qbyte    *buf;
145
146         buf = SZ_GetSpace (sb, 1);
147         buf[0] = c;
148 }
149
150 void MSG_WriteShort (sizebuf_t *sb, int c)
151 {
152         qbyte    *buf;
153
154         buf = SZ_GetSpace (sb, 2);
155         buf[0] = c&0xff;
156         buf[1] = c>>8;
157 }
158
159 void MSG_WriteLong (sizebuf_t *sb, int c)
160 {
161         qbyte    *buf;
162
163         buf = SZ_GetSpace (sb, 4);
164         buf[0] = c&0xff;
165         buf[1] = (c>>8)&0xff;
166         buf[2] = (c>>16)&0xff;
167         buf[3] = c>>24;
168 }
169
170 void MSG_WriteFloat (sizebuf_t *sb, float f)
171 {
172         union
173         {
174                 float   f;
175                 int     l;
176         } dat;
177
178
179         dat.f = f;
180         dat.l = LittleLong (dat.l);
181
182         SZ_Write (sb, &dat.l, 4);
183 }
184
185 void MSG_WriteString (sizebuf_t *sb, const char *s)
186 {
187         if (!s)
188                 SZ_Write (sb, "", 1);
189         else
190                 SZ_Write (sb, s, strlen(s)+1);
191 }
192
193 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
194 {
195         if (f >= 0)
196                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
197         else
198                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
199 }
200
201 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
202 {
203         if (f >= 0)
204                 MSG_WriteShort (sb, (int)(f + 0.5));
205         else
206                 MSG_WriteShort (sb, (int)(f - 0.5));
207 }
208
209 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
210 {
211         MSG_WriteFloat (sb, f);
212 }
213
214 void MSG_WriteCoord (sizebuf_t *sb, float f, int protocol)
215 {
216         if (protocol == PROTOCOL_QUAKE)
217                 MSG_WriteCoord13i (sb, f);
218         else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5)
219                 MSG_WriteCoord32f (sb, f);
220         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
221                 MSG_WriteCoord16i (sb, f);
222         else
223                 Host_Error("MSG_WriteCoord: unknown protocol\n");
224 }
225
226 void MSG_WriteVector (sizebuf_t *sb, float *v, int protocol)
227 {
228         MSG_WriteCoord (sb, v[0], protocol);
229         MSG_WriteCoord (sb, v[1], protocol);
230         MSG_WriteCoord (sb, v[2], protocol);
231 }
232
233 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
234 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
235 {
236         if (f >= 0)
237                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
238         else
239                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
240 }
241
242 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
243 {
244         if (f >= 0)
245                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
246         else
247                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
248 }
249
250 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
251 {
252         MSG_WriteFloat (sb, f);
253 }
254
255 void MSG_WriteAngle (sizebuf_t *sb, float f, int protocol)
256 {
257         if (protocol == PROTOCOL_DARKPLACES5)
258                 MSG_WriteAngle16i (sb, f);
259         else
260                 MSG_WriteAngle8i (sb, f);
261 }
262
263 //
264 // reading functions
265 //
266 int msg_readcount;
267 qboolean msg_badread;
268
269 void MSG_BeginReading (void)
270 {
271         msg_readcount = 0;
272         msg_badread = false;
273 }
274
275 int MSG_ReadLittleShort (void)
276 {
277         if (msg_readcount+2 > net_message.cursize)
278         {
279                 msg_badread = true;
280                 return -1;
281         }
282         msg_readcount += 2;
283         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
284 }
285
286 int MSG_ReadBigShort (void)
287 {
288         if (msg_readcount+2 > net_message.cursize)
289         {
290                 msg_badread = true;
291                 return -1;
292         }
293         msg_readcount += 2;
294         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
295 }
296
297 int MSG_ReadLittleLong (void)
298 {
299         if (msg_readcount+4 > net_message.cursize)
300         {
301                 msg_badread = true;
302                 return -1;
303         }
304         msg_readcount += 4;
305         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);
306 }
307
308 int MSG_ReadBigLong (void)
309 {
310         if (msg_readcount+4 > net_message.cursize)
311         {
312                 msg_badread = true;
313                 return -1;
314         }
315         msg_readcount += 4;
316         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];
317 }
318
319 float MSG_ReadLittleFloat (void)
320 {
321         union
322         {
323                 float f;
324                 int l;
325         } dat;
326         if (msg_readcount+4 > net_message.cursize)
327         {
328                 msg_badread = true;
329                 return -1;
330         }
331         msg_readcount += 4;
332         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);
333         return dat.f;
334 }
335
336 float MSG_ReadBigFloat (void)
337 {
338         union
339         {
340                 float f;
341                 int l;
342         } dat;
343         if (msg_readcount+4 > net_message.cursize)
344         {
345                 msg_badread = true;
346                 return -1;
347         }
348         msg_readcount += 4;
349         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];
350         return dat.f;
351 }
352
353 char *MSG_ReadString (void)
354 {
355         static char string[2048];
356         int l,c;
357         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
358                 string[l] = c;
359         string[l] = 0;
360         return string;
361 }
362
363 int MSG_ReadBytes (int numbytes, unsigned char *out)
364 {
365         int l, c;
366         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
367                 out[l] = c;
368         return l;
369 }
370
371 float MSG_ReadCoord13i (void)
372 {
373         return MSG_ReadLittleShort() * (1.0/8.0);
374 }
375
376 float MSG_ReadCoord16i (void)
377 {
378         return (signed short) MSG_ReadLittleShort();
379 }
380
381 float MSG_ReadCoord32f (void)
382 {
383         return MSG_ReadLittleFloat();
384 }
385
386 float MSG_ReadCoord (int protocol)
387 {
388         if (protocol == PROTOCOL_QUAKE || protocol == 250)
389                 return MSG_ReadCoord13i();
390         else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5)
391                 return MSG_ReadCoord32f();
392         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
393                 return MSG_ReadCoord16i();
394         Host_Error("MSG_ReadCoord: unknown protocol\n");
395         return 0;
396 }
397
398 void MSG_ReadVector (float *v, int protocol)
399 {
400         v[0] = MSG_ReadCoord(protocol);
401         v[1] = MSG_ReadCoord(protocol);
402         v[2] = MSG_ReadCoord(protocol);
403 }
404
405 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
406 float MSG_ReadAngle8i (void)
407 {
408         return (signed char) MSG_ReadByte () * (360.0/256.0);
409 }
410
411 float MSG_ReadAngle16i (void)
412 {
413         return (signed short)MSG_ReadShort () * (360.0/65536.0);
414 }
415
416 float MSG_ReadAngle32f (void)
417 {
418         return MSG_ReadFloat ();
419 }
420
421 float MSG_ReadAngle (int protocol)
422 {
423         if (protocol == PROTOCOL_DARKPLACES5)
424                 return MSG_ReadAngle16i ();
425         else
426                 return MSG_ReadAngle8i ();
427 }
428
429
430 //===========================================================================
431
432 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
433 {
434         if (startsize < 256)
435                 startsize = 256;
436         buf->mempool = Mem_AllocPool(name, 0, NULL);
437         buf->data = Mem_Alloc(buf->mempool, startsize);
438         buf->maxsize = startsize;
439         buf->cursize = 0;
440 }
441
442
443 void SZ_Free (sizebuf_t *buf)
444 {
445         Mem_FreePool(&buf->mempool);
446         buf->data = NULL;
447         buf->maxsize = 0;
448         buf->cursize = 0;
449 }
450
451 void SZ_Clear (sizebuf_t *buf)
452 {
453         buf->cursize = 0;
454 }
455
456 void *SZ_GetSpace (sizebuf_t *buf, int length)
457 {
458         void *data;
459
460         if (buf->cursize + length > buf->maxsize)
461         {
462                 if (!buf->allowoverflow)
463                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
464
465                 if (length > buf->maxsize)
466                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
467
468                 buf->overflowed = true;
469                 Con_Print("SZ_GetSpace: overflow\n");
470                 SZ_Clear (buf);
471         }
472
473         data = buf->data + buf->cursize;
474         buf->cursize += length;
475
476         return data;
477 }
478
479 void SZ_Write (sizebuf_t *buf, const void *data, int length)
480 {
481         memcpy (SZ_GetSpace(buf,length),data,length);
482 }
483
484 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
485 // attention, it has been eradicated from here, its only (former) use in
486 // all of darkplaces.
487
488 static char *hexchar = "0123456789ABCDEF";
489 void Com_HexDumpToConsole(const qbyte *data, int size)
490 {
491         int i, j, n;
492         char text[1024];
493         char *cur, *flushpointer;
494         const qbyte *d;
495         cur = text;
496         flushpointer = text + 512;
497         for (i = 0;i < size;)
498         {
499                 n = 16;
500                 if (n > size - i)
501                         n = size - i;
502                 d = data + i;
503                 // print offset
504                 *cur++ = hexchar[(i >> 12) & 15];
505                 *cur++ = hexchar[(i >>  8) & 15];
506                 *cur++ = hexchar[(i >>  4) & 15];
507                 *cur++ = hexchar[(i >>  0) & 15];
508                 *cur++ = ':';
509                 // print hex
510                 for (j = 0;j < 16;j++)
511                 {
512                         if (j < n)
513                         {
514                                 *cur++ = hexchar[(d[j] >> 4) & 15];
515                                 *cur++ = hexchar[(d[j] >> 0) & 15];
516                         }
517                         else
518                         {
519                                 *cur++ = ' ';
520                                 *cur++ = ' ';
521                         }
522                         if ((j & 3) == 3)
523                                 *cur++ = ' ';
524                 }
525                 // print text
526                 for (j = 0;j < 16;j++)
527                 {
528                         if (j < n)
529                         {
530                                 if (d[j] >= ' ' && d[j] <= 127)
531                                         *cur++ = d[j];
532                                 else
533                                         *cur++ = '.';
534                         }
535                         else
536                                 *cur++ = ' ';
537                 }
538                 *cur++ = '\n';
539                 i += n;
540                 if (cur >= flushpointer || i >= size)
541                 {
542                         *cur++ = 0;
543                         Con_Print(text);
544                         cur = text;
545                 }
546         }
547 }
548
549 void SZ_HexDumpToConsole(const sizebuf_t *buf)
550 {
551         Com_HexDumpToConsole(buf->data, buf->cursize);
552 }
553
554
555 //============================================================================
556
557
558 /*
559 ==============
560 COM_ParseToken
561
562 Parse a token out of a string
563 ==============
564 */
565 int COM_ParseToken(const char **datapointer, int returnnewline)
566 {
567         int len;
568         const char *data = *datapointer;
569
570         len = 0;
571         com_token[0] = 0;
572
573         if (!data)
574         {
575                 *datapointer = NULL;
576                 return false;
577         }
578
579 // skip whitespace
580 skipwhite:
581         for (;*data <= ' ' && (*data != '\n' || !returnnewline);data++)
582         {
583                 if (*data == 0)
584                 {
585                         // end of file
586                         *datapointer = NULL;
587                         return false;
588                 }
589         }
590
591         if (data[0] == '/' && data[1] == '/')
592         {
593                 // comment
594                 while (*data && *data != '\n')
595                         data++;
596                 goto skipwhite;
597         }
598         else if (data[0] == '/' && data[1] == '*')
599         {
600                 // comment
601                 data++;
602                 while (*data && (data[0] != '*' || data[1] != '/'))
603                         data++;
604                 data += 2;
605                 goto skipwhite;
606         }
607         else if (*data == '\"')
608         {
609                 // quoted string
610                 for (data++;*data != '\"';data++)
611                 {
612                         if (!*data || len >= (int)sizeof(com_token) - 1)
613                         {
614                                 com_token[0] = 0;
615                                 *datapointer = NULL;
616                                 return false;
617                         }
618                         com_token[len++] = *data;
619                 }
620                 com_token[len] = 0;
621                 *datapointer = data+1;
622                 return true;
623         }
624         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
625         {
626                 // single character
627                 com_token[len++] = *data++;
628                 com_token[len] = 0;
629                 *datapointer = data;
630                 return true;
631         }
632         else
633         {
634                 // regular word
635                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';';data++)
636                 {
637                         if (len >= (int)sizeof(com_token) - 1)
638                         {
639                                 com_token[0] = 0;
640                                 *datapointer = NULL;
641                                 return false;
642                         }
643                         com_token[len++] = *data;
644                 }
645                 com_token[len] = 0;
646                 *datapointer = data;
647                 return true;
648         }
649 }
650
651 /*
652 ==============
653 COM_ParseTokenConsole
654
655 Parse a token out of a string, behaving like the qwcl console
656 ==============
657 */
658 int COM_ParseTokenConsole(const char **datapointer)
659 {
660         int len;
661         const char *data = *datapointer;
662
663         len = 0;
664         com_token[0] = 0;
665
666         if (!data)
667         {
668                 *datapointer = NULL;
669                 return false;
670         }
671
672 // skip whitespace
673 skipwhite:
674         for (;*data <= ' ';data++)
675         {
676                 if (*data == 0)
677                 {
678                         // end of file
679                         *datapointer = NULL;
680                         return false;
681                 }
682         }
683
684         if (*data == '/' && data[1] == '/')
685         {
686                 // comment
687                 while (*data && *data != '\n')
688                         data++;
689                 goto skipwhite;
690         }
691         else if (*data == '\"')
692         {
693                 // quoted string
694                 for (data++;*data != '\"';data++)
695                 {
696                         if (!*data || len >= (int)sizeof(com_token) - 1)
697                         {
698                                 com_token[0] = 0;
699                                 *datapointer = NULL;
700                                 return false;
701                         }
702                         com_token[len++] = *data;
703                 }
704                 com_token[len] = 0;
705                 *datapointer = data+1;
706                 return true;
707         }
708         else
709         {
710                 // regular word
711                 for (;*data > ' ';data++)
712                 {
713                         if (len >= (int)sizeof(com_token) - 1)
714                         {
715                                 com_token[0] = 0;
716                                 *datapointer = NULL;
717                                 return false;
718                         }
719                         com_token[len++] = *data;
720                 }
721                 com_token[len] = 0;
722                 *datapointer = data;
723                 return true;
724         }
725 }
726
727
728 /*
729 ================
730 COM_CheckParm
731
732 Returns the position (1 to argc-1) in the program's argument list
733 where the given parameter apears, or 0 if not present
734 ================
735 */
736 int COM_CheckParm (const char *parm)
737 {
738         int i;
739
740         for (i=1 ; i<com_argc ; i++)
741         {
742                 if (!com_argv[i])
743                         continue;               // NEXTSTEP sometimes clears appkit vars.
744                 if (!strcmp (parm,com_argv[i]))
745                         return i;
746         }
747
748         return 0;
749 }
750
751 /*
752 ================
753 COM_CheckRegistered
754
755 Looks for the pop.txt file and verifies it.
756 Sets the "registered" cvar.
757 Immediately exits out if an alternate game was attempted to be started without
758 being registered.
759 ================
760 */
761 void COM_CheckRegistered (void)
762 {
763         Cvar_Set ("cmdline", com_cmdline);
764
765         if (!FS_FileExists("gfx/pop.lmp"))
766         {
767                 if (fs_modified)
768                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
769                 else
770                         Con_Print("Playing shareware version.\n");
771                 return;
772         }
773
774         Cvar_Set ("registered", "1");
775         Con_Print("Playing registered version.\n");
776 }
777
778
779 /*
780 ================
781 COM_InitArgv
782 ================
783 */
784 void COM_InitArgv (void)
785 {
786         int i, j, n;
787         // reconstitute the command line for the cmdline externally visible cvar
788         n = 0;
789         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
790         {
791                 i = 0;
792                 if (strstr(com_argv[j], " "))
793                 {
794                         // arg contains whitespace, store quotes around it
795                         com_cmdline[n++] = '\"';
796                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
797                                 com_cmdline[n++] = com_argv[j][i++];
798                         com_cmdline[n++] = '\"';
799                 }
800                 else
801                 {
802                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
803                                 com_cmdline[n++] = com_argv[j][i++];
804                 }
805                 if (n < (CMDLINE_LENGTH - 1))
806                         com_cmdline[n++] = ' ';
807                 else
808                         break;
809         }
810         com_cmdline[n] = 0;
811 }
812
813
814 //===========================================================================
815
816 // Game mods
817
818 typedef struct
819 {
820         const char* prog_name;
821         const char* cmdline;
822         const char* gamename;
823         const char* gamedirname;
824         const char* gamescreenshotname;
825 } gamemode_info_t;
826
827 static const gamemode_info_t gamemode_info [] =
828 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
829
830 // GAME_NORMAL
831 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
832 { "",                           "-quake",               "DarkPlaces-Quake",             "",                     "dp" },
833 // GAME_HIPNOTIC
834 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
835 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "hipnotic",     "dp" },
836 // GAME_ROGUE
837 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
838 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "rogue",        "dp" },
839 // GAME_NEHAHRA
840 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
841 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "nehahra",      "dp" },
842 // GAME_NEXUIZ
843 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
844 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         "nexuiz" },
845 // GAME_TRANSFUSION
846 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
847 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       "transfusion" },
848 // GAME_GOODVSBAD2
849 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
850 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          "gvb2" },
851 // GAME_TEU
852 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
853 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      "teu" },
854 // GAME_BATTLEMECH
855 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech 
856 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         "battlemech" },
857 // GAME_ZYMOTIC
858 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
859 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         "zymotic" },
860 // GAME_FNIGGIUM
861 // COMMANDLINEOPTION: Game: -fniggium runs the post apocalyptic melee RPG Fniggium 
862 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         "fniggium" },
863 // GAME_SETHERAL
864 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral 
865 { "setheral",           "-setheral",    "Setheral",                             "data",         "setheral" },
866 // GAME_SOM
867 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man 
868 { "som",                        "-som",                 "Son of Man",                   "sonofman",     "som" },
869 // GAME_TENEBRAE
870 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
871 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "tenebrae",     "dp" },
872 // GAME_NEOTERIC
873 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
874 { "neoteric",           "-neoteric",    "Neoteric",                             "neobase",      "neo" },
875 // GAME_OPENQUARTZ
876 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
877 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          "openquartz"},
878 // GAME_PRYDON
879 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
880 { "prydon",                     "-prydon",              "PrydonGate",                   "prydon",       "prydon"},
881 // GAME_NETHERWORLD
882 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Masters
883 { "netherworld",        "-netherworld", "Dark Masters",                 "netherworld",  "nw"},
884 };
885
886 void COM_InitGameType (void)
887 {
888         char name [MAX_OSPATH];
889         unsigned int i;
890
891         FS_StripExtension (com_argv[0], name, sizeof (name));
892         COM_ToLowerString (name, name, sizeof (name));
893
894         // Check the binary name; default to GAME_NORMAL (0)
895         gamemode = GAME_NORMAL;
896         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
897                 if (strstr (name, gamemode_info[i].prog_name))
898                 {
899                         gamemode = i;
900                         break;
901                 }
902
903         // Look for a command-line option
904         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
905                 if (COM_CheckParm (gamemode_info[i].cmdline))
906                 {
907                         gamemode = i;
908                         break;
909                 }
910
911         gamename = gamemode_info[gamemode].gamename;
912         gamedirname = gamemode_info[gamemode].gamedirname;
913         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
914 }
915
916
917 extern void Mathlib_Init(void);
918 extern void FS_Init (void);
919
920 /*
921 ================
922 COM_Init
923 ================
924 */
925 void COM_Init (void)
926 {
927         Cvar_RegisterVariable (&registered);
928         Cvar_RegisterVariable (&cmdline);
929
930         Mathlib_Init();
931
932         FS_Init ();
933         Log_Init ();
934         COM_CheckRegistered ();
935 }
936
937
938 /*
939 ============
940 va
941
942 does a varargs printf into a temp buffer, so I don't need to have
943 varargs versions of all text functions.
944 FIXME: make this buffer size safe someday
945 ============
946 */
947 char *va(const char *format, ...)
948 {
949         va_list argptr;
950         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
951         static char string[8][1024], *s;
952         static int stringindex = 0;
953
954         s = string[stringindex];
955         stringindex = (stringindex + 1) & 7;
956         va_start (argptr, format);
957         vsnprintf (s, sizeof (string[0]), format,argptr);
958         va_end (argptr);
959
960         return s;
961 }
962
963
964 //======================================
965
966 void COM_ToLowerString (const char *in, char *out, size_t size_out)
967 {
968         if (size_out == 0)
969                 return;
970
971         while (*in && size_out > 1)
972         {
973                 if (*in >= 'A' && *in <= 'Z')
974                         *out++ = *in++ + 'a' - 'A';
975                 else
976                         *out++ = *in++;
977                 size_out--;
978         }
979         *out = '\0';
980 }
981
982 void COM_ToUpperString (const char *in, char *out, size_t size_out)
983 {
984         if (size_out == 0)
985                 return;
986
987         while (*in && size_out > 1)
988         {
989                 if (*in >= 'a' && *in <= 'z')
990                         *out++ = *in++ + 'A' - 'a';
991                 else
992                         *out++ = *in++;
993                 size_out--;
994         }
995         *out = '\0';
996 }
997
998 int COM_StringBeginsWith(const char *s, const char *match)
999 {
1000         for (;*s && *match;s++, match++)
1001                 if (*s != *match)
1002                         return false;
1003         return true;
1004 }
1005
1006 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1007 {
1008         int argc, commentprefixlength;
1009         char *tokenbufend;
1010         const char *l;
1011         argc = 0;
1012         tokenbufend = tokenbuf + tokenbufsize;
1013         l = *text;
1014         commentprefixlength = 0;
1015         if (commentprefix)
1016                 commentprefixlength = strlen(commentprefix);
1017         while (*l && *l != '\n')
1018         {
1019                 if (*l > ' ')
1020                 {
1021                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1022                         {
1023                                 while (*l && *l != '\n')
1024                                         l++;
1025                                 break;
1026                         }
1027                         if (argc >= maxargc)
1028                                 return -1;
1029                         argv[argc++] = tokenbuf;
1030                         if (*l == '"')
1031                         {
1032                                 l++;
1033                                 while (*l && *l != '"')
1034                                 {
1035                                         if (tokenbuf >= tokenbufend)
1036                                                 return -1;
1037                                         *tokenbuf++ = *l++;
1038                                 }
1039                                 if (*l == '"')
1040                                         l++;
1041                         }
1042                         else
1043                         {
1044                                 while (*l > ' ')
1045                                 {
1046                                         if (tokenbuf >= tokenbufend)
1047                                                 return -1;
1048                                         *tokenbuf++ = *l++;
1049                                 }
1050                         }
1051                         if (tokenbuf >= tokenbufend)
1052                                 return -1;
1053                         *tokenbuf++ = 0;
1054                 }
1055                 else
1056                         l++;
1057         }
1058         if (*l == '\n')
1059                 l++;
1060         *text = l;
1061         return argc;
1062 }
1063
1064 // written by Elric, thanks Elric!
1065 char *SearchInfostring(const char *infostring, const char *key)
1066 {
1067         static char value [256];
1068         char crt_key [256];
1069         size_t value_ind, key_ind;
1070         char c;
1071
1072         if (*infostring++ != '\\')
1073                 return NULL;
1074
1075         value_ind = 0;
1076         for (;;)
1077         {
1078                 key_ind = 0;
1079
1080                 // Get the key name
1081                 for (;;)
1082                 {
1083                         c = *infostring++;
1084
1085                         if (c == '\0')
1086                                 return NULL;
1087                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1088                         {
1089                                 crt_key[key_ind] = '\0';
1090                                 break;
1091                         }
1092
1093                         crt_key[key_ind++] = c;
1094                 }
1095
1096                 // If it's the key we are looking for, save it in "value"
1097                 if (!strcmp(crt_key, key))
1098                 {
1099                         for (;;)
1100                         {
1101                                 c = *infostring++;
1102
1103                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1104                                 {
1105                                         value[value_ind] = '\0';
1106                                         return value;
1107                                 }
1108
1109                                 value[value_ind++] = c;
1110                         }
1111                 }
1112
1113                 // Else, skip the value
1114                 for (;;)
1115                 {
1116                         c = *infostring++;
1117
1118                         if (c == '\0')
1119                                 return NULL;
1120                         if (c == '\\')
1121                                 break;
1122                 }
1123         }
1124 }
1125
1126
1127 //========================================================
1128 // strlcat and strlcpy, from OpenBSD
1129
1130 /*
1131  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1132  *
1133  * Permission to use, copy, modify, and distribute this software for any
1134  * purpose with or without fee is hereby granted, provided that the above
1135  * copyright notice and this permission notice appear in all copies.
1136  *
1137  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1138  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1139  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1140  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1141  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1142  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1143  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1144  */
1145
1146 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1147 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1148
1149
1150 #ifndef HAVE_STRLCAT
1151 size_t
1152 strlcat(char *dst, const char *src, size_t siz)
1153 {
1154         register char *d = dst;
1155         register const char *s = src;
1156         register size_t n = siz;
1157         size_t dlen;
1158
1159         /* Find the end of dst and adjust bytes left but don't go past end */
1160         while (n-- != 0 && *d != '\0')
1161                 d++;
1162         dlen = d - dst;
1163         n = siz - dlen;
1164
1165         if (n == 0)
1166                 return(dlen + strlen(s));
1167         while (*s != '\0') {
1168                 if (n != 1) {
1169                         *d++ = *s;
1170                         n--;
1171                 }
1172                 s++;
1173         }
1174         *d = '\0';
1175
1176         return(dlen + (s - src));       /* count does not include NUL */
1177 }
1178 #endif  // #ifndef HAVE_STRLCAT
1179
1180
1181 #ifndef HAVE_STRLCPY
1182 size_t
1183 strlcpy(char *dst, const char *src, size_t siz)
1184 {
1185         register char *d = dst;
1186         register const char *s = src;
1187         register size_t n = siz;
1188
1189         /* Copy as many bytes as will fit */
1190         if (n != 0 && --n != 0) {
1191                 do {
1192                         if ((*d++ = *s++) == 0)
1193                                 break;
1194                 } while (--n != 0);
1195         }
1196
1197         /* Not enough room in dst, add NUL and traverse rest of src */
1198         if (n == 0) {
1199                 if (siz != 0)
1200                         *d = '\0';              /* NUL-terminate dst */
1201                 while (*s++)
1202                         ;
1203         }
1204
1205         return(s - src - 1);    /* count does not include NUL */
1206 }
1207
1208 #endif  // #ifndef HAVE_STRLCPY