Adding FreeType2 and UTF-8 Support.
[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                                 wordChars = strnlen(cursor, wordLen);
744                                 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
745                                 if(wordChars < 1)
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, u8_strnlen(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 }