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