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