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