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