use double precision for coord and angle conversions... might help with jerky camera...
[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
256 //
257 // reading functions
258 //
259 int msg_readcount;
260 qboolean msg_badread;
261
262 void MSG_BeginReading (void)
263 {
264         msg_readcount = 0;
265         msg_badread = false;
266 }
267
268 int MSG_ReadLittleShort (void)
269 {
270         if (msg_readcount+2 > net_message.cursize)
271         {
272                 msg_badread = true;
273                 return -1;
274         }
275         msg_readcount += 2;
276         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
277 }
278
279 int MSG_ReadBigShort (void)
280 {
281         if (msg_readcount+2 > net_message.cursize)
282         {
283                 msg_badread = true;
284                 return -1;
285         }
286         msg_readcount += 2;
287         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
288 }
289
290 int MSG_ReadLittleLong (void)
291 {
292         if (msg_readcount+4 > net_message.cursize)
293         {
294                 msg_badread = true;
295                 return -1;
296         }
297         msg_readcount += 4;
298         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);
299 }
300
301 int MSG_ReadBigLong (void)
302 {
303         if (msg_readcount+4 > net_message.cursize)
304         {
305                 msg_badread = true;
306                 return -1;
307         }
308         msg_readcount += 4;
309         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];
310 }
311
312 float MSG_ReadLittleFloat (void)
313 {
314         union
315         {
316                 float f;
317                 int l;
318         } dat;
319         if (msg_readcount+4 > net_message.cursize)
320         {
321                 msg_badread = true;
322                 return -1;
323         }
324         msg_readcount += 4;
325         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);
326         return dat.f;
327 }
328
329 float MSG_ReadBigFloat (void)
330 {
331         union
332         {
333                 float f;
334                 int l;
335         } dat;
336         if (msg_readcount+4 > net_message.cursize)
337         {
338                 msg_badread = true;
339                 return -1;
340         }
341         msg_readcount += 4;
342         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];
343         return dat.f;
344 }
345
346 char *MSG_ReadString (void)
347 {
348         static char string[2048];
349         int l,c;
350         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
351                 string[l] = c;
352         string[l] = 0;
353         return string;
354 }
355
356 int MSG_ReadBytes (int numbytes, unsigned char *out)
357 {
358         int l, c;
359         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
360                 out[l] = c;
361         return l;
362 }
363
364 float MSG_ReadCoord13i (void)
365 {
366         return MSG_ReadLittleShort() * (1.0/8.0);
367 }
368
369 float MSG_ReadCoord16i (void)
370 {
371         return (signed short) MSG_ReadLittleShort();
372 }
373
374 float MSG_ReadCoord32f (void)
375 {
376         return MSG_ReadLittleFloat();
377 }
378
379 float MSG_ReadCoord (int protocol)
380 {
381         if (protocol == PROTOCOL_QUAKE)
382                 return MSG_ReadCoord13i();
383         else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5)
384                 return MSG_ReadCoord32f();
385         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
386                 return MSG_ReadCoord16i();
387         Host_Error("MSG_ReadCoord: unknown protocol\n");
388         return 0;
389 }
390
391 void MSG_ReadVector (float *v, int protocol)
392 {
393         v[0] = MSG_ReadCoord(protocol);
394         v[1] = MSG_ReadCoord(protocol);
395         v[2] = MSG_ReadCoord(protocol);
396 }
397
398 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
399 float MSG_ReadAngle8i (void)
400 {
401         return MSG_ReadByte () * (360.0/256.0);
402 }
403
404 float MSG_ReadAngle16i (void)
405 {
406         return MSG_ReadShort () * (360.0/65536.0);
407 }
408
409 float MSG_ReadAngle32f (void)
410 {
411         return MSG_ReadFloat ();
412 }
413
414
415 //===========================================================================
416
417 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
418 {
419         if (startsize < 256)
420                 startsize = 256;
421         buf->mempool = Mem_AllocPool(name, 0, NULL);
422         buf->data = Mem_Alloc(buf->mempool, startsize);
423         buf->maxsize = startsize;
424         buf->cursize = 0;
425 }
426
427
428 void SZ_Free (sizebuf_t *buf)
429 {
430         Mem_FreePool(&buf->mempool);
431         buf->data = NULL;
432         buf->maxsize = 0;
433         buf->cursize = 0;
434 }
435
436 void SZ_Clear (sizebuf_t *buf)
437 {
438         buf->cursize = 0;
439 }
440
441 void *SZ_GetSpace (sizebuf_t *buf, int length)
442 {
443         void *data;
444
445         if (buf->cursize + length > buf->maxsize)
446         {
447                 if (!buf->allowoverflow)
448                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
449
450                 if (length > buf->maxsize)
451                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
452
453                 buf->overflowed = true;
454                 Con_Print("SZ_GetSpace: overflow\n");
455                 SZ_Clear (buf);
456         }
457
458         data = buf->data + buf->cursize;
459         buf->cursize += length;
460
461         return data;
462 }
463
464 void SZ_Write (sizebuf_t *buf, const void *data, int length)
465 {
466         memcpy (SZ_GetSpace(buf,length),data,length);
467 }
468
469 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
470 // attention, it has been eradicated from here, its only (former) use in
471 // all of darkplaces.
472
473 static char *hexchar = "0123456789ABCDEF";
474 void Com_HexDumpToConsole(const qbyte *data, int size)
475 {
476         int i, j, n;
477         char text[1024];
478         char *cur, *flushpointer;
479         const qbyte *d;
480         cur = text;
481         flushpointer = text + 512;
482         for (i = 0;i < size;)
483         {
484                 n = 16;
485                 if (n > size - i)
486                         n = size - i;
487                 d = data + i;
488                 // print offset
489                 *cur++ = hexchar[(i >> 12) & 15];
490                 *cur++ = hexchar[(i >>  8) & 15];
491                 *cur++ = hexchar[(i >>  4) & 15];
492                 *cur++ = hexchar[(i >>  0) & 15];
493                 *cur++ = ':';
494                 // print hex
495                 for (j = 0;j < 16;j++)
496                 {
497                         if (j < n)
498                         {
499                                 *cur++ = hexchar[(d[j] >> 4) & 15];
500                                 *cur++ = hexchar[(d[j] >> 0) & 15];
501                         }
502                         else
503                         {
504                                 *cur++ = ' ';
505                                 *cur++ = ' ';
506                         }
507                         if ((j & 3) == 0)
508                                 *cur++ = ' ';
509                 }
510                 // print text
511                 for (j = 0;j < 16;j++)
512                 {
513                         if (j < n)
514                         {
515                                 if (d[j] >= ' ' && d[j] <= 127)
516                                         *cur++ = d[j];
517                                 else
518                                         *cur++ = '.';
519                         }
520                         else
521                                 *cur++ = ' ';
522                 }
523                 *cur++ = '\n';
524                 i += n;
525                 if (cur >= flushpointer || i >= size)
526                 {
527                         *cur++ = 0;
528                         Con_Print(text);
529                         cur = text;
530                 }
531         }
532 }
533
534 void SZ_HexDumpToConsole(const sizebuf_t *buf)
535 {
536         Com_HexDumpToConsole(buf->data, buf->cursize);
537 }
538
539
540 //============================================================================
541
542
543 /*
544 ==============
545 COM_ParseToken
546
547 Parse a token out of a string
548 ==============
549 */
550 int COM_ParseToken(const char **datapointer, int returnnewline)
551 {
552         int len;
553         const char *data = *datapointer;
554
555         len = 0;
556         com_token[0] = 0;
557
558         if (!data)
559         {
560                 *datapointer = NULL;
561                 return false;
562         }
563
564 // skip whitespace
565 skipwhite:
566         for (;*data <= ' ' && (*data != '\n' || !returnnewline);data++)
567         {
568                 if (*data == 0)
569                 {
570                         // end of file
571                         *datapointer = NULL;
572                         return false;
573                 }
574         }
575
576         if (data[0] == '/' && data[1] == '/')
577         {
578                 // comment
579                 while (*data && *data != '\n')
580                         data++;
581                 goto skipwhite;
582         }
583         else if (data[0] == '/' && data[1] == '*')
584         {
585                 // comment
586                 data++;
587                 while (*data && (data[0] != '*' || data[1] != '/'))
588                         data++;
589                 data += 2;
590                 goto skipwhite;
591         }
592         else if (*data == '\"')
593         {
594                 // quoted string
595                 for (data++;*data != '\"';data++)
596                 {
597                         if (!*data || len >= (int)sizeof(com_token) - 1)
598                         {
599                                 com_token[0] = 0;
600                                 *datapointer = NULL;
601                                 return false;
602                         }
603                         com_token[len++] = *data;
604                 }
605                 com_token[len] = 0;
606                 *datapointer = data+1;
607                 return true;
608         }
609         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
610         {
611                 // single character
612                 com_token[len++] = *data++;
613                 com_token[len] = 0;
614                 *datapointer = data;
615                 return true;
616         }
617         else
618         {
619                 // regular word
620                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';';data++)
621                 {
622                         if (len >= (int)sizeof(com_token) - 1)
623                         {
624                                 com_token[0] = 0;
625                                 *datapointer = NULL;
626                                 return false;
627                         }
628                         com_token[len++] = *data;
629                 }
630                 com_token[len] = 0;
631                 *datapointer = data;
632                 return true;
633         }
634 }
635
636 /*
637 ==============
638 COM_ParseTokenConsole
639
640 Parse a token out of a string, behaving like the qwcl console
641 ==============
642 */
643 int COM_ParseTokenConsole(const char **datapointer)
644 {
645         int len;
646         const char *data = *datapointer;
647
648         len = 0;
649         com_token[0] = 0;
650
651         if (!data)
652         {
653                 *datapointer = NULL;
654                 return false;
655         }
656
657 // skip whitespace
658 skipwhite:
659         for (;*data <= ' ';data++)
660         {
661                 if (*data == 0)
662                 {
663                         // end of file
664                         *datapointer = NULL;
665                         return false;
666                 }
667         }
668
669         if (*data == '/' && data[1] == '/')
670         {
671                 // comment
672                 while (*data && *data != '\n')
673                         data++;
674                 goto skipwhite;
675         }
676         else if (*data == '\"')
677         {
678                 // quoted string
679                 for (data++;*data != '\"';data++)
680                 {
681                         if (!*data || len >= (int)sizeof(com_token) - 1)
682                         {
683                                 com_token[0] = 0;
684                                 *datapointer = NULL;
685                                 return false;
686                         }
687                         com_token[len++] = *data;
688                 }
689                 com_token[len] = 0;
690                 *datapointer = data+1;
691                 return true;
692         }
693         else
694         {
695                 // regular word
696                 for (;*data > ' ';data++)
697                 {
698                         if (len >= (int)sizeof(com_token) - 1)
699                         {
700                                 com_token[0] = 0;
701                                 *datapointer = NULL;
702                                 return false;
703                         }
704                         com_token[len++] = *data;
705                 }
706                 com_token[len] = 0;
707                 *datapointer = data;
708                 return true;
709         }
710 }
711
712
713 /*
714 ================
715 COM_CheckParm
716
717 Returns the position (1 to argc-1) in the program's argument list
718 where the given parameter apears, or 0 if not present
719 ================
720 */
721 int COM_CheckParm (const char *parm)
722 {
723         int i;
724
725         for (i=1 ; i<com_argc ; i++)
726         {
727                 if (!com_argv[i])
728                         continue;               // NEXTSTEP sometimes clears appkit vars.
729                 if (!strcmp (parm,com_argv[i]))
730                         return i;
731         }
732
733         return 0;
734 }
735
736 /*
737 ================
738 COM_CheckRegistered
739
740 Looks for the pop.txt file and verifies it.
741 Sets the "registered" cvar.
742 Immediately exits out if an alternate game was attempted to be started without
743 being registered.
744 ================
745 */
746 void COM_CheckRegistered (void)
747 {
748         Cvar_Set ("cmdline", com_cmdline);
749
750         if (!FS_FileExists("gfx/pop.lmp"))
751         {
752                 if (fs_modified)
753                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
754                 else
755                         Con_Print("Playing shareware version.\n");
756                 return;
757         }
758
759         Cvar_Set ("registered", "1");
760         Con_Print("Playing registered version.\n");
761 }
762
763
764 /*
765 ================
766 COM_InitArgv
767 ================
768 */
769 void COM_InitArgv (void)
770 {
771         int i, j, n;
772         // reconstitute the command line for the cmdline externally visible cvar
773         n = 0;
774         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
775         {
776                 i = 0;
777                 if (strstr(com_argv[j], " "))
778                 {
779                         // arg contains whitespace, store quotes around it
780                         com_cmdline[n++] = '\"';
781                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
782                                 com_cmdline[n++] = com_argv[j][i++];
783                         com_cmdline[n++] = '\"';
784                 }
785                 else
786                 {
787                         while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
788                                 com_cmdline[n++] = com_argv[j][i++];
789                 }
790                 if (n < (CMDLINE_LENGTH - 1))
791                         com_cmdline[n++] = ' ';
792                 else
793                         break;
794         }
795         com_cmdline[n] = 0;
796 }
797
798
799 //===========================================================================
800
801 // Game mods
802
803 typedef struct
804 {
805         const char* prog_name;
806         const char* cmdline;
807         const char* gamename;
808         const char* gamedirname;
809         const char* gamescreenshotname;
810 } gamemode_info_t;
811
812 static const gamemode_info_t gamemode_info [] =
813 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
814
815 // GAME_NORMAL
816 { "",                           "-quake",               "DarkPlaces-Quake",             "",                     "dp" },
817 // GAME_HIPNOTIC
818 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "hipnotic",     "dp" },
819 // GAME_ROGUE
820 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "rogue",        "dp" },
821 // GAME_NEHAHRA
822 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "nehahra",      "dp" },
823 // GAME_NEXUIZ
824 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         "nexuiz" },
825 // GAME_TRANSFUSION
826 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       "transfusion" },
827 // GAME_GOODVSBAD2
828 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          "gvb2" },
829 // GAME_TEU
830 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      "teu" },
831 // GAME_BATTLEMECH
832 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         "battlemech" },
833 // GAME_ZYMOTIC
834 { "zymotic",            "-zymotic",             "Zymotic",                              "data",         "zymotic" },
835 // GAME_FNIGGIUM
836 { "fniggium",           "-fniggium",    "Fniggium",                             "data",         "fniggium" },
837 // GAME_SETHERAL
838 { "setheral",           "-setheral",    "Setheral",                             "data",         "setheral" },
839 // GAME_SOM
840 { "som",                        "-som",                 "Son of Man",                   "sonofman",     "som" },
841 // GAME_TENEBRAE
842 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "tenebrae",     "dp" },
843 // GAME_NEOTERIC
844 { "neoteric",           "-neoteric",    "Neoteric",                             "neobase",      "neo" },
845 // GAME_OPENQUARTZ
846 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          "openquartz"},
847 // GAME_PRYDON
848 { "prydon",                     "-prydon",              "PrydonGate",                   "prydon",       "prydon"},
849 // GAME_NETHERWORLD
850 { "netherworld",        "-netherworld", "Dark Masters",                 "netherworld",  "nw"},
851 };
852
853 void COM_InitGameType (void)
854 {
855         char name [MAX_OSPATH];
856         unsigned int i;
857
858         FS_StripExtension (com_argv[0], name, sizeof (name));
859         COM_ToLowerString (name, name, sizeof (name));
860
861         // Check the binary name; default to GAME_NORMAL (0)
862         gamemode = GAME_NORMAL;
863         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
864                 if (strstr (name, gamemode_info[i].prog_name))
865                 {
866                         gamemode = i;
867                         break;
868                 }
869
870         // Look for a command-line option
871         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
872                 if (COM_CheckParm (gamemode_info[i].cmdline))
873                 {
874                         gamemode = i;
875                         break;
876                 }
877
878         gamename = gamemode_info[gamemode].gamename;
879         gamedirname = gamemode_info[gamemode].gamedirname;
880         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
881 }
882
883
884 extern void Mathlib_Init(void);
885 extern void FS_Init (void);
886
887 /*
888 ================
889 COM_Init
890 ================
891 */
892 void COM_Init (void)
893 {
894         Cvar_RegisterVariable (&registered);
895         Cvar_RegisterVariable (&cmdline);
896
897         Mathlib_Init();
898
899         FS_Init ();
900         Log_Init ();
901         COM_CheckRegistered ();
902 }
903
904
905 /*
906 ============
907 va
908
909 does a varargs printf into a temp buffer, so I don't need to have
910 varargs versions of all text functions.
911 FIXME: make this buffer size safe someday
912 ============
913 */
914 char *va(const char *format, ...)
915 {
916         va_list argptr;
917         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
918         static char string[8][1024], *s;
919         static int stringindex = 0;
920
921         s = string[stringindex];
922         stringindex = (stringindex + 1) & 7;
923         va_start (argptr, format);
924         vsnprintf (s, sizeof (string[0]), format,argptr);
925         va_end (argptr);
926
927         return s;
928 }
929
930
931 //======================================
932
933 void COM_ToLowerString (const char *in, char *out, size_t size_out)
934 {
935         if (size_out == 0)
936                 return;
937
938         while (*in && size_out > 1)
939         {
940                 if (*in >= 'A' && *in <= 'Z')
941                         *out++ = *in++ + 'a' - 'A';
942                 else
943                         *out++ = *in++;
944                 size_out--;
945         }
946         *out = '\0';
947 }
948
949 void COM_ToUpperString (const char *in, char *out, size_t size_out)
950 {
951         if (size_out == 0)
952                 return;
953
954         while (*in && size_out > 1)
955         {
956                 if (*in >= 'a' && *in <= 'z')
957                         *out++ = *in++ + 'A' - 'a';
958                 else
959                         *out++ = *in++;
960                 size_out--;
961         }
962         *out = '\0';
963 }
964
965 int COM_StringBeginsWith(const char *s, const char *match)
966 {
967         for (;*s && *match;s++, match++)
968                 if (*s != *match)
969                         return false;
970         return true;
971 }
972
973 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
974 {
975         int argc, commentprefixlength;
976         char *tokenbufend;
977         const char *l;
978         argc = 0;
979         tokenbufend = tokenbuf + tokenbufsize;
980         l = *text;
981         commentprefixlength = 0;
982         if (commentprefix)
983                 commentprefixlength = strlen(commentprefix);
984         while (*l && *l != '\n')
985         {
986                 if (*l > ' ')
987                 {
988                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
989                         {
990                                 while (*l && *l != '\n')
991                                         l++;
992                                 break;
993                         }
994                         if (argc >= maxargc)
995                                 return -1;
996                         argv[argc++] = tokenbuf;
997                         if (*l == '"')
998                         {
999                                 l++;
1000                                 while (*l && *l != '"')
1001                                 {
1002                                         if (tokenbuf >= tokenbufend)
1003                                                 return -1;
1004                                         *tokenbuf++ = *l++;
1005                                 }
1006                                 if (*l == '"')
1007                                         l++;
1008                         }
1009                         else
1010                         {
1011                                 while (*l > ' ')
1012                                 {
1013                                         if (tokenbuf >= tokenbufend)
1014                                                 return -1;
1015                                         *tokenbuf++ = *l++;
1016                                 }
1017                         }
1018                         if (tokenbuf >= tokenbufend)
1019                                 return -1;
1020                         *tokenbuf++ = 0;
1021                 }
1022                 else
1023                         l++;
1024         }
1025         if (*l == '\n')
1026                 l++;
1027         *text = l;
1028         return argc;
1029 }
1030
1031 // written by Elric, thanks Elric!
1032 char *SearchInfostring(const char *infostring, const char *key)
1033 {
1034         static char value [256];
1035         char crt_key [256];
1036         size_t value_ind, key_ind;
1037         char c;
1038
1039         if (*infostring++ != '\\')
1040                 return NULL;
1041
1042         value_ind = 0;
1043         for (;;)
1044         {
1045                 key_ind = 0;
1046
1047                 // Get the key name
1048                 for (;;)
1049                 {
1050                         c = *infostring++;
1051
1052                         if (c == '\0')
1053                                 return NULL;
1054                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1055                         {
1056                                 crt_key[key_ind] = '\0';
1057                                 break;
1058                         }
1059
1060                         crt_key[key_ind++] = c;
1061                 }
1062
1063                 // If it's the key we are looking for, save it in "value"
1064                 if (!strcmp(crt_key, key))
1065                 {
1066                         for (;;)
1067                         {
1068                                 c = *infostring++;
1069
1070                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1071                                 {
1072                                         value[value_ind] = '\0';
1073                                         return value;
1074                                 }
1075
1076                                 value[value_ind++] = c;
1077                         }
1078                 }
1079
1080                 // Else, skip the value
1081                 for (;;)
1082                 {
1083                         c = *infostring++;
1084
1085                         if (c == '\0')
1086                                 return NULL;
1087                         if (c == '\\')
1088                                 break;
1089                 }
1090         }
1091 }
1092
1093
1094 //========================================================
1095 // strlcat and strlcpy, from OpenBSD
1096
1097 /*
1098  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1099  *
1100  * Permission to use, copy, modify, and distribute this software for any
1101  * purpose with or without fee is hereby granted, provided that the above
1102  * copyright notice and this permission notice appear in all copies.
1103  *
1104  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1105  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1106  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1107  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1108  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1109  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1110  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1111  */
1112
1113 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1114 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1115
1116
1117 #ifndef HAVE_STRLCAT
1118 size_t
1119 strlcat(char *dst, const char *src, size_t siz)
1120 {
1121         register char *d = dst;
1122         register const char *s = src;
1123         register size_t n = siz;
1124         size_t dlen;
1125
1126         /* Find the end of dst and adjust bytes left but don't go past end */
1127         while (n-- != 0 && *d != '\0')
1128                 d++;
1129         dlen = d - dst;
1130         n = siz - dlen;
1131
1132         if (n == 0)
1133                 return(dlen + strlen(s));
1134         while (*s != '\0') {
1135                 if (n != 1) {
1136                         *d++ = *s;
1137                         n--;
1138                 }
1139                 s++;
1140         }
1141         *d = '\0';
1142
1143         return(dlen + (s - src));       /* count does not include NUL */
1144 }
1145 #endif  // #ifndef HAVE_STRLCAT
1146
1147
1148 #ifndef HAVE_STRLCPY
1149 size_t
1150 strlcpy(char *dst, const char *src, size_t siz)
1151 {
1152         register char *d = dst;
1153         register const char *s = src;
1154         register size_t n = siz;
1155
1156         /* Copy as many bytes as will fit */
1157         if (n != 0 && --n != 0) {
1158                 do {
1159                         if ((*d++ = *s++) == 0)
1160                                 break;
1161                 } while (--n != 0);
1162         }
1163
1164         /* Not enough room in dst, add NUL and traverse rest of src */
1165         if (n == 0) {
1166                 if (siz != 0)
1167                         *d = '\0';              /* NUL-terminate dst */
1168                 while (*s++)
1169                         ;
1170         }
1171
1172         return(s - src - 1);    /* count does not include NUL */
1173 }
1174
1175 #endif  // #ifndef HAVE_STRLCPY