implemented occlusion query support on corona rendering, this enables
[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 "quakedef.h"
23
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #ifndef WIN32
27 #include <unistd.h>
28 #endif
29
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
32
33 char com_token[MAX_INPUTLINE];
34 int com_argc;
35 const char **com_argv;
36
37 gamemode_t gamemode;
38 const char *gamename;
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
44
45
46 /*
47 ============================================================================
48
49                                         BYTE ORDER FUNCTIONS
50
51 ============================================================================
52 */
53
54 short   ShortSwap (short l)
55 {
56         unsigned char    b1,b2;
57
58         b1 = l&255;
59         b2 = (l>>8)&255;
60
61         return (b1<<8) + b2;
62 }
63
64 int    LongSwap (int l)
65 {
66         unsigned char    b1,b2,b3,b4;
67
68         b1 = l&255;
69         b2 = (l>>8)&255;
70         b3 = (l>>16)&255;
71         b4 = (l>>24)&255;
72
73         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
74 }
75
76 float FloatSwap (float f)
77 {
78         union
79         {
80                 float   f;
81                 unsigned char    b[4];
82         } dat1, dat2;
83
84
85         dat1.f = f;
86         dat2.b[0] = dat1.b[3];
87         dat2.b[1] = dat1.b[2];
88         dat2.b[2] = dat1.b[1];
89         dat2.b[3] = dat1.b[0];
90         return dat2.f;
91 }
92
93
94 // Extract integers from buffers
95
96 unsigned int BuffBigLong (const unsigned char *buffer)
97 {
98         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
99 }
100
101 unsigned short BuffBigShort (const unsigned char *buffer)
102 {
103         return (buffer[0] << 8) | buffer[1];
104 }
105
106 unsigned int BuffLittleLong (const unsigned char *buffer)
107 {
108         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
109 }
110
111 unsigned short BuffLittleShort (const unsigned char *buffer)
112 {
113         return (buffer[1] << 8) | buffer[0];
114 }
115
116
117 /*
118 ============================================================================
119
120                                         CRC FUNCTIONS
121
122 ============================================================================
123 */
124
125 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
126 // and the initial and final xor values shown below...  in other words, the
127 // CCITT standard CRC used by XMODEM
128
129 #define CRC_INIT_VALUE  0xffff
130 #define CRC_XOR_VALUE   0x0000
131
132 static unsigned short crctable[256] =
133 {
134         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
135         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
136         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
137         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
138         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
139         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
140         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
141         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
142         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
143         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
144         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
145         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
146         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
147         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
148         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
149         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
150         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
151         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
152         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
153         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
154         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
155         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
156         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
157         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
158         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
159         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
160         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
161         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
162         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
163         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
164         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
165         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
166 };
167
168 unsigned short CRC_Block(const unsigned char *data, size_t size)
169 {
170         unsigned short crc = CRC_INIT_VALUE;
171         while (size--)
172                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
173         return crc ^ CRC_XOR_VALUE;
174 }
175
176 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
177 {
178         unsigned short crc = CRC_INIT_VALUE;
179         while (size--)
180                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
181         return crc ^ CRC_XOR_VALUE;
182 }
183
184 // QuakeWorld
185 static unsigned char chktbl[1024 + 4] =
186 {
187         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
188         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
189         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
190         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
191         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
192         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
193         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
194         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
195         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
196         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
197         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
198         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
199         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
200         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
201         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
202         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
203         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
204         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
205         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
206         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
207         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
208         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
209         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
210         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
211         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
212         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
213         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
214         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
215         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
216         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
217         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
218         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
219
220         // map checksum goes here
221         0x00,0x00,0x00,0x00
222 };
223
224 // QuakeWorld
225 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
226 {
227         unsigned char *p;
228         unsigned char chkb[60 + 4];
229
230         p = chktbl + (sequence % (sizeof(chktbl) - 8));
231
232         if (length > 60)
233                 length = 60;
234         memcpy(chkb, base, length);
235
236         chkb[length] = (sequence & 0xff) ^ p[0];
237         chkb[length+1] = p[1];
238         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
239         chkb[length+3] = p[3];
240
241         return CRC_Block(chkb, length + 4) & 0xff;
242 }
243
244 /*
245 ==============================================================================
246
247                         MESSAGE IO FUNCTIONS
248
249 Handles byte ordering and avoids alignment errors
250 ==============================================================================
251 */
252
253 //
254 // writing functions
255 //
256
257 void MSG_WriteChar (sizebuf_t *sb, int c)
258 {
259         unsigned char    *buf;
260
261         buf = SZ_GetSpace (sb, 1);
262         buf[0] = c;
263 }
264
265 void MSG_WriteByte (sizebuf_t *sb, int c)
266 {
267         unsigned char    *buf;
268
269         buf = SZ_GetSpace (sb, 1);
270         buf[0] = c;
271 }
272
273 void MSG_WriteShort (sizebuf_t *sb, int c)
274 {
275         unsigned char    *buf;
276
277         buf = SZ_GetSpace (sb, 2);
278         buf[0] = c&0xff;
279         buf[1] = c>>8;
280 }
281
282 void MSG_WriteLong (sizebuf_t *sb, int c)
283 {
284         unsigned char    *buf;
285
286         buf = SZ_GetSpace (sb, 4);
287         buf[0] = c&0xff;
288         buf[1] = (c>>8)&0xff;
289         buf[2] = (c>>16)&0xff;
290         buf[3] = c>>24;
291 }
292
293 void MSG_WriteFloat (sizebuf_t *sb, float f)
294 {
295         union
296         {
297                 float   f;
298                 int     l;
299         } dat;
300
301
302         dat.f = f;
303         dat.l = LittleLong (dat.l);
304
305         SZ_Write (sb, (unsigned char *)&dat.l, 4);
306 }
307
308 void MSG_WriteString (sizebuf_t *sb, const char *s)
309 {
310         if (!s || !*s)
311                 MSG_WriteChar (sb, 0);
312         else
313                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
314 }
315
316 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
317 {
318         if (s && *s)
319                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
320 }
321
322 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
323 {
324         if (f >= 0)
325                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
326         else
327                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
328 }
329
330 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
331 {
332         if (f >= 0)
333                 MSG_WriteShort (sb, (int)(f + 0.5));
334         else
335                 MSG_WriteShort (sb, (int)(f - 0.5));
336 }
337
338 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
339 {
340         MSG_WriteFloat (sb, f);
341 }
342
343 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
344 {
345         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
346                 MSG_WriteCoord13i (sb, f);
347         else if (protocol == PROTOCOL_DARKPLACES1)
348                 MSG_WriteCoord32f (sb, f);
349         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
350                 MSG_WriteCoord16i (sb, f);
351         else
352                 MSG_WriteCoord32f (sb, f);
353 }
354
355 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
356 {
357         MSG_WriteCoord (sb, v[0], protocol);
358         MSG_WriteCoord (sb, v[1], protocol);
359         MSG_WriteCoord (sb, v[2], protocol);
360 }
361
362 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
363 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
364 {
365         if (f >= 0)
366                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
367         else
368                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
369 }
370
371 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
372 {
373         if (f >= 0)
374                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
375         else
376                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
377 }
378
379 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
380 {
381         MSG_WriteFloat (sb, f);
382 }
383
384 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
385 {
386         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
387                 MSG_WriteAngle8i (sb, f);
388         else
389                 MSG_WriteAngle16i (sb, f);
390 }
391
392 //
393 // reading functions
394 //
395 int msg_readcount;
396 qboolean msg_badread;
397
398 void MSG_BeginReading (void)
399 {
400         msg_readcount = 0;
401         msg_badread = false;
402 }
403
404 int MSG_ReadLittleShort (void)
405 {
406         if (msg_readcount+2 > net_message.cursize)
407         {
408                 msg_badread = true;
409                 return -1;
410         }
411         msg_readcount += 2;
412         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
413 }
414
415 int MSG_ReadBigShort (void)
416 {
417         if (msg_readcount+2 > net_message.cursize)
418         {
419                 msg_badread = true;
420                 return -1;
421         }
422         msg_readcount += 2;
423         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
424 }
425
426 int MSG_ReadLittleLong (void)
427 {
428         if (msg_readcount+4 > net_message.cursize)
429         {
430                 msg_badread = true;
431                 return -1;
432         }
433         msg_readcount += 4;
434         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);
435 }
436
437 int MSG_ReadBigLong (void)
438 {
439         if (msg_readcount+4 > net_message.cursize)
440         {
441                 msg_badread = true;
442                 return -1;
443         }
444         msg_readcount += 4;
445         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];
446 }
447
448 float MSG_ReadLittleFloat (void)
449 {
450         union
451         {
452                 float f;
453                 int l;
454         } dat;
455         if (msg_readcount+4 > net_message.cursize)
456         {
457                 msg_badread = true;
458                 return -1;
459         }
460         msg_readcount += 4;
461         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);
462         return dat.f;
463 }
464
465 float MSG_ReadBigFloat (void)
466 {
467         union
468         {
469                 float f;
470                 int l;
471         } dat;
472         if (msg_readcount+4 > net_message.cursize)
473         {
474                 msg_badread = true;
475                 return -1;
476         }
477         msg_readcount += 4;
478         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];
479         return dat.f;
480 }
481
482 char *MSG_ReadString (void)
483 {
484         static char string[MAX_INPUTLINE];
485         int l,c;
486         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
487                 string[l] = c;
488         string[l] = 0;
489         return string;
490 }
491
492 int MSG_ReadBytes (int numbytes, unsigned char *out)
493 {
494         int l, c;
495         for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
496                 out[l] = c;
497         return l;
498 }
499
500 float MSG_ReadCoord13i (void)
501 {
502         return MSG_ReadLittleShort() * (1.0/8.0);
503 }
504
505 float MSG_ReadCoord16i (void)
506 {
507         return (signed short) MSG_ReadLittleShort();
508 }
509
510 float MSG_ReadCoord32f (void)
511 {
512         return MSG_ReadLittleFloat();
513 }
514
515 float MSG_ReadCoord (protocolversion_t protocol)
516 {
517         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
518                 return MSG_ReadCoord13i();
519         else if (protocol == PROTOCOL_DARKPLACES1)
520                 return MSG_ReadCoord32f();
521         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
522                 return MSG_ReadCoord16i();
523         else
524                 return MSG_ReadCoord32f();
525 }
526
527 void MSG_ReadVector (float *v, protocolversion_t protocol)
528 {
529         v[0] = MSG_ReadCoord(protocol);
530         v[1] = MSG_ReadCoord(protocol);
531         v[2] = MSG_ReadCoord(protocol);
532 }
533
534 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
535 float MSG_ReadAngle8i (void)
536 {
537         return (signed char) MSG_ReadByte () * (360.0/256.0);
538 }
539
540 float MSG_ReadAngle16i (void)
541 {
542         return (signed short)MSG_ReadShort () * (360.0/65536.0);
543 }
544
545 float MSG_ReadAngle32f (void)
546 {
547         return MSG_ReadFloat ();
548 }
549
550 float MSG_ReadAngle (protocolversion_t protocol)
551 {
552         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
553                 return MSG_ReadAngle8i ();
554         else
555                 return MSG_ReadAngle16i ();
556 }
557
558
559 //===========================================================================
560
561 void SZ_Clear (sizebuf_t *buf)
562 {
563         buf->cursize = 0;
564 }
565
566 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
567 {
568         unsigned char *data;
569
570         if (buf->cursize + length > buf->maxsize)
571         {
572                 if (!buf->allowoverflow)
573                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
574
575                 if (length > buf->maxsize)
576                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
577
578                 buf->overflowed = true;
579                 Con_Print("SZ_GetSpace: overflow\n");
580                 SZ_Clear (buf);
581         }
582
583         data = buf->data + buf->cursize;
584         buf->cursize += length;
585
586         return data;
587 }
588
589 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
590 {
591         memcpy (SZ_GetSpace(buf,length),data,length);
592 }
593
594 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
595 // attention, it has been eradicated from here, its only (former) use in
596 // all of darkplaces.
597
598 static char *hexchar = "0123456789ABCDEF";
599 void Com_HexDumpToConsole(const unsigned char *data, int size)
600 {
601         int i, j, n;
602         char text[1024];
603         char *cur, *flushpointer;
604         const unsigned char *d;
605         cur = text;
606         flushpointer = text + 512;
607         for (i = 0;i < size;)
608         {
609                 n = 16;
610                 if (n > size - i)
611                         n = size - i;
612                 d = data + i;
613                 // print offset
614                 *cur++ = hexchar[(i >> 12) & 15];
615                 *cur++ = hexchar[(i >>  8) & 15];
616                 *cur++ = hexchar[(i >>  4) & 15];
617                 *cur++ = hexchar[(i >>  0) & 15];
618                 *cur++ = ':';
619                 // print hex
620                 for (j = 0;j < 16;j++)
621                 {
622                         if (j < n)
623                         {
624                                 *cur++ = hexchar[(d[j] >> 4) & 15];
625                                 *cur++ = hexchar[(d[j] >> 0) & 15];
626                         }
627                         else
628                         {
629                                 *cur++ = ' ';
630                                 *cur++ = ' ';
631                         }
632                         if ((j & 3) == 3)
633                                 *cur++ = ' ';
634                 }
635                 // print text
636                 for (j = 0;j < 16;j++)
637                 {
638                         if (j < n)
639                         {
640                                 // color change prefix character has to be treated specially
641                                 if (d[j] == STRING_COLOR_TAG)
642                                 {
643                                         *cur++ = STRING_COLOR_TAG;
644                                         *cur++ = STRING_COLOR_TAG;
645                                 }
646                                 else if (d[j] >= (unsigned char) ' ')
647                                         *cur++ = d[j];
648                                 else
649                                         *cur++ = '.';
650                         }
651                         else
652                                 *cur++ = ' ';
653                 }
654                 *cur++ = '\n';
655                 i += n;
656                 if (cur >= flushpointer || i >= size)
657                 {
658                         *cur++ = 0;
659                         Con_Print(text);
660                         cur = text;
661                 }
662         }
663 }
664
665 void SZ_HexDumpToConsole(const sizebuf_t *buf)
666 {
667         Com_HexDumpToConsole(buf->data, buf->cursize);
668 }
669
670
671 //============================================================================
672
673 /*
674 ==============
675 COM_Wordwrap
676
677 Word wraps a string. The wordWidth function is guaranteed to be called exactly
678 once for each word in the string, so it may be stateful, no idea what that
679 would be good for any more. At the beginning of the string, it will be called
680 for the char 0 to initialize a clean state, and then once with the string " "
681 (a space) so the routine knows how long a space is.
682
683 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
684
685 The sum of the return values of the processLine function will be returned.
686 ==============
687 */
688 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
689 {
690         // Logic is as follows:
691         //
692         // For each word or whitespace:
693         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
694         //   Space found? Always add it to the current line, no matter if it fits.
695         //   Word found? Check if current line + current word fits.
696         //     If it fits, append it. Continue.
697         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
698
699         qboolean isContinuation = false;
700         float spaceWidth;
701         const char *startOfLine = string;
702         const char *cursor = string;
703         const char *end = string + length;
704         float spaceUsedInLine = 0;
705         float spaceUsedForWord;
706         int result = 0;
707         size_t wordLen;
708         size_t dummy;
709
710         dummy = 0;
711         wordWidth(passthroughCW, NULL, &dummy, -1);
712         dummy = 1;
713         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
714
715         for(;;)
716         {
717                 char ch = (cursor < end) ? *cursor : 0;
718                 switch(ch)
719                 {
720                         case 0: // end of string
721                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
722                                 isContinuation = false;
723                                 goto out;
724                                 break;
725                         case '\n': // end of line
726                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
727                                 isContinuation = false;
728                                 ++cursor;
729                                 startOfLine = cursor;
730                                 break;
731                         case ' ': // space
732                                 ++cursor;
733                                 spaceUsedInLine += spaceWidth;
734                                 break;
735                         default: // word
736                                 wordLen = 1;
737                                 while(cursor + wordLen < end)
738                                 {
739                                         switch(cursor[wordLen])
740                                         {
741                                                 case 0:
742                                                 case '\n':
743                                                 case ' ':
744                                                         goto out_inner;
745                                                 default:
746                                                         ++wordLen;
747                                                         break;
748                                         }
749                                 }
750                                 out_inner:
751                                 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
752                                 if(wordLen < 1)
753                                 {
754                                         wordLen = 1;
755                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
756                                 }
757                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
758                                 {
759                                         // we can simply append it
760                                         cursor += wordLen;
761                                         spaceUsedInLine += spaceUsedForWord;
762                                 }
763                                 else
764                                 {
765                                         // output current line
766                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
767                                         isContinuation = true;
768                                         startOfLine = cursor;
769                                         cursor += wordLen;
770                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
771                                 }
772                 }
773         }
774         out:
775
776         return result;
777
778 /*
779         qboolean isContinuation = false;
780         float currentWordSpace = 0;
781         const char *currentWord = 0;
782         float minReserve = 0;
783
784         float spaceUsedInLine = 0;
785         const char *currentLine = 0;
786         const char *currentLineEnd = 0;
787         float currentLineFinalWhitespace = 0;
788         const char *p;
789
790         int result = 0;
791         minReserve = charWidth(passthroughCW, 0);
792         minReserve += charWidth(passthroughCW, ' ');
793
794         if(maxWidth < continuationWidth + minReserve)
795                 maxWidth = continuationWidth + minReserve;
796
797         charWidth(passthroughCW, 0);
798
799         for(p = string; p < string + length; ++p)
800         {
801                 char c = *p;
802                 float w = charWidth(passthroughCW, c);
803
804                 if(!currentWord)
805                 {
806                         currentWord = p;
807                         currentWordSpace = 0;
808                 }
809
810                 if(!currentLine)
811                 {
812                         currentLine = p;
813                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
814                         currentLineEnd = 0;
815                 }
816
817                 if(c == ' ')
818                 {
819                         // 1. I can add the word AND a space - then just append it.
820                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
821                         {
822                                 currentLineEnd = p; // note: space not included here
823                                 currentLineFinalWhitespace = w;
824                                 spaceUsedInLine += currentWordSpace + w;
825                         }
826                         // 2. I can just add the word - then append it, output current line and go to next one.
827                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
828                         {
829                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
830                                 currentLine = 0;
831                                 isContinuation = true;
832                         }
833                         // 3. Otherwise, output current line and go to next one, where I can add the word.
834                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
835                         {
836                                 if(currentLineEnd)
837                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
838                                 currentLine = currentWord;
839                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
840                                 currentLineEnd = p;
841                                 currentLineFinalWhitespace = w;
842                                 isContinuation = true;
843                         }
844                         // 4. We can't even do that? Then output both current and next word as new lines.
845                         else
846                         {
847                                 if(currentLineEnd)
848                                 {
849                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
850                                         isContinuation = true;
851                                 }
852                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
853                                 currentLine = 0;
854                                 isContinuation = true;
855                         }
856                         currentWord = 0;
857                 }
858                 else if(c == '\n')
859                 {
860                         // 1. I can add the word - then do it.
861                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
862                         {
863                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
864                         }
865                         // 2. Otherwise, output current line, next one and make tabula rasa.
866                         else
867                         {
868                                 if(currentLineEnd)
869                                 {
870                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
871                                         isContinuation = true;
872                                 }
873                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
874                         }
875                         currentWord = 0;
876                         currentLine = 0;
877                         isContinuation = false;
878                 }
879                 else
880                 {
881                         currentWordSpace += w;
882                         if(
883                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
884                                 &&
885                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
886                         )
887                         {
888                                 // this word cannot join ANY line...
889                                 // so output the current line...
890                                 if(currentLineEnd)
891                                 {
892                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
893                                         isContinuation = true;
894                                 }
895
896                                 // then this word's beginning...
897                                 if(isContinuation)
898                                 {
899                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
900                                         float pieceWidth = maxWidth - continuationWidth;
901                                         const char *pos = currentWord;
902                                         currentWordSpace = 0;
903
904                                         // reset the char width function to a state where no kerning occurs (start of word)
905                                         charWidth(passthroughCW, ' ');
906                                         while(pos <= p)
907                                         {
908                                                 float w = charWidth(passthroughCW, *pos);
909                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
910                                                 {
911                                                         // print everything until it
912                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
913                                                         // go to here
914                                                         currentWord = pos;
915                                                         currentWordSpace = 0;
916                                                 }
917                                                 currentWordSpace += w;
918                                                 ++pos;
919                                         }
920                                         // now we have a currentWord that fits... set up its next line
921                                         // currentWordSpace has been set
922                                         // currentWord has been set
923                                         spaceUsedInLine = continuationWidth;
924                                         currentLine = currentWord;
925                                         currentLineEnd = 0;
926                                         isContinuation = true;
927                                 }
928                                 else
929                                 {
930                                         // we have a guarantee that it will fix (see if clause)
931                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
932
933                                         // and use the rest of this word as new start of a line
934                                         currentWordSpace = w;
935                                         currentWord = p;
936                                         spaceUsedInLine = continuationWidth;
937                                         currentLine = p;
938                                         currentLineEnd = 0;
939                                         isContinuation = true;
940                                 }
941                         }
942                 }
943         }
944
945         if(!currentWord)
946         {
947                 currentWord = p;
948                 currentWordSpace = 0;
949         }
950
951         if(currentLine) // Same procedure as \n
952         {
953                 // Can I append the current word?
954                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
955                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
956                 else
957                 {
958                         if(currentLineEnd)
959                         {
960                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
961                                 isContinuation = true;
962                         }
963                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
964                 }
965         }
966
967         return result;
968 */
969 }
970
971 /*
972 ==============
973 COM_ParseToken_Simple
974
975 Parse a token out of a string
976 ==============
977 */
978 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
979 {
980         int len;
981         int c;
982         const char *data = *datapointer;
983
984         len = 0;
985         com_token[0] = 0;
986
987         if (!data)
988         {
989                 *datapointer = NULL;
990                 return false;
991         }
992
993 // skip whitespace
994 skipwhite:
995         // line endings:
996         // UNIX: \n
997         // Mac: \r
998         // Windows: \r\n
999         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1000         {
1001                 if (*data == 0)
1002                 {
1003                         // end of file
1004                         *datapointer = NULL;
1005                         return false;
1006                 }
1007         }
1008
1009         // handle Windows line ending
1010         if (data[0] == '\r' && data[1] == '\n')
1011                 data++;
1012
1013         if (data[0] == '/' && data[1] == '/')
1014         {
1015                 // comment
1016                 while (*data && *data != '\n' && *data != '\r')
1017                         data++;
1018                 goto skipwhite;
1019         }
1020         else if (data[0] == '/' && data[1] == '*')
1021         {
1022                 // comment
1023                 data++;
1024                 while (*data && (data[0] != '*' || data[1] != '/'))
1025                         data++;
1026                 if (*data)
1027                         data++;
1028                 if (*data)
1029                         data++;
1030                 goto skipwhite;
1031         }
1032         else if (*data == '\"')
1033         {
1034                 // quoted string
1035                 for (data++;*data && *data != '\"';data++)
1036                 {
1037                         c = *data;
1038                         if (*data == '\\' && parsebackslash)
1039                         {
1040                                 data++;
1041                                 c = *data;
1042                                 if (c == 'n')
1043                                         c = '\n';
1044                                 else if (c == 't')
1045                                         c = '\t';
1046                         }
1047                         if (len < (int)sizeof(com_token) - 1)
1048                                 com_token[len++] = c;
1049                 }
1050                 com_token[len] = 0;
1051                 if (*data == '\"')
1052                         data++;
1053                 *datapointer = data;
1054                 return true;
1055         }
1056         else if (*data == '\r')
1057         {
1058                 // translate Mac line ending to UNIX
1059                 com_token[len++] = '\n';data++;
1060                 com_token[len] = 0;
1061                 *datapointer = data;
1062                 return true;
1063         }
1064         else if (*data == '\n')
1065         {
1066                 // single character
1067                 com_token[len++] = *data++;
1068                 com_token[len] = 0;
1069                 *datapointer = data;
1070                 return true;
1071         }
1072         else
1073         {
1074                 // regular word
1075                 for (;!ISWHITESPACE(*data);data++)
1076                         if (len < (int)sizeof(com_token) - 1)
1077                                 com_token[len++] = *data;
1078                 com_token[len] = 0;
1079                 *datapointer = data;
1080                 return true;
1081         }
1082 }
1083
1084 /*
1085 ==============
1086 COM_ParseToken_QuakeC
1087
1088 Parse a token out of a string
1089 ==============
1090 */
1091 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1092 {
1093         int len;
1094         int c;
1095         const char *data = *datapointer;
1096
1097         len = 0;
1098         com_token[0] = 0;
1099
1100         if (!data)
1101         {
1102                 *datapointer = NULL;
1103                 return false;
1104         }
1105
1106 // skip whitespace
1107 skipwhite:
1108         // line endings:
1109         // UNIX: \n
1110         // Mac: \r
1111         // Windows: \r\n
1112         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1113         {
1114                 if (*data == 0)
1115                 {
1116                         // end of file
1117                         *datapointer = NULL;
1118                         return false;
1119                 }
1120         }
1121
1122         // handle Windows line ending
1123         if (data[0] == '\r' && data[1] == '\n')
1124                 data++;
1125
1126         if (data[0] == '/' && data[1] == '/')
1127         {
1128                 // comment
1129                 while (*data && *data != '\n' && *data != '\r')
1130                         data++;
1131                 goto skipwhite;
1132         }
1133         else if (data[0] == '/' && data[1] == '*')
1134         {
1135                 // comment
1136                 data++;
1137                 while (*data && (data[0] != '*' || data[1] != '/'))
1138                         data++;
1139                 if (*data)
1140                         data++;
1141                 if (*data)
1142                         data++;
1143                 goto skipwhite;
1144         }
1145         else if (*data == '\"' || *data == '\'')
1146         {
1147                 // quoted string
1148                 char quote = *data;
1149                 for (data++;*data && *data != quote;data++)
1150                 {
1151                         c = *data;
1152                         if (*data == '\\')
1153                         {
1154                                 data++;
1155                                 c = *data;
1156                                 if (c == 'n')
1157                                         c = '\n';
1158                                 else if (c == 't')
1159                                         c = '\t';
1160                         }
1161                         if (len < (int)sizeof(com_token) - 1)
1162                                 com_token[len++] = c;
1163                 }
1164                 com_token[len] = 0;
1165                 if (*data == quote)
1166                         data++;
1167                 *datapointer = data;
1168                 return true;
1169         }
1170         else if (*data == '\r')
1171         {
1172                 // translate Mac line ending to UNIX
1173                 com_token[len++] = '\n';data++;
1174                 com_token[len] = 0;
1175                 *datapointer = data;
1176                 return true;
1177         }
1178         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1179         {
1180                 // single character
1181                 com_token[len++] = *data++;
1182                 com_token[len] = 0;
1183                 *datapointer = data;
1184                 return true;
1185         }
1186         else
1187         {
1188                 // regular word
1189                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1190                         if (len < (int)sizeof(com_token) - 1)
1191                                 com_token[len++] = *data;
1192                 com_token[len] = 0;
1193                 *datapointer = data;
1194                 return true;
1195         }
1196 }
1197
1198 /*
1199 ==============
1200 COM_ParseToken_VM_Tokenize
1201
1202 Parse a token out of a string
1203 ==============
1204 */
1205 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1206 {
1207         int len;
1208         int c;
1209         const char *data = *datapointer;
1210
1211         len = 0;
1212         com_token[0] = 0;
1213
1214         if (!data)
1215         {
1216                 *datapointer = NULL;
1217                 return false;
1218         }
1219
1220 // skip whitespace
1221 skipwhite:
1222         // line endings:
1223         // UNIX: \n
1224         // Mac: \r
1225         // Windows: \r\n
1226         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1227         {
1228                 if (*data == 0)
1229                 {
1230                         // end of file
1231                         *datapointer = NULL;
1232                         return false;
1233                 }
1234         }
1235
1236         // handle Windows line ending
1237         if (data[0] == '\r' && data[1] == '\n')
1238                 data++;
1239
1240         if (data[0] == '/' && data[1] == '/')
1241         {
1242                 // comment
1243                 while (*data && *data != '\n' && *data != '\r')
1244                         data++;
1245                 goto skipwhite;
1246         }
1247         else if (data[0] == '/' && data[1] == '*')
1248         {
1249                 // comment
1250                 data++;
1251                 while (*data && (data[0] != '*' || data[1] != '/'))
1252                         data++;
1253                 if (*data)
1254                         data++;
1255                 if (*data)
1256                         data++;
1257                 goto skipwhite;
1258         }
1259         else if (*data == '\"' || *data == '\'')
1260         {
1261                 char quote = *data;
1262                 // quoted string
1263                 for (data++;*data && *data != quote;data++)
1264                 {
1265                         c = *data;
1266                         if (*data == '\\')
1267                         {
1268                                 data++;
1269                                 c = *data;
1270                                 if (c == 'n')
1271                                         c = '\n';
1272                                 else if (c == 't')
1273                                         c = '\t';
1274                         }
1275                         if (len < (int)sizeof(com_token) - 1)
1276                                 com_token[len++] = c;
1277                 }
1278                 com_token[len] = 0;
1279                 if (*data == quote)
1280                         data++;
1281                 *datapointer = data;
1282                 return true;
1283         }
1284         else if (*data == '\r')
1285         {
1286                 // translate Mac line ending to UNIX
1287                 com_token[len++] = '\n';data++;
1288                 com_token[len] = 0;
1289                 *datapointer = data;
1290                 return true;
1291         }
1292         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1293         {
1294                 // single character
1295                 com_token[len++] = *data++;
1296                 com_token[len] = 0;
1297                 *datapointer = data;
1298                 return true;
1299         }
1300         else
1301         {
1302                 // regular word
1303                 for (;!ISWHITESPACE(*data) && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1304                         if (len < (int)sizeof(com_token) - 1)
1305                                 com_token[len++] = *data;
1306                 com_token[len] = 0;
1307                 *datapointer = data;
1308                 return true;
1309         }
1310 }
1311
1312 /*
1313 ==============
1314 COM_ParseToken_Console
1315
1316 Parse a token out of a string, behaving like the qwcl console
1317 ==============
1318 */
1319 int COM_ParseToken_Console(const char **datapointer)
1320 {
1321         int len;
1322         const char *data = *datapointer;
1323
1324         len = 0;
1325         com_token[0] = 0;
1326
1327         if (!data)
1328         {
1329                 *datapointer = NULL;
1330                 return false;
1331         }
1332
1333 // skip whitespace
1334 skipwhite:
1335         for (;ISWHITESPACE(*data);data++)
1336         {
1337                 if (*data == 0)
1338                 {
1339                         // end of file
1340                         *datapointer = NULL;
1341                         return false;
1342                 }
1343         }
1344
1345         if (*data == '/' && data[1] == '/')
1346         {
1347                 // comment
1348                 while (*data && *data != '\n' && *data != '\r')
1349                         data++;
1350                 goto skipwhite;
1351         }
1352         else if (*data == '\"')
1353         {
1354                 // quoted string
1355                 for (data++;*data && *data != '\"';data++)
1356                 {
1357                         // allow escaped " and \ case
1358                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1359                                 data++;
1360                         if (len < (int)sizeof(com_token) - 1)
1361                                 com_token[len++] = *data;
1362                 }
1363                 com_token[len] = 0;
1364                 if (*data == '\"')
1365                         data++;
1366                 *datapointer = data;
1367         }
1368         else
1369         {
1370                 // regular word
1371                 for (;!ISWHITESPACE(*data);data++)
1372                         if (len < (int)sizeof(com_token) - 1)
1373                                 com_token[len++] = *data;
1374                 com_token[len] = 0;
1375                 *datapointer = data;
1376         }
1377
1378         return true;
1379 }
1380
1381
1382 /*
1383 ================
1384 COM_CheckParm
1385
1386 Returns the position (1 to argc-1) in the program's argument list
1387 where the given parameter apears, or 0 if not present
1388 ================
1389 */
1390 int COM_CheckParm (const char *parm)
1391 {
1392         int i;
1393
1394         for (i=1 ; i<com_argc ; i++)
1395         {
1396                 if (!com_argv[i])
1397                         continue;               // NEXTSTEP sometimes clears appkit vars.
1398                 if (!strcmp (parm,com_argv[i]))
1399                         return i;
1400         }
1401
1402         return 0;
1403 }
1404
1405 //===========================================================================
1406
1407 // Game mods
1408
1409 typedef struct gamemode_info_s
1410 {
1411         const char* prog_name;
1412         const char* cmdline;
1413         const char* gamename;
1414         const char* gamedirname1;
1415         const char* gamedirname2;
1416         const char* gamescreenshotname;
1417         const char* gameuserdirname;
1418 } gamemode_info_t;
1419
1420 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1421 {// prog_name           cmdline                 gamename                                basegame        modgame                 screenshotprefix        userdir
1422
1423 // GAME_NORMAL
1424 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1425 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
1426 // GAME_HIPNOTIC
1427 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1428 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
1429 // GAME_ROGUE
1430 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1431 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
1432 // GAME_NEHAHRA
1433 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1434 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
1435 // GAME_NEXUIZ
1436 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1437 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
1438 // GAME_TRANSFUSION
1439 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1440 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
1441 // GAME_GOODVSBAD2
1442 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1443 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
1444 // GAME_TEU
1445 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1446 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
1447 // GAME_BATTLEMECH
1448 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1449 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
1450 // GAME_ZYMOTIC
1451 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1452 { "zymotic",            "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
1453 // GAME_SETHERAL
1454 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1455 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
1456 // GAME_SOM
1457 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1458 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
1459 // GAME_TENEBRAE
1460 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1461 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
1462 // GAME_NEOTERIC
1463 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1464 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
1465 // GAME_OPENQUARTZ
1466 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1467 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
1468 // GAME_PRYDON
1469 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1470 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
1471 // GAME_DELUXEQUAKE
1472 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1473 { "dq", "-dq",  "Deluxe Quake",         "basedq",               "extradq",              "basedq",               "dq" },
1474 // GAME_THEHUNTED
1475 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1476 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
1477 // GAME_DEFEATINDETAIL2
1478 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1479 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
1480 // GAME_DARSANA
1481 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1482 { "darsana",            "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
1483 // GAME_CONTAGIONTHEORY
1484 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1485 { "contagiontheory",            "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
1486 // GAME_EDU2P
1487 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1488 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1489 // GAME_BLADEMASTER
1490 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1491 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1492 // GAME_PROPHECY
1493 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1494 { "prophecy",                           "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
1495 };
1496
1497 void COM_InitGameType (void)
1498 {
1499         char name [MAX_OSPATH];
1500         unsigned int i;
1501
1502         FS_StripExtension (com_argv[0], name, sizeof (name));
1503         COM_ToLowerString (name, name, sizeof (name));
1504
1505         // Check the binary name; default to GAME_NORMAL (0)
1506         gamemode = GAME_NORMAL;
1507         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1508                 if (strstr (name, gamemode_info[i].prog_name))
1509                 {
1510                         gamemode = (gamemode_t)i;
1511                         break;
1512                 }
1513
1514         // Look for a command-line option
1515         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1516                 if (COM_CheckParm (gamemode_info[i].cmdline))
1517                 {
1518                         gamemode = (gamemode_t)i;
1519                         break;
1520                 }
1521
1522         gamename = gamemode_info[gamemode].gamename;
1523         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1524         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1525         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1526         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1527 }
1528
1529
1530 /*
1531 ================
1532 COM_Init
1533 ================
1534 */
1535 void COM_Init_Commands (void)
1536 {
1537         int i, j, n;
1538         char com_cmdline[MAX_INPUTLINE];
1539
1540         Cvar_RegisterVariable (&registered);
1541         Cvar_RegisterVariable (&cmdline);
1542
1543         // reconstitute the command line for the cmdline externally visible cvar
1544         n = 0;
1545         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1546         {
1547                 i = 0;
1548                 if (strstr(com_argv[j], " "))
1549                 {
1550                         // arg contains whitespace, store quotes around it
1551                         com_cmdline[n++] = '\"';
1552                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1553                                 com_cmdline[n++] = com_argv[j][i++];
1554                         com_cmdline[n++] = '\"';
1555                 }
1556                 else
1557                 {
1558                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1559                                 com_cmdline[n++] = com_argv[j][i++];
1560                 }
1561                 if (n < ((int)sizeof(com_cmdline) - 1))
1562                         com_cmdline[n++] = ' ';
1563                 else
1564                         break;
1565         }
1566         com_cmdline[n] = 0;
1567         Cvar_Set ("cmdline", com_cmdline);
1568 }
1569
1570 /*
1571 ============
1572 va
1573
1574 does a varargs printf into a temp buffer, so I don't need to have
1575 varargs versions of all text functions.
1576 FIXME: make this buffer size safe someday
1577 ============
1578 */
1579 char *va(const char *format, ...)
1580 {
1581         va_list argptr;
1582         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1583         static char string[8][1024], *s;
1584         static int stringindex = 0;
1585
1586         s = string[stringindex];
1587         stringindex = (stringindex + 1) & 7;
1588         va_start (argptr, format);
1589         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1590         va_end (argptr);
1591
1592         return s;
1593 }
1594
1595
1596 //======================================
1597
1598 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1599
1600 #undef snprintf
1601 #undef vsnprintf
1602
1603 #ifdef WIN32
1604 # define snprintf _snprintf
1605 # define vsnprintf _vsnprintf
1606 #endif
1607
1608
1609 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1610 {
1611         va_list args;
1612         int result;
1613
1614         va_start (args, format);
1615         result = dpvsnprintf (buffer, buffersize, format, args);
1616         va_end (args);
1617
1618         return result;
1619 }
1620
1621
1622 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1623 {
1624         int result;
1625
1626 #if _MSC_VER >= 1400
1627         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1628 #else
1629         result = vsnprintf (buffer, buffersize, format, args);
1630 #endif
1631         if (result < 0 || (size_t)result >= buffersize)
1632         {
1633                 buffer[buffersize - 1] = '\0';
1634                 return -1;
1635         }
1636
1637         return result;
1638 }
1639
1640
1641 //======================================
1642
1643 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1644 {
1645         if (size_out == 0)
1646                 return;
1647
1648         while (*in && size_out > 1)
1649         {
1650                 if (*in >= 'A' && *in <= 'Z')
1651                         *out++ = *in++ + 'a' - 'A';
1652                 else
1653                         *out++ = *in++;
1654                 size_out--;
1655         }
1656         *out = '\0';
1657 }
1658
1659 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1660 {
1661         if (size_out == 0)
1662                 return;
1663
1664         while (*in && size_out > 1)
1665         {
1666                 if (*in >= 'a' && *in <= 'z')
1667                         *out++ = *in++ + 'A' - 'a';
1668                 else
1669                         *out++ = *in++;
1670                 size_out--;
1671         }
1672         *out = '\0';
1673 }
1674
1675 int COM_StringBeginsWith(const char *s, const char *match)
1676 {
1677         for (;*s && *match;s++, match++)
1678                 if (*s != *match)
1679                         return false;
1680         return true;
1681 }
1682
1683 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1684 {
1685         int argc, commentprefixlength;
1686         char *tokenbufend;
1687         const char *l;
1688         argc = 0;
1689         tokenbufend = tokenbuf + tokenbufsize;
1690         l = *text;
1691         commentprefixlength = 0;
1692         if (commentprefix)
1693                 commentprefixlength = (int)strlen(commentprefix);
1694         while (*l && *l != '\n' && *l != '\r')
1695         {
1696                 if (!ISWHITESPACE(*l))
1697                 {
1698                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1699                         {
1700                                 while (*l && *l != '\n' && *l != '\r')
1701                                         l++;
1702                                 break;
1703                         }
1704                         if (argc >= maxargc)
1705                                 return -1;
1706                         argv[argc++] = tokenbuf;
1707                         if (*l == '"')
1708                         {
1709                                 l++;
1710                                 while (*l && *l != '"')
1711                                 {
1712                                         if (tokenbuf >= tokenbufend)
1713                                                 return -1;
1714                                         *tokenbuf++ = *l++;
1715                                 }
1716                                 if (*l == '"')
1717                                         l++;
1718                         }
1719                         else
1720                         {
1721                                 while (!ISWHITESPACE(*l))
1722                                 {
1723                                         if (tokenbuf >= tokenbufend)
1724                                                 return -1;
1725                                         *tokenbuf++ = *l++;
1726                                 }
1727                         }
1728                         if (tokenbuf >= tokenbufend)
1729                                 return -1;
1730                         *tokenbuf++ = 0;
1731                 }
1732                 else
1733                         l++;
1734         }
1735         // line endings:
1736         // UNIX: \n
1737         // Mac: \r
1738         // Windows: \r\n
1739         if (*l == '\r')
1740                 l++;
1741         if (*l == '\n')
1742                 l++;
1743         *text = l;
1744         return argc;
1745 }
1746
1747 /*
1748 ============
1749 COM_StringLengthNoColors
1750
1751 calculates the visible width of a color coded string.
1752
1753 *valid is filled with TRUE if the string is a valid colored string (that is, if
1754 it does not end with an unfinished color code). If it gets filled with FALSE, a
1755 fix would be adding a STRING_COLOR_TAG at the end of the string.
1756
1757 valid can be set to NULL if the caller doesn't care.
1758
1759 For size_s, specify the maximum number of characters from s to use, or 0 to use
1760 all characters until the zero terminator.
1761 ============
1762 */
1763 size_t
1764 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1765 {
1766         const char *end = size_s ? (s + size_s) : NULL;
1767         size_t len = 0;
1768         for(;;)
1769         {
1770                 switch((s == end) ? 0 : *s)
1771                 {
1772                         case 0:
1773                                 if(valid)
1774                                         *valid = TRUE;
1775                                 return len;
1776                         case STRING_COLOR_TAG:
1777                                 ++s;
1778                                 switch((s == end) ? 0 : *s)
1779                                 {
1780                                         case STRING_COLOR_RGB_DEFAULT:
1781                                                 if (s+1 != end && isxdigit(s[1]) &&
1782                                                         s+2 != end && isxdigit(s[2]) &&
1783                                                         s+3 != end && isxdigit(s[3]) )
1784                                                 {
1785                                                         s+=3;
1786                                                         break;
1787                                                 }
1788                                                 ++len; // STRING_COLOR_TAG
1789                                                 ++len; // STRING_COLOR_RGB_DEFAULT
1790                                                 break;
1791                                         /*case 'a':
1792                                                 if ( s+1 != end && ( isxdigit(s[1]) || (s[1] == '+' || s[1] == '-') ) )
1793                                                 {
1794                                                         s++;
1795                                                         break;
1796                                                 }
1797                                                 ++len; // STRING_COLOR_TAG
1798                                                 ++len; // STRING_COLOR_RGB_DEFAULT
1799                                                 break;*/
1800                                         case 0: // ends with unfinished color code!
1801                                                 ++len;
1802                                                 if(valid)
1803                                                         *valid = FALSE;
1804                                                 return len;
1805                                         case STRING_COLOR_TAG: // escaped ^
1806                                                 ++len;
1807                                                 break;
1808                                         case '0': case '1': case '2': case '3': case '4':
1809                                         case '5': case '6': case '7': case '8': case '9': // color code
1810                                                 break;
1811                                         default: // not a color code
1812                                                 ++len; // STRING_COLOR_TAG
1813                                                 ++len; // the character
1814                                                 break;
1815                                 }
1816                                 break;
1817                         default:
1818                                 ++len;
1819                                 break;
1820                 }
1821                 ++s;
1822         }
1823         // never get here
1824 }
1825
1826 /*
1827 ============
1828 COM_StringDecolorize
1829
1830 removes color codes from a string.
1831
1832 If escape_carets is true, the resulting string will be safe for printing. If
1833 escape_carets is false, the function will just strip color codes (for logging
1834 for example).
1835
1836 If the output buffer size did not suffice for converting, the function returns
1837 FALSE. Generally, if escape_carets is false, the output buffer needs
1838 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1839 bytes. In any case, the function makes sure that the resulting string is
1840 zero terminated.
1841
1842 For size_in, specify the maximum number of characters from in to use, or 0 to use
1843 all characters until the zero terminator.
1844 ============
1845 */
1846 qboolean
1847 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1848 {
1849 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1850         const char *end = size_in ? (in + size_in) : NULL;
1851         if(size_out < 1)
1852                 return FALSE;
1853         for(;;)
1854         {
1855                 switch((in == end) ? 0 : *in)
1856                 {
1857                         case 0:
1858                                 *out++ = 0;
1859                                 return TRUE;
1860                         case STRING_COLOR_TAG:
1861                                 ++in;
1862                                 switch((in == end) ? 0 : *in)
1863                                 {
1864                                         case STRING_COLOR_RGB_DEFAULT:
1865                                                 if (in+1 != end && isxdigit(in[1]) &&
1866                                                         in+2 != end && isxdigit(in[2]) &&
1867                                                         in+3 != end && isxdigit(in[3]) )
1868                                                 {
1869                                                         in+=3;
1870                                                         break;
1871                                                 }
1872                                                 APPEND(STRING_COLOR_TAG);
1873                                                 if(escape_carets)
1874                                                         APPEND(STRING_COLOR_TAG);
1875                                                 APPEND(STRING_COLOR_RGB_DEFAULT);
1876                                                 break;
1877                                         /*case 'a':
1878                                                 if ( in+1 != end && ( isxdigit(in[1]) || (in[1] == '+' || in[1] == '-') ) )
1879                                                 {
1880                                                         in++;
1881                                                         break;
1882                                                 }*/
1883                                         case 0: // ends with unfinished color code!
1884                                                 APPEND(STRING_COLOR_TAG);
1885                                                 // finish the code by appending another caret when escaping
1886                                                 if(escape_carets)
1887                                                         APPEND(STRING_COLOR_TAG);
1888                                                 *out++ = 0;
1889                                                 return TRUE;
1890                                         case STRING_COLOR_TAG: // escaped ^
1891                                                 APPEND(STRING_COLOR_TAG);
1892                                                 // append a ^ twice when escaping
1893                                                 if(escape_carets)
1894                                                         APPEND(STRING_COLOR_TAG);
1895                                                 break;
1896                                         case '0': case '1': case '2': case '3': case '4':
1897                                         case '5': case '6': case '7': case '8': case '9': // color code
1898                                                 break;
1899                                         default: // not a color code
1900                                                 APPEND(STRING_COLOR_TAG);
1901                                                 APPEND(*in);
1902                                                 break;
1903                                 }
1904                                 break;
1905                         default:
1906                                 APPEND(*in);
1907                                 break;
1908                 }
1909                 ++in;
1910         }
1911         // never get here
1912 #undef APPEND
1913 }
1914
1915 // written by Elric, thanks Elric!
1916 char *SearchInfostring(const char *infostring, const char *key)
1917 {
1918         static char value [MAX_INPUTLINE];
1919         char crt_key [MAX_INPUTLINE];
1920         size_t value_ind, key_ind;
1921         char c;
1922
1923         if (*infostring++ != '\\')
1924                 return NULL;
1925
1926         value_ind = 0;
1927         for (;;)
1928         {
1929                 key_ind = 0;
1930
1931                 // Get the key name
1932                 for (;;)
1933                 {
1934                         c = *infostring++;
1935
1936                         if (c == '\0')
1937                                 return NULL;
1938                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1939                         {
1940                                 crt_key[key_ind] = '\0';
1941                                 break;
1942                         }
1943
1944                         crt_key[key_ind++] = c;
1945                 }
1946
1947                 // If it's the key we are looking for, save it in "value"
1948                 if (!strcmp(crt_key, key))
1949                 {
1950                         for (;;)
1951                         {
1952                                 c = *infostring++;
1953
1954                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1955                                 {
1956                                         value[value_ind] = '\0';
1957                                         return value;
1958                                 }
1959
1960                                 value[value_ind++] = c;
1961                         }
1962                 }
1963
1964                 // Else, skip the value
1965                 for (;;)
1966                 {
1967                         c = *infostring++;
1968
1969                         if (c == '\0')
1970                                 return NULL;
1971                         if (c == '\\')
1972                                 break;
1973                 }
1974         }
1975 }
1976
1977 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1978 {
1979         int pos = 0, j;
1980         size_t keylength;
1981         if (!key)
1982                 key = "";
1983         if (!value)
1984                 value = "";
1985         keylength = strlen(key);
1986         if (valuelength < 1 || !value)
1987         {
1988                 Con_Printf("InfoString_GetValue: no room in value\n");
1989                 return;
1990         }
1991         value[0] = 0;
1992         if (strchr(key, '\\'))
1993         {
1994                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1995                 return;
1996         }
1997         if (strchr(key, '\"'))
1998         {
1999                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2000                 return;
2001         }
2002         if (!key[0])
2003         {
2004                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2005                 return;
2006         }
2007         while (buffer[pos] == '\\')
2008         {
2009                 if (!memcmp(buffer + pos+1, key, keylength))
2010                 {
2011                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2012                         pos++;
2013                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2014                                 value[j] = buffer[pos+j];
2015                         value[j] = 0;
2016                         return;
2017                 }
2018                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2019                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2020         }
2021         // if we reach this point the key was not found
2022 }
2023
2024 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2025 {
2026         int pos = 0, pos2;
2027         size_t keylength;
2028         if (!key)
2029                 key = "";
2030         if (!value)
2031                 value = "";
2032         keylength = strlen(key);
2033         if (strchr(key, '\\') || strchr(value, '\\'))
2034         {
2035                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2036                 return;
2037         }
2038         if (strchr(key, '\"') || strchr(value, '\"'))
2039         {
2040                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2041                 return;
2042         }
2043         if (!key[0])
2044         {
2045                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2046                 return;
2047         }
2048         while (buffer[pos] == '\\')
2049         {
2050                 if (!memcmp(buffer + pos+1, key, keylength))
2051                         break;
2052                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2053                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2054         }
2055         // if we found the key, find the end of it because we will be replacing it
2056         pos2 = pos;
2057         if (buffer[pos] == '\\')
2058         {
2059                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2060                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2061         }
2062         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2063         {
2064                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2065                 return;
2066         }
2067         if (value && value[0])
2068         {
2069                 // set the key/value and append the remaining text
2070                 char tempbuffer[4096];
2071                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2072                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2073         }
2074         else
2075         {
2076                 // just remove the key from the text
2077                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2078         }
2079 }
2080
2081 void InfoString_Print(char *buffer)
2082 {
2083         int i;
2084         char key[2048];
2085         char value[2048];
2086         while (*buffer)
2087         {
2088                 if (*buffer != '\\')
2089                 {
2090                         Con_Printf("InfoString_Print: corrupt string\n");
2091                         return;
2092                 }
2093                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2094                         if (i < (int)sizeof(key)-1)
2095                                 key[i++] = *buffer;
2096                 key[i] = 0;
2097                 if (*buffer != '\\')
2098                 {
2099                         Con_Printf("InfoString_Print: corrupt string\n");
2100                         return;
2101                 }
2102                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2103                         if (i < (int)sizeof(value)-1)
2104                                 value[i++] = *buffer;
2105                 value[i] = 0;
2106                 // empty value is an error case
2107                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2108         }
2109 }
2110
2111 //========================================================
2112 // strlcat and strlcpy, from OpenBSD
2113
2114 /*
2115  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2116  *
2117  * Permission to use, copy, modify, and distribute this software for any
2118  * purpose with or without fee is hereby granted, provided that the above
2119  * copyright notice and this permission notice appear in all copies.
2120  *
2121  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2122  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2123  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2124  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2125  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2126  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2127  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2128  */
2129
2130 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2131 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2132
2133
2134 #ifndef HAVE_STRLCAT
2135 size_t
2136 strlcat(char *dst, const char *src, size_t siz)
2137 {
2138         register char *d = dst;
2139         register const char *s = src;
2140         register size_t n = siz;
2141         size_t dlen;
2142
2143         /* Find the end of dst and adjust bytes left but don't go past end */
2144         while (n-- != 0 && *d != '\0')
2145                 d++;
2146         dlen = d - dst;
2147         n = siz - dlen;
2148
2149         if (n == 0)
2150                 return(dlen + strlen(s));
2151         while (*s != '\0') {
2152                 if (n != 1) {
2153                         *d++ = *s;
2154                         n--;
2155                 }
2156                 s++;
2157         }
2158         *d = '\0';
2159
2160         return(dlen + (s - src));       /* count does not include NUL */
2161 }
2162 #endif  // #ifndef HAVE_STRLCAT
2163
2164
2165 #ifndef HAVE_STRLCPY
2166 size_t
2167 strlcpy(char *dst, const char *src, size_t siz)
2168 {
2169         register char *d = dst;
2170         register const char *s = src;
2171         register size_t n = siz;
2172
2173         /* Copy as many bytes as will fit */
2174         if (n != 0 && --n != 0) {
2175                 do {
2176                         if ((*d++ = *s++) == 0)
2177                                 break;
2178                 } while (--n != 0);
2179         }
2180
2181         /* Not enough room in dst, add NUL and traverse rest of src */
2182         if (n == 0) {
2183                 if (siz != 0)
2184                         *d = '\0';              /* NUL-terminate dst */
2185                 while (*s++)
2186                         ;
2187         }
2188
2189         return(s - src - 1);    /* count does not include NUL */
2190 }
2191
2192 #endif  // #ifndef HAVE_STRLCPY