MSG_ReadAngle functions now return -180 to +180 range instead of 0-360 range which...
[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)
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 { "",                           "-quake",               "DarkPlaces-Quake",             "",                     "dp" },
832 // GAME_HIPNOTIC
833 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "hipnotic",     "dp" },
834 // GAME_ROGUE
835 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "rogue",        "dp" },
836 // GAME_NEHAHRA
837 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "nehahra",      "dp" },
838 // GAME_NEXUIZ
839 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         "nexuiz" },
840 // GAME_TRANSFUSION
841 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       "transfusion" },
842 // GAME_GOODVSBAD2
843 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          "gvb2" },
844 // GAME_TEU
845 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      "teu" },
846 // GAME_BATTLEMECH
847 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         "battlemech" },
848 // GAME_ZYMOTIC
849 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         "zymotic" },
850 // GAME_FNIGGIUM
851 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         "fniggium" },
852 // GAME_SETHERAL
853 { "setheral",           "-setheral",    "Setheral",                             "data",         "setheral" },
854 // GAME_SOM
855 { "som",                        "-som",                 "Son of Man",                   "sonofman",     "som" },
856 // GAME_TENEBRAE
857 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "tenebrae",     "dp" },
858 // GAME_NEOTERIC
859 { "neoteric",           "-neoteric",    "Neoteric",                             "neobase",      "neo" },
860 // GAME_OPENQUARTZ
861 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          "openquartz"},
862 // GAME_PRYDON
863 { "prydon",                     "-prydon",              "PrydonGate",                   "prydon",       "prydon"},
864 // GAME_NETHERWORLD
865 { "netherworld",        "-netherworld", "Dark Masters",                 "netherworld",  "nw"},
866 };
867
868 void COM_InitGameType (void)
869 {
870         char name [MAX_OSPATH];
871         unsigned int i;
872
873         FS_StripExtension (com_argv[0], name, sizeof (name));
874         COM_ToLowerString (name, name, sizeof (name));
875
876         // Check the binary name; default to GAME_NORMAL (0)
877         gamemode = GAME_NORMAL;
878         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
879                 if (strstr (name, gamemode_info[i].prog_name))
880                 {
881                         gamemode = i;
882                         break;
883                 }
884
885         // Look for a command-line option
886         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
887                 if (COM_CheckParm (gamemode_info[i].cmdline))
888                 {
889                         gamemode = i;
890                         break;
891                 }
892
893         gamename = gamemode_info[gamemode].gamename;
894         gamedirname = gamemode_info[gamemode].gamedirname;
895         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
896 }
897
898
899 extern void Mathlib_Init(void);
900 extern void FS_Init (void);
901
902 /*
903 ================
904 COM_Init
905 ================
906 */
907 void COM_Init (void)
908 {
909         Cvar_RegisterVariable (&registered);
910         Cvar_RegisterVariable (&cmdline);
911
912         Mathlib_Init();
913
914         FS_Init ();
915         Log_Init ();
916         COM_CheckRegistered ();
917 }
918
919
920 /*
921 ============
922 va
923
924 does a varargs printf into a temp buffer, so I don't need to have
925 varargs versions of all text functions.
926 FIXME: make this buffer size safe someday
927 ============
928 */
929 char *va(const char *format, ...)
930 {
931         va_list argptr;
932         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
933         static char string[8][1024], *s;
934         static int stringindex = 0;
935
936         s = string[stringindex];
937         stringindex = (stringindex + 1) & 7;
938         va_start (argptr, format);
939         vsnprintf (s, sizeof (string[0]), format,argptr);
940         va_end (argptr);
941
942         return s;
943 }
944
945
946 //======================================
947
948 void COM_ToLowerString (const char *in, char *out, size_t size_out)
949 {
950         if (size_out == 0)
951                 return;
952
953         while (*in && size_out > 1)
954         {
955                 if (*in >= 'A' && *in <= 'Z')
956                         *out++ = *in++ + 'a' - 'A';
957                 else
958                         *out++ = *in++;
959                 size_out--;
960         }
961         *out = '\0';
962 }
963
964 void COM_ToUpperString (const char *in, char *out, size_t size_out)
965 {
966         if (size_out == 0)
967                 return;
968
969         while (*in && size_out > 1)
970         {
971                 if (*in >= 'a' && *in <= 'z')
972                         *out++ = *in++ + 'A' - 'a';
973                 else
974                         *out++ = *in++;
975                 size_out--;
976         }
977         *out = '\0';
978 }
979
980 int COM_StringBeginsWith(const char *s, const char *match)
981 {
982         for (;*s && *match;s++, match++)
983                 if (*s != *match)
984                         return false;
985         return true;
986 }
987
988 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
989 {
990         int argc, commentprefixlength;
991         char *tokenbufend;
992         const char *l;
993         argc = 0;
994         tokenbufend = tokenbuf + tokenbufsize;
995         l = *text;
996         commentprefixlength = 0;
997         if (commentprefix)
998                 commentprefixlength = strlen(commentprefix);
999         while (*l && *l != '\n')
1000         {
1001                 if (*l > ' ')
1002                 {
1003                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1004                         {
1005                                 while (*l && *l != '\n')
1006                                         l++;
1007                                 break;
1008                         }
1009                         if (argc >= maxargc)
1010                                 return -1;
1011                         argv[argc++] = tokenbuf;
1012                         if (*l == '"')
1013                         {
1014                                 l++;
1015                                 while (*l && *l != '"')
1016                                 {
1017                                         if (tokenbuf >= tokenbufend)
1018                                                 return -1;
1019                                         *tokenbuf++ = *l++;
1020                                 }
1021                                 if (*l == '"')
1022                                         l++;
1023                         }
1024                         else
1025                         {
1026                                 while (*l > ' ')
1027                                 {
1028                                         if (tokenbuf >= tokenbufend)
1029                                                 return -1;
1030                                         *tokenbuf++ = *l++;
1031                                 }
1032                         }
1033                         if (tokenbuf >= tokenbufend)
1034                                 return -1;
1035                         *tokenbuf++ = 0;
1036                 }
1037                 else
1038                         l++;
1039         }
1040         if (*l == '\n')
1041                 l++;
1042         *text = l;
1043         return argc;
1044 }
1045
1046 // written by Elric, thanks Elric!
1047 char *SearchInfostring(const char *infostring, const char *key)
1048 {
1049         static char value [256];
1050         char crt_key [256];
1051         size_t value_ind, key_ind;
1052         char c;
1053
1054         if (*infostring++ != '\\')
1055                 return NULL;
1056
1057         value_ind = 0;
1058         for (;;)
1059         {
1060                 key_ind = 0;
1061
1062                 // Get the key name
1063                 for (;;)
1064                 {
1065                         c = *infostring++;
1066
1067                         if (c == '\0')
1068                                 return NULL;
1069                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1070                         {
1071                                 crt_key[key_ind] = '\0';
1072                                 break;
1073                         }
1074
1075                         crt_key[key_ind++] = c;
1076                 }
1077
1078                 // If it's the key we are looking for, save it in "value"
1079                 if (!strcmp(crt_key, key))
1080                 {
1081                         for (;;)
1082                         {
1083                                 c = *infostring++;
1084
1085                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1086                                 {
1087                                         value[value_ind] = '\0';
1088                                         return value;
1089                                 }
1090
1091                                 value[value_ind++] = c;
1092                         }
1093                 }
1094
1095                 // Else, skip the value
1096                 for (;;)
1097                 {
1098                         c = *infostring++;
1099
1100                         if (c == '\0')
1101                                 return NULL;
1102                         if (c == '\\')
1103                                 break;
1104                 }
1105         }
1106 }
1107
1108
1109 //========================================================
1110 // strlcat and strlcpy, from OpenBSD
1111
1112 /*
1113  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1114  *
1115  * Permission to use, copy, modify, and distribute this software for any
1116  * purpose with or without fee is hereby granted, provided that the above
1117  * copyright notice and this permission notice appear in all copies.
1118  *
1119  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1120  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1121  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1122  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1123  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1124  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1125  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1126  */
1127
1128 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1129 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1130
1131
1132 #ifndef HAVE_STRLCAT
1133 size_t
1134 strlcat(char *dst, const char *src, size_t siz)
1135 {
1136         register char *d = dst;
1137         register const char *s = src;
1138         register size_t n = siz;
1139         size_t dlen;
1140
1141         /* Find the end of dst and adjust bytes left but don't go past end */
1142         while (n-- != 0 && *d != '\0')
1143                 d++;
1144         dlen = d - dst;
1145         n = siz - dlen;
1146
1147         if (n == 0)
1148                 return(dlen + strlen(s));
1149         while (*s != '\0') {
1150                 if (n != 1) {
1151                         *d++ = *s;
1152                         n--;
1153                 }
1154                 s++;
1155         }
1156         *d = '\0';
1157
1158         return(dlen + (s - src));       /* count does not include NUL */
1159 }
1160 #endif  // #ifndef HAVE_STRLCAT
1161
1162
1163 #ifndef HAVE_STRLCPY
1164 size_t
1165 strlcpy(char *dst, const char *src, size_t siz)
1166 {
1167         register char *d = dst;
1168         register const char *s = src;
1169         register size_t n = siz;
1170
1171         /* Copy as many bytes as will fit */
1172         if (n != 0 && --n != 0) {
1173                 do {
1174                         if ((*d++ = *s++) == 0)
1175                                 break;
1176                 } while (--n != 0);
1177         }
1178
1179         /* Not enough room in dst, add NUL and traverse rest of src */
1180         if (n == 0) {
1181                 if (siz != 0)
1182                         *d = '\0';              /* NUL-terminate dst */
1183                 while (*s++)
1184                         ;
1185         }
1186
1187         return(s - src - 1);    /* count does not include NUL */
1188 }
1189
1190 #endif  // #ifndef HAVE_STRLCPY