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