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