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