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