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