]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
f75d24eafa5d952589000cef62fb945c44a28e36
[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(gamenetworkfiltername));
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                         com_cmdline[n++] = '\"';
1623                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1624                                 com_cmdline[n++] = com_argv[j][i++];
1625                         com_cmdline[n++] = '\"';
1626                 }
1627                 else
1628                 {
1629                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1630                                 com_cmdline[n++] = com_argv[j][i++];
1631                 }
1632                 if (n < ((int)sizeof(com_cmdline) - 1))
1633                         com_cmdline[n++] = ' ';
1634                 else
1635                         break;
1636         }
1637         com_cmdline[n] = 0;
1638         Cvar_Set ("cmdline", com_cmdline);
1639 }
1640
1641 /*
1642 ============
1643 va
1644
1645 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1646 ============
1647 */
1648 char *va(char *buf, size_t buflen, const char *format, ...)
1649 {
1650         va_list argptr;
1651
1652         va_start (argptr, format);
1653         dpvsnprintf (buf, buflen, format,argptr);
1654         va_end (argptr);
1655
1656         return buf;
1657 }
1658
1659
1660 //======================================
1661
1662 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1663
1664 #undef snprintf
1665 #undef vsnprintf
1666
1667 #ifdef WIN32
1668 # define snprintf _snprintf
1669 # define vsnprintf _vsnprintf
1670 #endif
1671
1672
1673 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1674 {
1675         va_list args;
1676         int result;
1677
1678         va_start (args, format);
1679         result = dpvsnprintf (buffer, buffersize, format, args);
1680         va_end (args);
1681
1682         return result;
1683 }
1684
1685
1686 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1687 {
1688         int result;
1689
1690 #if _MSC_VER >= 1400
1691         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1692 #else
1693         result = vsnprintf (buffer, buffersize, format, args);
1694 #endif
1695         if (result < 0 || (size_t)result >= buffersize)
1696         {
1697                 buffer[buffersize - 1] = '\0';
1698                 return -1;
1699         }
1700
1701         return result;
1702 }
1703
1704
1705 //======================================
1706
1707 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1708 {
1709         if (size_out == 0)
1710                 return;
1711
1712         if(utf8_enable.integer)
1713         {
1714                 *out = 0;
1715                 while(*in && size_out > 1)
1716                 {
1717                         int n;
1718                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1719                         ch = u8_tolower(ch);
1720                         n = u8_fromchar(ch, out, size_out);
1721                         if(n <= 0)
1722                                 break;
1723                         out += n;
1724                         size_out -= n;
1725                 }
1726                 return;
1727         }
1728
1729         while (*in && size_out > 1)
1730         {
1731                 if (*in >= 'A' && *in <= 'Z')
1732                         *out++ = *in++ + 'a' - 'A';
1733                 else
1734                         *out++ = *in++;
1735                 size_out--;
1736         }
1737         *out = '\0';
1738 }
1739
1740 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1741 {
1742         if (size_out == 0)
1743                 return;
1744
1745         if(utf8_enable.integer)
1746         {
1747                 *out = 0;
1748                 while(*in && size_out > 1)
1749                 {
1750                         int n;
1751                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1752                         ch = u8_toupper(ch);
1753                         n = u8_fromchar(ch, out, size_out);
1754                         if(n <= 0)
1755                                 break;
1756                         out += n;
1757                         size_out -= n;
1758                 }
1759                 return;
1760         }
1761
1762         while (*in && size_out > 1)
1763         {
1764                 if (*in >= 'a' && *in <= 'z')
1765                         *out++ = *in++ + 'A' - 'a';
1766                 else
1767                         *out++ = *in++;
1768                 size_out--;
1769         }
1770         *out = '\0';
1771 }
1772
1773 int COM_StringBeginsWith(const char *s, const char *match)
1774 {
1775         for (;*s && *match;s++, match++)
1776                 if (*s != *match)
1777                         return false;
1778         return true;
1779 }
1780
1781 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1782 {
1783         int argc, commentprefixlength;
1784         char *tokenbufend;
1785         const char *l;
1786         argc = 0;
1787         tokenbufend = tokenbuf + tokenbufsize;
1788         l = *text;
1789         commentprefixlength = 0;
1790         if (commentprefix)
1791                 commentprefixlength = (int)strlen(commentprefix);
1792         while (*l && *l != '\n' && *l != '\r')
1793         {
1794                 if (!ISWHITESPACE(*l))
1795                 {
1796                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1797                         {
1798                                 while (*l && *l != '\n' && *l != '\r')
1799                                         l++;
1800                                 break;
1801                         }
1802                         if (argc >= maxargc)
1803                                 return -1;
1804                         argv[argc++] = tokenbuf;
1805                         if (*l == '"')
1806                         {
1807                                 l++;
1808                                 while (*l && *l != '"')
1809                                 {
1810                                         if (tokenbuf >= tokenbufend)
1811                                                 return -1;
1812                                         *tokenbuf++ = *l++;
1813                                 }
1814                                 if (*l == '"')
1815                                         l++;
1816                         }
1817                         else
1818                         {
1819                                 while (!ISWHITESPACE(*l))
1820                                 {
1821                                         if (tokenbuf >= tokenbufend)
1822                                                 return -1;
1823                                         *tokenbuf++ = *l++;
1824                                 }
1825                         }
1826                         if (tokenbuf >= tokenbufend)
1827                                 return -1;
1828                         *tokenbuf++ = 0;
1829                 }
1830                 else
1831                         l++;
1832         }
1833         // line endings:
1834         // UNIX: \n
1835         // Mac: \r
1836         // Windows: \r\n
1837         if (*l == '\r')
1838                 l++;
1839         if (*l == '\n')
1840                 l++;
1841         *text = l;
1842         return argc;
1843 }
1844
1845 /*
1846 ============
1847 COM_StringLengthNoColors
1848
1849 calculates the visible width of a color coded string.
1850
1851 *valid is filled with TRUE if the string is a valid colored string (that is, if
1852 it does not end with an unfinished color code). If it gets filled with FALSE, a
1853 fix would be adding a STRING_COLOR_TAG at the end of the string.
1854
1855 valid can be set to NULL if the caller doesn't care.
1856
1857 For size_s, specify the maximum number of characters from s to use, or 0 to use
1858 all characters until the zero terminator.
1859 ============
1860 */
1861 size_t
1862 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1863 {
1864         const char *end = size_s ? (s + size_s) : NULL;
1865         size_t len = 0;
1866         for(;;)
1867         {
1868                 switch((s == end) ? 0 : *s)
1869                 {
1870                         case 0:
1871                                 if(valid)
1872                                         *valid = TRUE;
1873                                 return len;
1874                         case STRING_COLOR_TAG:
1875                                 ++s;
1876                                 switch((s == end) ? 0 : *s)
1877                                 {
1878                                         case STRING_COLOR_RGB_TAG_CHAR:
1879                                                 if (s+1 != end && isxdigit(s[1]) &&
1880                                                         s+2 != end && isxdigit(s[2]) &&
1881                                                         s+3 != end && isxdigit(s[3]) )
1882                                                 {
1883                                                         s+=3;
1884                                                         break;
1885                                                 }
1886                                                 ++len; // STRING_COLOR_TAG
1887                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1888                                                 break;
1889                                         case 0: // ends with unfinished color code!
1890                                                 ++len;
1891                                                 if(valid)
1892                                                         *valid = FALSE;
1893                                                 return len;
1894                                         case STRING_COLOR_TAG: // escaped ^
1895                                                 ++len;
1896                                                 break;
1897                                         case '0': case '1': case '2': case '3': case '4':
1898                                         case '5': case '6': case '7': case '8': case '9': // color code
1899                                                 break;
1900                                         default: // not a color code
1901                                                 ++len; // STRING_COLOR_TAG
1902                                                 ++len; // the character
1903                                                 break;
1904                                 }
1905                                 break;
1906                         default:
1907                                 ++len;
1908                                 break;
1909                 }
1910                 ++s;
1911         }
1912         // never get here
1913 }
1914
1915 /*
1916 ============
1917 COM_StringDecolorize
1918
1919 removes color codes from a string.
1920
1921 If escape_carets is true, the resulting string will be safe for printing. If
1922 escape_carets is false, the function will just strip color codes (for logging
1923 for example).
1924
1925 If the output buffer size did not suffice for converting, the function returns
1926 FALSE. Generally, if escape_carets is false, the output buffer needs
1927 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1928 bytes. In any case, the function makes sure that the resulting string is
1929 zero terminated.
1930
1931 For size_in, specify the maximum number of characters from in to use, or 0 to use
1932 all characters until the zero terminator.
1933 ============
1934 */
1935 qboolean
1936 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1937 {
1938 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1939         const char *end = size_in ? (in + size_in) : NULL;
1940         if(size_out < 1)
1941                 return FALSE;
1942         for(;;)
1943         {
1944                 switch((in == end) ? 0 : *in)
1945                 {
1946                         case 0:
1947                                 *out++ = 0;
1948                                 return TRUE;
1949                         case STRING_COLOR_TAG:
1950                                 ++in;
1951                                 switch((in == end) ? 0 : *in)
1952                                 {
1953                                         case STRING_COLOR_RGB_TAG_CHAR:
1954                                                 if (in+1 != end && isxdigit(in[1]) &&
1955                                                         in+2 != end && isxdigit(in[2]) &&
1956                                                         in+3 != end && isxdigit(in[3]) )
1957                                                 {
1958                                                         in+=3;
1959                                                         break;
1960                                                 }
1961                                                 APPEND(STRING_COLOR_TAG);
1962                                                 if(escape_carets)
1963                                                         APPEND(STRING_COLOR_TAG);
1964                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1965                                                 break;
1966                                         case 0: // ends with unfinished color code!
1967                                                 APPEND(STRING_COLOR_TAG);
1968                                                 // finish the code by appending another caret when escaping
1969                                                 if(escape_carets)
1970                                                         APPEND(STRING_COLOR_TAG);
1971                                                 *out++ = 0;
1972                                                 return TRUE;
1973                                         case STRING_COLOR_TAG: // escaped ^
1974                                                 APPEND(STRING_COLOR_TAG);
1975                                                 // append a ^ twice when escaping
1976                                                 if(escape_carets)
1977                                                         APPEND(STRING_COLOR_TAG);
1978                                                 break;
1979                                         case '0': case '1': case '2': case '3': case '4':
1980                                         case '5': case '6': case '7': case '8': case '9': // color code
1981                                                 break;
1982                                         default: // not a color code
1983                                                 APPEND(STRING_COLOR_TAG);
1984                                                 APPEND(*in);
1985                                                 break;
1986                                 }
1987                                 break;
1988                         default:
1989                                 APPEND(*in);
1990                                 break;
1991                 }
1992                 ++in;
1993         }
1994         // never get here
1995 #undef APPEND
1996 }
1997
1998 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1999 {
2000         int pos = 0, j;
2001         size_t keylength;
2002         if (!key)
2003                 key = "";
2004         keylength = strlen(key);
2005         if (valuelength < 1 || !value)
2006         {
2007                 Con_Printf("InfoString_GetValue: no room in value\n");
2008                 return NULL;
2009         }
2010         value[0] = 0;
2011         if (strchr(key, '\\'))
2012         {
2013                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
2014                 return NULL;
2015         }
2016         if (strchr(key, '\"'))
2017         {
2018                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2019                 return NULL;
2020         }
2021         if (!key[0])
2022         {
2023                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2024                 return NULL;
2025         }
2026         while (buffer[pos] == '\\')
2027         {
2028                 if (!memcmp(buffer + pos+1, key, keylength))
2029                 {
2030                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2031                         pos++;
2032                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2033                                 value[j] = buffer[pos+j];
2034                         value[j] = 0;
2035                         return value;
2036                 }
2037                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2038                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2039         }
2040         // if we reach this point the key was not found
2041         return NULL;
2042 }
2043
2044 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2045 {
2046         int pos = 0, pos2;
2047         size_t keylength;
2048         if (!key)
2049                 key = "";
2050         if (!value)
2051                 value = "";
2052         keylength = strlen(key);
2053         if (strchr(key, '\\') || strchr(value, '\\'))
2054         {
2055                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2056                 return;
2057         }
2058         if (strchr(key, '\"') || strchr(value, '\"'))
2059         {
2060                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2061                 return;
2062         }
2063         if (!key[0])
2064         {
2065                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2066                 return;
2067         }
2068         while (buffer[pos] == '\\')
2069         {
2070                 if (!memcmp(buffer + pos+1, key, keylength))
2071                         break;
2072                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2073                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2074         }
2075         // if we found the key, find the end of it because we will be replacing it
2076         pos2 = pos;
2077         if (buffer[pos] == '\\')
2078         {
2079                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2080                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2081         }
2082         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2083         {
2084                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2085                 return;
2086         }
2087         if (value && value[0])
2088         {
2089                 // set the key/value and append the remaining text
2090                 char tempbuffer[MAX_INPUTLINE];
2091                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2092                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2093         }
2094         else
2095         {
2096                 // just remove the key from the text
2097                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2098         }
2099 }
2100
2101 void InfoString_Print(char *buffer)
2102 {
2103         int i;
2104         char key[MAX_INPUTLINE];
2105         char value[MAX_INPUTLINE];
2106         while (*buffer)
2107         {
2108                 if (*buffer != '\\')
2109                 {
2110                         Con_Printf("InfoString_Print: corrupt string\n");
2111                         return;
2112                 }
2113                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2114                         if (i < (int)sizeof(key)-1)
2115                                 key[i++] = *buffer;
2116                 key[i] = 0;
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(value)-1)
2124                                 value[i++] = *buffer;
2125                 value[i] = 0;
2126                 // empty value is an error case
2127                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2128         }
2129 }
2130
2131 //========================================================
2132 // strlcat and strlcpy, from OpenBSD
2133
2134 /*
2135  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2136  *
2137  * Permission to use, copy, modify, and distribute this software for any
2138  * purpose with or without fee is hereby granted, provided that the above
2139  * copyright notice and this permission notice appear in all copies.
2140  *
2141  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2142  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2143  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2144  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2145  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2146  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2147  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2148  */
2149
2150 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2151 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2152
2153
2154 #ifndef HAVE_STRLCAT
2155 size_t
2156 strlcat(char *dst, const char *src, size_t siz)
2157 {
2158         register char *d = dst;
2159         register const char *s = src;
2160         register size_t n = siz;
2161         size_t dlen;
2162
2163         /* Find the end of dst and adjust bytes left but don't go past end */
2164         while (n-- != 0 && *d != '\0')
2165                 d++;
2166         dlen = d - dst;
2167         n = siz - dlen;
2168
2169         if (n == 0)
2170                 return(dlen + strlen(s));
2171         while (*s != '\0') {
2172                 if (n != 1) {
2173                         *d++ = *s;
2174                         n--;
2175                 }
2176                 s++;
2177         }
2178         *d = '\0';
2179
2180         return(dlen + (s - src));       /* count does not include NUL */
2181 }
2182 #endif  // #ifndef HAVE_STRLCAT
2183
2184
2185 #ifndef HAVE_STRLCPY
2186 size_t
2187 strlcpy(char *dst, const char *src, size_t siz)
2188 {
2189         register char *d = dst;
2190         register const char *s = src;
2191         register size_t n = siz;
2192
2193         /* Copy as many bytes as will fit */
2194         if (n != 0 && --n != 0) {
2195                 do {
2196                         if ((*d++ = *s++) == 0)
2197                                 break;
2198                 } while (--n != 0);
2199         }
2200
2201         /* Not enough room in dst, add NUL and traverse rest of src */
2202         if (n == 0) {
2203                 if (siz != 0)
2204                         *d = '\0';              /* NUL-terminate dst */
2205                 while (*s++)
2206                         ;
2207         }
2208
2209         return(s - src - 1);    /* count does not include NUL */
2210 }
2211
2212 #endif  // #ifndef HAVE_STRLCPY
2213
2214 void FindFraction(double val, int *num, int *denom, int denomMax)
2215 {
2216         int i;
2217         double bestdiff;
2218         // initialize
2219         bestdiff = fabs(val);
2220         *num = 0;
2221         *denom = 1;
2222
2223         for(i = 1; i <= denomMax; ++i)
2224         {
2225                 int inum = (int) floor(0.5 + val * i);
2226                 double diff = fabs(val - inum / (double)i);
2227                 if(diff < bestdiff)
2228                 {
2229                         bestdiff = diff;
2230                         *num = inum;
2231                         *denom = i;
2232                 }
2233         }
2234 }
2235
2236 // decodes an XPM from C syntax
2237 char **XPM_DecodeString(const char *in)
2238 {
2239         static char *tokens[257];
2240         static char lines[257][512];
2241         size_t line = 0;
2242
2243         // skip until "{" token
2244         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2245
2246         // now, read in succession: string, comma-or-}
2247         while(COM_ParseToken_QuakeC(&in, false))
2248         {
2249                 tokens[line] = lines[line];
2250                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2251                 if(!COM_ParseToken_QuakeC(&in, false))
2252                         return NULL;
2253                 if(!strcmp(com_token, "}"))
2254                         break;
2255                 if(strcmp(com_token, ","))
2256                         return NULL;
2257                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2258                         return NULL;
2259         }
2260
2261         return tokens;
2262 }
2263
2264 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2265 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2266 {
2267         unsigned char i0 = (bytes > 0) ? in[0] : 0;
2268         unsigned char i1 = (bytes > 1) ? in[1] : 0;
2269         unsigned char i2 = (bytes > 2) ? in[2] : 0;
2270         unsigned char o0 = base64[i0 >> 2];
2271         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2272         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2273         unsigned char o3 = base64[i2 & 077];
2274         out[0] = (bytes > 0) ? o0 : '?';
2275         out[1] = (bytes > 0) ? o1 : '?';
2276         out[2] = (bytes > 1) ? o2 : '=';
2277         out[3] = (bytes > 2) ? o3 : '=';
2278 }
2279
2280 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2281 {
2282         size_t blocks, i;
2283         // expand the out-buffer
2284         blocks = (buflen + 2) / 3;
2285         if(blocks*4 > outbuflen)
2286                 return 0;
2287         for(i = blocks; i > 0; )
2288         {
2289                 --i;
2290                 base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);
2291         }
2292         return blocks * 4;
2293 }