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