]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
Implemented r_sky_scissor feature (on by default) - this limits rendering of sky...
[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_STEELSTORMREVENANTS,             GAME_STEELSTORMREVENANTS,       "steelstorm-revenants", "-steelstormrev",                       "Steel Storm: Revenants",       "Steel_Storm_Revenants",        "base", NULL,                           "ssrev",                        "steelstorm-revenants"  }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
1479 { 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
1480 { 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
1481 { GAME_MOONHELM,                                GAME_MOONHELM,                          "moonhelm",                             "-moonhelm",                            "MoonHelm",                                     "MoonHelm",                                     "data",         NULL,                   "mh",                           "moonhelm"                              }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1482 { GAME_VORETOURNAMENT,                  GAME_VORETOURNAMENT,            "voretournament",               "-voretournament",                      "Vore Tournament",                      "Vore_Tournament",                      "data",         NULL,                   "voretournament",       "voretournament"                }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
1483 };
1484
1485 static void COM_SetGameType(int index);
1486 void COM_InitGameType (void)
1487 {
1488         char name [MAX_OSPATH];
1489         int i;
1490         int index = 0;
1491
1492 #ifdef FORCEGAME
1493         COM_ToLowerString(FORCEGAME, name, sizeof (name));
1494 #else
1495         // check executable filename for keywords, but do it SMARTLY - only check the last path element
1496         FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
1497         COM_ToLowerString(name, name, sizeof (name));
1498 #endif
1499         for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1500                 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1501                         index = i;
1502
1503         // check commandline options for keywords
1504         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1505                 if (COM_CheckParm (gamemode_info[i].cmdline))
1506                         index = i;
1507
1508         com_startupgamemode = gamemode_info[index].mode;
1509         com_startupgamegroup = gamemode_info[index].group;
1510         COM_SetGameType(index);
1511 }
1512
1513 void COM_ChangeGameTypeForGameDirs(void)
1514 {
1515         int i;
1516         int index = -1;
1517         // this will not not change the gamegroup
1518         // first check if a base game (single gamedir) matches
1519         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1520         {
1521                 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1522                 {
1523                         index = i;
1524                         break;
1525                 }
1526         }
1527         // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1528         if (fs_numgamedirs)
1529         {
1530                 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1531                 {
1532                         if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1533                         {
1534                                 index = i;
1535                                 break;
1536                         }
1537                 }
1538         }
1539         // we now have a good guess at which game this is meant to be...
1540         if (index >= 0 && gamemode != gamemode_info[index].mode)
1541                 COM_SetGameType(index);
1542 }
1543
1544 static void COM_SetGameType(int index)
1545 {
1546         static char gamenetworkfilternamebuffer[64];
1547         int i, t;
1548         if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1549                 index = 0;
1550         gamemode = gamemode_info[index].mode;
1551         gamename = gamemode_info[index].gamename;
1552         gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
1553         gamedirname1 = gamemode_info[index].gamedirname1;
1554         gamedirname2 = gamemode_info[index].gamedirname2;
1555         gamescreenshotname = gamemode_info[index].gamescreenshotname;
1556         gameuserdirname = gamemode_info[index].gameuserdirname;
1557
1558         if (gamemode == com_startupgamemode)
1559         {
1560                 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1561                         gamename = gamenetworkfiltername = com_argv[t+1];
1562                 if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < com_argc)
1563                         gamenetworkfiltername = com_argv[t+1];
1564                 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1565                         gamedirname1 = com_argv[t+1];
1566                 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1567                         gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1568                 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1569                         gamescreenshotname = com_argv[t+1];
1570                 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1571                         gameuserdirname = com_argv[t+1];
1572         }
1573
1574         if (gamedirname2 && gamedirname2[0])
1575                 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1576         else
1577                 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1578         for (i = 0;i < fs_numgamedirs;i++)
1579         {
1580                 if (i == 0)
1581                         Con_Printf(", with mod gamedirs");
1582                 Con_Printf(" %s", fs_gamedirs[i]);
1583         }
1584         Con_Printf("\n");
1585
1586         if (strchr(gamenetworkfiltername, ' '))
1587         {
1588                 char *s;
1589                 // if there are spaces in the game's network filter name it would
1590                 // cause parse errors in getservers in dpmaster, so we need to replace
1591                 // them with _ characters
1592                 strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
1593                 while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
1594                         *s = '_';
1595                 gamenetworkfiltername = gamenetworkfilternamebuffer;
1596         }
1597
1598         Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
1599 }
1600
1601
1602 /*
1603 ================
1604 COM_Init
1605 ================
1606 */
1607 void COM_Init_Commands (void)
1608 {
1609         int i, j, n;
1610         char com_cmdline[MAX_INPUTLINE];
1611
1612         Cvar_RegisterVariable (&registered);
1613         Cvar_RegisterVariable (&cmdline);
1614
1615         // reconstitute the command line for the cmdline externally visible cvar
1616         n = 0;
1617         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1618         {
1619                 i = 0;
1620                 if (strstr(com_argv[j], " "))
1621                 {
1622                         // arg contains whitespace, store quotes around it
1623                         // This condition checks whether we can allow to put
1624                         // in two quote characters.
1625                         if (n >= ((int)sizeof(com_cmdline) - 2))
1626                                 break;
1627                         com_cmdline[n++] = '\"';
1628                         // This condition checks whether we can allow one
1629                         // more character and a quote character.
1630                         while ((n < ((int)sizeof(com_cmdline) - 2)) && com_argv[j][i])
1631                                 // FIXME: Doesn't quote special characters.
1632                                 com_cmdline[n++] = com_argv[j][i++];
1633                         com_cmdline[n++] = '\"';
1634                 }
1635                 else
1636                 {
1637                         // This condition checks whether we can allow one
1638                         // more character.
1639                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1640                                 com_cmdline[n++] = com_argv[j][i++];
1641                 }
1642                 if (n < ((int)sizeof(com_cmdline) - 1))
1643                         com_cmdline[n++] = ' ';
1644                 else
1645                         break;
1646         }
1647         com_cmdline[n] = 0;
1648         Cvar_Set ("cmdline", com_cmdline);
1649 }
1650
1651 /*
1652 ============
1653 va
1654
1655 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1656 ============
1657 */
1658 char *va(char *buf, size_t buflen, const char *format, ...)
1659 {
1660         va_list argptr;
1661
1662         va_start (argptr, format);
1663         dpvsnprintf (buf, buflen, format,argptr);
1664         va_end (argptr);
1665
1666         return buf;
1667 }
1668
1669
1670 //======================================
1671
1672 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1673
1674 #undef snprintf
1675 #undef vsnprintf
1676
1677 #ifdef WIN32
1678 # define snprintf _snprintf
1679 # define vsnprintf _vsnprintf
1680 #endif
1681
1682
1683 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1684 {
1685         va_list args;
1686         int result;
1687
1688         va_start (args, format);
1689         result = dpvsnprintf (buffer, buffersize, format, args);
1690         va_end (args);
1691
1692         return result;
1693 }
1694
1695
1696 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1697 {
1698         int result;
1699
1700 #if _MSC_VER >= 1400
1701         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1702 #else
1703         result = vsnprintf (buffer, buffersize, format, args);
1704 #endif
1705         if (result < 0 || (size_t)result >= buffersize)
1706         {
1707                 buffer[buffersize - 1] = '\0';
1708                 return -1;
1709         }
1710
1711         return result;
1712 }
1713
1714
1715 //======================================
1716
1717 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1718 {
1719         if (size_out == 0)
1720                 return;
1721
1722         if(utf8_enable.integer)
1723         {
1724                 *out = 0;
1725                 while(*in && size_out > 1)
1726                 {
1727                         int n;
1728                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1729                         ch = u8_tolower(ch);
1730                         n = u8_fromchar(ch, out, size_out);
1731                         if(n <= 0)
1732                                 break;
1733                         out += n;
1734                         size_out -= n;
1735                 }
1736                 return;
1737         }
1738
1739         while (*in && size_out > 1)
1740         {
1741                 if (*in >= 'A' && *in <= 'Z')
1742                         *out++ = *in++ + 'a' - 'A';
1743                 else
1744                         *out++ = *in++;
1745                 size_out--;
1746         }
1747         *out = '\0';
1748 }
1749
1750 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1751 {
1752         if (size_out == 0)
1753                 return;
1754
1755         if(utf8_enable.integer)
1756         {
1757                 *out = 0;
1758                 while(*in && size_out > 1)
1759                 {
1760                         int n;
1761                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1762                         ch = u8_toupper(ch);
1763                         n = u8_fromchar(ch, out, size_out);
1764                         if(n <= 0)
1765                                 break;
1766                         out += n;
1767                         size_out -= n;
1768                 }
1769                 return;
1770         }
1771
1772         while (*in && size_out > 1)
1773         {
1774                 if (*in >= 'a' && *in <= 'z')
1775                         *out++ = *in++ + 'A' - 'a';
1776                 else
1777                         *out++ = *in++;
1778                 size_out--;
1779         }
1780         *out = '\0';
1781 }
1782
1783 int COM_StringBeginsWith(const char *s, const char *match)
1784 {
1785         for (;*s && *match;s++, match++)
1786                 if (*s != *match)
1787                         return false;
1788         return true;
1789 }
1790
1791 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1792 {
1793         int argc, commentprefixlength;
1794         char *tokenbufend;
1795         const char *l;
1796         argc = 0;
1797         tokenbufend = tokenbuf + tokenbufsize;
1798         l = *text;
1799         commentprefixlength = 0;
1800         if (commentprefix)
1801                 commentprefixlength = (int)strlen(commentprefix);
1802         while (*l && *l != '\n' && *l != '\r')
1803         {
1804                 if (!ISWHITESPACE(*l))
1805                 {
1806                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1807                         {
1808                                 while (*l && *l != '\n' && *l != '\r')
1809                                         l++;
1810                                 break;
1811                         }
1812                         if (argc >= maxargc)
1813                                 return -1;
1814                         argv[argc++] = tokenbuf;
1815                         if (*l == '"')
1816                         {
1817                                 l++;
1818                                 while (*l && *l != '"')
1819                                 {
1820                                         if (tokenbuf >= tokenbufend)
1821                                                 return -1;
1822                                         *tokenbuf++ = *l++;
1823                                 }
1824                                 if (*l == '"')
1825                                         l++;
1826                         }
1827                         else
1828                         {
1829                                 while (!ISWHITESPACE(*l))
1830                                 {
1831                                         if (tokenbuf >= tokenbufend)
1832                                                 return -1;
1833                                         *tokenbuf++ = *l++;
1834                                 }
1835                         }
1836                         if (tokenbuf >= tokenbufend)
1837                                 return -1;
1838                         *tokenbuf++ = 0;
1839                 }
1840                 else
1841                         l++;
1842         }
1843         // line endings:
1844         // UNIX: \n
1845         // Mac: \r
1846         // Windows: \r\n
1847         if (*l == '\r')
1848                 l++;
1849         if (*l == '\n')
1850                 l++;
1851         *text = l;
1852         return argc;
1853 }
1854
1855 /*
1856 ============
1857 COM_StringLengthNoColors
1858
1859 calculates the visible width of a color coded string.
1860
1861 *valid is filled with TRUE if the string is a valid colored string (that is, if
1862 it does not end with an unfinished color code). If it gets filled with FALSE, a
1863 fix would be adding a STRING_COLOR_TAG at the end of the string.
1864
1865 valid can be set to NULL if the caller doesn't care.
1866
1867 For size_s, specify the maximum number of characters from s to use, or 0 to use
1868 all characters until the zero terminator.
1869 ============
1870 */
1871 size_t
1872 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1873 {
1874         const char *end = size_s ? (s + size_s) : NULL;
1875         size_t len = 0;
1876         for(;;)
1877         {
1878                 switch((s == end) ? 0 : *s)
1879                 {
1880                         case 0:
1881                                 if(valid)
1882                                         *valid = TRUE;
1883                                 return len;
1884                         case STRING_COLOR_TAG:
1885                                 ++s;
1886                                 switch((s == end) ? 0 : *s)
1887                                 {
1888                                         case STRING_COLOR_RGB_TAG_CHAR:
1889                                                 if (s+1 != end && isxdigit(s[1]) &&
1890                                                         s+2 != end && isxdigit(s[2]) &&
1891                                                         s+3 != end && isxdigit(s[3]) )
1892                                                 {
1893                                                         s+=3;
1894                                                         break;
1895                                                 }
1896                                                 ++len; // STRING_COLOR_TAG
1897                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1898                                                 break;
1899                                         case 0: // ends with unfinished color code!
1900                                                 ++len;
1901                                                 if(valid)
1902                                                         *valid = FALSE;
1903                                                 return len;
1904                                         case STRING_COLOR_TAG: // escaped ^
1905                                                 ++len;
1906                                                 break;
1907                                         case '0': case '1': case '2': case '3': case '4':
1908                                         case '5': case '6': case '7': case '8': case '9': // color code
1909                                                 break;
1910                                         default: // not a color code
1911                                                 ++len; // STRING_COLOR_TAG
1912                                                 ++len; // the character
1913                                                 break;
1914                                 }
1915                                 break;
1916                         default:
1917                                 ++len;
1918                                 break;
1919                 }
1920                 ++s;
1921         }
1922         // never get here
1923 }
1924
1925 /*
1926 ============
1927 COM_StringDecolorize
1928
1929 removes color codes from a string.
1930
1931 If escape_carets is true, the resulting string will be safe for printing. If
1932 escape_carets is false, the function will just strip color codes (for logging
1933 for example).
1934
1935 If the output buffer size did not suffice for converting, the function returns
1936 FALSE. Generally, if escape_carets is false, the output buffer needs
1937 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1938 bytes. In any case, the function makes sure that the resulting string is
1939 zero terminated.
1940
1941 For size_in, specify the maximum number of characters from in to use, or 0 to use
1942 all characters until the zero terminator.
1943 ============
1944 */
1945 qboolean
1946 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1947 {
1948 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1949         const char *end = size_in ? (in + size_in) : NULL;
1950         if(size_out < 1)
1951                 return FALSE;
1952         for(;;)
1953         {
1954                 switch((in == end) ? 0 : *in)
1955                 {
1956                         case 0:
1957                                 *out++ = 0;
1958                                 return TRUE;
1959                         case STRING_COLOR_TAG:
1960                                 ++in;
1961                                 switch((in == end) ? 0 : *in)
1962                                 {
1963                                         case STRING_COLOR_RGB_TAG_CHAR:
1964                                                 if (in+1 != end && isxdigit(in[1]) &&
1965                                                         in+2 != end && isxdigit(in[2]) &&
1966                                                         in+3 != end && isxdigit(in[3]) )
1967                                                 {
1968                                                         in+=3;
1969                                                         break;
1970                                                 }
1971                                                 APPEND(STRING_COLOR_TAG);
1972                                                 if(escape_carets)
1973                                                         APPEND(STRING_COLOR_TAG);
1974                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1975                                                 break;
1976                                         case 0: // ends with unfinished color code!
1977                                                 APPEND(STRING_COLOR_TAG);
1978                                                 // finish the code by appending another caret when escaping
1979                                                 if(escape_carets)
1980                                                         APPEND(STRING_COLOR_TAG);
1981                                                 *out++ = 0;
1982                                                 return TRUE;
1983                                         case STRING_COLOR_TAG: // escaped ^
1984                                                 APPEND(STRING_COLOR_TAG);
1985                                                 // append a ^ twice when escaping
1986                                                 if(escape_carets)
1987                                                         APPEND(STRING_COLOR_TAG);
1988                                                 break;
1989                                         case '0': case '1': case '2': case '3': case '4':
1990                                         case '5': case '6': case '7': case '8': case '9': // color code
1991                                                 break;
1992                                         default: // not a color code
1993                                                 APPEND(STRING_COLOR_TAG);
1994                                                 APPEND(*in);
1995                                                 break;
1996                                 }
1997                                 break;
1998                         default:
1999                                 APPEND(*in);
2000                                 break;
2001                 }
2002                 ++in;
2003         }
2004         // never get here
2005 #undef APPEND
2006 }
2007
2008 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
2009 {
2010         int pos = 0, j;
2011         size_t keylength;
2012         if (!key)
2013                 key = "";
2014         keylength = strlen(key);
2015         if (valuelength < 1 || !value)
2016         {
2017                 Con_Printf("InfoString_GetValue: no room in value\n");
2018                 return NULL;
2019         }
2020         value[0] = 0;
2021         if (strchr(key, '\\'))
2022         {
2023                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
2024                 return NULL;
2025         }
2026         if (strchr(key, '\"'))
2027         {
2028                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2029                 return NULL;
2030         }
2031         if (!key[0])
2032         {
2033                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2034                 return NULL;
2035         }
2036         while (buffer[pos] == '\\')
2037         {
2038                 if (!memcmp(buffer + pos+1, key, keylength))
2039                 {
2040                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2041                         pos++;
2042                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2043                                 value[j] = buffer[pos+j];
2044                         value[j] = 0;
2045                         return value;
2046                 }
2047                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2048                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2049         }
2050         // if we reach this point the key was not found
2051         return NULL;
2052 }
2053
2054 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2055 {
2056         int pos = 0, pos2;
2057         size_t keylength;
2058         if (!key)
2059                 key = "";
2060         if (!value)
2061                 value = "";
2062         keylength = strlen(key);
2063         if (strchr(key, '\\') || strchr(value, '\\'))
2064         {
2065                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2066                 return;
2067         }
2068         if (strchr(key, '\"') || strchr(value, '\"'))
2069         {
2070                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2071                 return;
2072         }
2073         if (!key[0])
2074         {
2075                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2076                 return;
2077         }
2078         while (buffer[pos] == '\\')
2079         {
2080                 if (!memcmp(buffer + pos+1, key, keylength))
2081                         break;
2082                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2083                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2084         }
2085         // if we found the key, find the end of it because we will be replacing it
2086         pos2 = pos;
2087         if (buffer[pos] == '\\')
2088         {
2089                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2090                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2091         }
2092         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2093         {
2094                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2095                 return;
2096         }
2097         if (value[0])
2098         {
2099                 // set the key/value and append the remaining text
2100                 char tempbuffer[MAX_INPUTLINE];
2101                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2102                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2103         }
2104         else
2105         {
2106                 // just remove the key from the text
2107                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2108         }
2109 }
2110
2111 void InfoString_Print(char *buffer)
2112 {
2113         int i;
2114         char key[MAX_INPUTLINE];
2115         char value[MAX_INPUTLINE];
2116         while (*buffer)
2117         {
2118                 if (*buffer != '\\')
2119                 {
2120                         Con_Printf("InfoString_Print: corrupt string\n");
2121                         return;
2122                 }
2123                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2124                         if (i < (int)sizeof(key)-1)
2125                                 key[i++] = *buffer;
2126                 key[i] = 0;
2127                 if (*buffer != '\\')
2128                 {
2129                         Con_Printf("InfoString_Print: corrupt string\n");
2130                         return;
2131                 }
2132                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2133                         if (i < (int)sizeof(value)-1)
2134                                 value[i++] = *buffer;
2135                 value[i] = 0;
2136                 // empty value is an error case
2137                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2138         }
2139 }
2140
2141 //========================================================
2142 // strlcat and strlcpy, from OpenBSD
2143
2144 /*
2145  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2146  *
2147  * Permission to use, copy, modify, and distribute this software for any
2148  * purpose with or without fee is hereby granted, provided that the above
2149  * copyright notice and this permission notice appear in all copies.
2150  *
2151  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2152  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2153  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2154  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2155  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2156  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2157  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2158  */
2159
2160 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2161 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2162
2163
2164 #ifndef HAVE_STRLCAT
2165 size_t
2166 strlcat(char *dst, const char *src, size_t siz)
2167 {
2168         register char *d = dst;
2169         register const char *s = src;
2170         register size_t n = siz;
2171         size_t dlen;
2172
2173         /* Find the end of dst and adjust bytes left but don't go past end */
2174         while (n-- != 0 && *d != '\0')
2175                 d++;
2176         dlen = d - dst;
2177         n = siz - dlen;
2178
2179         if (n == 0)
2180                 return(dlen + strlen(s));
2181         while (*s != '\0') {
2182                 if (n != 1) {
2183                         *d++ = *s;
2184                         n--;
2185                 }
2186                 s++;
2187         }
2188         *d = '\0';
2189
2190         return(dlen + (s - src));       /* count does not include NUL */
2191 }
2192 #endif  // #ifndef HAVE_STRLCAT
2193
2194
2195 #ifndef HAVE_STRLCPY
2196 size_t
2197 strlcpy(char *dst, const char *src, size_t siz)
2198 {
2199         register char *d = dst;
2200         register const char *s = src;
2201         register size_t n = siz;
2202
2203         /* Copy as many bytes as will fit */
2204         if (n != 0 && --n != 0) {
2205                 do {
2206                         if ((*d++ = *s++) == 0)
2207                                 break;
2208                 } while (--n != 0);
2209         }
2210
2211         /* Not enough room in dst, add NUL and traverse rest of src */
2212         if (n == 0) {
2213                 if (siz != 0)
2214                         *d = '\0';              /* NUL-terminate dst */
2215                 while (*s++)
2216                         ;
2217         }
2218
2219         return(s - src - 1);    /* count does not include NUL */
2220 }
2221
2222 #endif  // #ifndef HAVE_STRLCPY
2223
2224 void FindFraction(double val, int *num, int *denom, int denomMax)
2225 {
2226         int i;
2227         double bestdiff;
2228         // initialize
2229         bestdiff = fabs(val);
2230         *num = 0;
2231         *denom = 1;
2232
2233         for(i = 1; i <= denomMax; ++i)
2234         {
2235                 int inum = (int) floor(0.5 + val * i);
2236                 double diff = fabs(val - inum / (double)i);
2237                 if(diff < bestdiff)
2238                 {
2239                         bestdiff = diff;
2240                         *num = inum;
2241                         *denom = i;
2242                 }
2243         }
2244 }
2245
2246 // decodes an XPM from C syntax
2247 char **XPM_DecodeString(const char *in)
2248 {
2249         static char *tokens[257];
2250         static char lines[257][512];
2251         size_t line = 0;
2252
2253         // skip until "{" token
2254         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2255
2256         // now, read in succession: string, comma-or-}
2257         while(COM_ParseToken_QuakeC(&in, false))
2258         {
2259                 tokens[line] = lines[line];
2260                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2261                 if(!COM_ParseToken_QuakeC(&in, false))
2262                         return NULL;
2263                 if(!strcmp(com_token, "}"))
2264                         break;
2265                 if(strcmp(com_token, ","))
2266                         return NULL;
2267                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2268                         return NULL;
2269         }
2270
2271         return tokens;
2272 }
2273
2274 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2275 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2276 {
2277         unsigned char i0 = (bytes > 0) ? in[0] : 0;
2278         unsigned char i1 = (bytes > 1) ? in[1] : 0;
2279         unsigned char i2 = (bytes > 2) ? in[2] : 0;
2280         unsigned char o0 = base64[i0 >> 2];
2281         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2282         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2283         unsigned char o3 = base64[i2 & 077];
2284         out[0] = (bytes > 0) ? o0 : '?';
2285         out[1] = (bytes > 0) ? o1 : '?';
2286         out[2] = (bytes > 1) ? o2 : '=';
2287         out[3] = (bytes > 2) ? o3 : '=';
2288 }
2289
2290 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2291 {
2292         size_t blocks, i;
2293         // expand the out-buffer
2294         blocks = (buflen + 2) / 3;
2295         if(blocks*4 > outbuflen)
2296                 return 0;
2297         for(i = blocks; i > 0; )
2298         {
2299                 --i;
2300                 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));
2301         }
2302         return blocks * 4;
2303 }