fe7e44298823f3d2fdcdb3461490ec1895b44176
[divverent/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
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
32
33 char com_token[MAX_INPUTLINE];
34 int com_argc;
35 const char **com_argv;
36
37 gamemode_t gamemode;
38 const char *gamename;
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
44
45
46 /*
47 ============================================================================
48
49                                         BYTE ORDER FUNCTIONS
50
51 ============================================================================
52 */
53
54 short   ShortSwap (short l)
55 {
56         unsigned char    b1,b2;
57
58         b1 = l&255;
59         b2 = (l>>8)&255;
60
61         return (b1<<8) + b2;
62 }
63
64 int    LongSwap (int l)
65 {
66         unsigned char    b1,b2,b3,b4;
67
68         b1 = l&255;
69         b2 = (l>>8)&255;
70         b3 = (l>>16)&255;
71         b4 = (l>>24)&255;
72
73         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
74 }
75
76 float FloatSwap (float f)
77 {
78         union
79         {
80                 float   f;
81                 unsigned char    b[4];
82         } dat1, dat2;
83
84
85         dat1.f = f;
86         dat2.b[0] = dat1.b[3];
87         dat2.b[1] = dat1.b[2];
88         dat2.b[2] = dat1.b[1];
89         dat2.b[3] = dat1.b[0];
90         return dat2.f;
91 }
92
93
94 // Extract integers from buffers
95
96 unsigned int BuffBigLong (const unsigned char *buffer)
97 {
98         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
99 }
100
101 unsigned short BuffBigShort (const unsigned char *buffer)
102 {
103         return (buffer[0] << 8) | buffer[1];
104 }
105
106 unsigned int BuffLittleLong (const unsigned char *buffer)
107 {
108         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
109 }
110
111 unsigned short BuffLittleShort (const unsigned char *buffer)
112 {
113         return (buffer[1] << 8) | buffer[0];
114 }
115
116
117 /*
118 ============================================================================
119
120                                         CRC FUNCTIONS
121
122 ============================================================================
123 */
124
125 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
126 // and the initial and final xor values shown below...  in other words, the
127 // CCITT standard CRC used by XMODEM
128
129 #define CRC_INIT_VALUE  0xffff
130 #define CRC_XOR_VALUE   0x0000
131
132 static unsigned short crctable[256] =
133 {
134         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
135         0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
136         0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
137         0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
138         0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
139         0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
140         0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
141         0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
142         0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
143         0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
144         0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
145         0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
146         0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
147         0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
148         0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
149         0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
150         0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
151         0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
152         0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
153         0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
154         0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
155         0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
156         0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
157         0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
158         0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
159         0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
160         0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
161         0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
162         0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
163         0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
164         0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
165         0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
166 };
167
168 unsigned short CRC_Block(const unsigned char *data, size_t size)
169 {
170         unsigned short crc = CRC_INIT_VALUE;
171         while (size--)
172                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
173         return crc ^ CRC_XOR_VALUE;
174 }
175
176 // QuakeWorld
177 static unsigned char chktbl[1024 + 4] =
178 {
179         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
180         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
181         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
182         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
183         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
184         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
185         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
186         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
187         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
188         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
189         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
190         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
191         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
192         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
193         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
194         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
195         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
196         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
197         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
198         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
199         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
200         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
201         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
202         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
203         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
204         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
205         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
206         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
207         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
208         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
209         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
210         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
211
212         // map checksum goes here
213         0x00,0x00,0x00,0x00
214 };
215
216 // QuakeWorld
217 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
218 {
219         unsigned char *p;
220         unsigned char chkb[60 + 4];
221
222         p = chktbl + (sequence % (sizeof(chktbl) - 8));
223
224         if (length > 60)
225                 length = 60;
226         memcpy(chkb, base, length);
227
228         chkb[length] = (sequence & 0xff) ^ p[0];
229         chkb[length+1] = p[1];
230         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
231         chkb[length+3] = p[3];
232
233         return CRC_Block(chkb, length + 4) & 0xff;
234 }
235
236 /*
237 ==============================================================================
238
239                         MESSAGE IO FUNCTIONS
240
241 Handles byte ordering and avoids alignment errors
242 ==============================================================================
243 */
244
245 //
246 // writing functions
247 //
248
249 void MSG_WriteChar (sizebuf_t *sb, int c)
250 {
251         unsigned char    *buf;
252
253         buf = SZ_GetSpace (sb, 1);
254         buf[0] = c;
255 }
256
257 void MSG_WriteByte (sizebuf_t *sb, int c)
258 {
259         unsigned char    *buf;
260
261         buf = SZ_GetSpace (sb, 1);
262         buf[0] = c;
263 }
264
265 void MSG_WriteShort (sizebuf_t *sb, int c)
266 {
267         unsigned char    *buf;
268
269         buf = SZ_GetSpace (sb, 2);
270         buf[0] = c&0xff;
271         buf[1] = c>>8;
272 }
273
274 void MSG_WriteLong (sizebuf_t *sb, int c)
275 {
276         unsigned char    *buf;
277
278         buf = SZ_GetSpace (sb, 4);
279         buf[0] = c&0xff;
280         buf[1] = (c>>8)&0xff;
281         buf[2] = (c>>16)&0xff;
282         buf[3] = c>>24;
283 }
284
285 void MSG_WriteFloat (sizebuf_t *sb, float f)
286 {
287         union
288         {
289                 float   f;
290                 int     l;
291         } dat;
292
293
294         dat.f = f;
295         dat.l = LittleLong (dat.l);
296
297         SZ_Write (sb, (unsigned char *)&dat.l, 4);
298 }
299
300 void MSG_WriteString (sizebuf_t *sb, const char *s)
301 {
302         if (!s || !*s)
303                 MSG_WriteChar (sb, 0);
304         else
305                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
306 }
307
308 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
309 {
310         if (s && *s)
311                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
312 }
313
314 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
315 {
316         if (f >= 0)
317                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
318         else
319                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
320 }
321
322 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
323 {
324         if (f >= 0)
325                 MSG_WriteShort (sb, (int)(f + 0.5));
326         else
327                 MSG_WriteShort (sb, (int)(f - 0.5));
328 }
329
330 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
331 {
332         MSG_WriteFloat (sb, f);
333 }
334
335 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
336 {
337         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
338                 MSG_WriteCoord13i (sb, f);
339         else if (protocol == PROTOCOL_DARKPLACES1)
340                 MSG_WriteCoord32f (sb, f);
341         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
342                 MSG_WriteCoord16i (sb, f);
343         else
344                 MSG_WriteCoord32f (sb, f);
345 }
346
347 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
348 {
349         MSG_WriteCoord (sb, v[0], protocol);
350         MSG_WriteCoord (sb, v[1], protocol);
351         MSG_WriteCoord (sb, v[2], protocol);
352 }
353
354 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
355 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
356 {
357         if (f >= 0)
358                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
359         else
360                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
361 }
362
363 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
364 {
365         if (f >= 0)
366                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
367         else
368                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
369 }
370
371 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
372 {
373         MSG_WriteFloat (sb, f);
374 }
375
376 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
377 {
378         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
379                 MSG_WriteAngle8i (sb, f);
380         else
381                 MSG_WriteAngle16i (sb, f);
382 }
383
384 //
385 // reading functions
386 //
387 int msg_readcount;
388 qboolean msg_badread;
389
390 void MSG_BeginReading (void)
391 {
392         msg_readcount = 0;
393         msg_badread = false;
394 }
395
396 int MSG_ReadLittleShort (void)
397 {
398         if (msg_readcount+2 > net_message.cursize)
399         {
400                 msg_badread = true;
401                 return -1;
402         }
403         msg_readcount += 2;
404         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
405 }
406
407 int MSG_ReadBigShort (void)
408 {
409         if (msg_readcount+2 > net_message.cursize)
410         {
411                 msg_badread = true;
412                 return -1;
413         }
414         msg_readcount += 2;
415         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
416 }
417
418 int MSG_ReadLittleLong (void)
419 {
420         if (msg_readcount+4 > net_message.cursize)
421         {
422                 msg_badread = true;
423                 return -1;
424         }
425         msg_readcount += 4;
426         return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
427 }
428
429 int MSG_ReadBigLong (void)
430 {
431         if (msg_readcount+4 > net_message.cursize)
432         {
433                 msg_badread = true;
434                 return -1;
435         }
436         msg_readcount += 4;
437         return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
438 }
439
440 float MSG_ReadLittleFloat (void)
441 {
442         union
443         {
444                 float f;
445                 int l;
446         } dat;
447         if (msg_readcount+4 > net_message.cursize)
448         {
449                 msg_badread = true;
450                 return -1;
451         }
452         msg_readcount += 4;
453         dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
454         return dat.f;
455 }
456
457 float MSG_ReadBigFloat (void)
458 {
459         union
460         {
461                 float f;
462                 int l;
463         } dat;
464         if (msg_readcount+4 > net_message.cursize)
465         {
466                 msg_badread = true;
467                 return -1;
468         }
469         msg_readcount += 4;
470         dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
471         return dat.f;
472 }
473
474 char *MSG_ReadString (void)
475 {
476         static char string[MAX_INPUTLINE];
477         int l,c;
478         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
479                 string[l] = c;
480         string[l] = 0;
481         return string;
482 }
483
484 int MSG_ReadBytes (int numbytes, unsigned char *out)
485 {
486         int l, c;
487         for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
488                 out[l] = c;
489         return l;
490 }
491
492 float MSG_ReadCoord13i (void)
493 {
494         return MSG_ReadLittleShort() * (1.0/8.0);
495 }
496
497 float MSG_ReadCoord16i (void)
498 {
499         return (signed short) MSG_ReadLittleShort();
500 }
501
502 float MSG_ReadCoord32f (void)
503 {
504         return MSG_ReadLittleFloat();
505 }
506
507 float MSG_ReadCoord (protocolversion_t protocol)
508 {
509         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD)
510                 return MSG_ReadCoord13i();
511         else if (protocol == PROTOCOL_DARKPLACES1)
512                 return MSG_ReadCoord32f();
513         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
514                 return MSG_ReadCoord16i();
515         else
516                 return MSG_ReadCoord32f();
517 }
518
519 void MSG_ReadVector (float *v, protocolversion_t protocol)
520 {
521         v[0] = MSG_ReadCoord(protocol);
522         v[1] = MSG_ReadCoord(protocol);
523         v[2] = MSG_ReadCoord(protocol);
524 }
525
526 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
527 float MSG_ReadAngle8i (void)
528 {
529         return (signed char) MSG_ReadByte () * (360.0/256.0);
530 }
531
532 float MSG_ReadAngle16i (void)
533 {
534         return (signed short)MSG_ReadShort () * (360.0/65536.0);
535 }
536
537 float MSG_ReadAngle32f (void)
538 {
539         return MSG_ReadFloat ();
540 }
541
542 float MSG_ReadAngle (protocolversion_t protocol)
543 {
544         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
545                 return MSG_ReadAngle8i ();
546         else
547                 return MSG_ReadAngle16i ();
548 }
549
550
551 //===========================================================================
552
553 void SZ_Clear (sizebuf_t *buf)
554 {
555         buf->cursize = 0;
556 }
557
558 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
559 {
560         unsigned char *data;
561
562         if (buf->cursize + length > buf->maxsize)
563         {
564                 if (!buf->allowoverflow)
565                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
566
567                 if (length > buf->maxsize)
568                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
569
570                 buf->overflowed = true;
571                 Con_Print("SZ_GetSpace: overflow\n");
572                 SZ_Clear (buf);
573         }
574
575         data = buf->data + buf->cursize;
576         buf->cursize += length;
577
578         return data;
579 }
580
581 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
582 {
583         memcpy (SZ_GetSpace(buf,length),data,length);
584 }
585
586 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
587 // attention, it has been eradicated from here, its only (former) use in
588 // all of darkplaces.
589
590 static char *hexchar = "0123456789ABCDEF";
591 void Com_HexDumpToConsole(const unsigned char *data, int size)
592 {
593         int i, j, n;
594         char text[1024];
595         char *cur, *flushpointer;
596         const unsigned char *d;
597         cur = text;
598         flushpointer = text + 512;
599         for (i = 0;i < size;)
600         {
601                 n = 16;
602                 if (n > size - i)
603                         n = size - i;
604                 d = data + i;
605                 // print offset
606                 *cur++ = hexchar[(i >> 12) & 15];
607                 *cur++ = hexchar[(i >>  8) & 15];
608                 *cur++ = hexchar[(i >>  4) & 15];
609                 *cur++ = hexchar[(i >>  0) & 15];
610                 *cur++ = ':';
611                 // print hex
612                 for (j = 0;j < 16;j++)
613                 {
614                         if (j < n)
615                         {
616                                 *cur++ = hexchar[(d[j] >> 4) & 15];
617                                 *cur++ = hexchar[(d[j] >> 0) & 15];
618                         }
619                         else
620                         {
621                                 *cur++ = ' ';
622                                 *cur++ = ' ';
623                         }
624                         if ((j & 3) == 3)
625                                 *cur++ = ' ';
626                 }
627                 // print text
628                 for (j = 0;j < 16;j++)
629                 {
630                         if (j < n)
631                         {
632                                 // color change prefix character has to be treated specially
633                                 if (d[j] == STRING_COLOR_TAG)
634                                 {
635                                         *cur++ = STRING_COLOR_TAG;
636                                         *cur++ = STRING_COLOR_TAG;
637                                 }
638                                 else if (d[j] >= ' ')
639                                         *cur++ = d[j];
640                                 else
641                                         *cur++ = '.';
642                         }
643                         else
644                                 *cur++ = ' ';
645                 }
646                 *cur++ = '\n';
647                 i += n;
648                 if (cur >= flushpointer || i >= size)
649                 {
650                         *cur++ = 0;
651                         Con_Print(text);
652                         cur = text;
653                 }
654         }
655 }
656
657 void SZ_HexDumpToConsole(const sizebuf_t *buf)
658 {
659         Com_HexDumpToConsole(buf->data, buf->cursize);
660 }
661
662
663 //============================================================================
664
665
666 /*
667 ==============
668 COM_ParseToken
669
670 Parse a token out of a string
671 ==============
672 */
673 int COM_ParseToken(const char **datapointer, int returnnewline)
674 {
675         int len;
676         int c;
677         const char *data = *datapointer;
678
679         len = 0;
680         com_token[0] = 0;
681
682         if (!data)
683         {
684                 *datapointer = NULL;
685                 return false;
686         }
687
688 // skip whitespace
689 skipwhite:
690         // line endings:
691         // UNIX: \n
692         // Mac: \r
693         // Windows: \r\n
694         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
695         {
696                 if (*data == 0)
697                 {
698                         // end of file
699                         *datapointer = NULL;
700                         return false;
701                 }
702         }
703
704         // handle Windows line ending
705         if (data[0] == '\r' && data[1] == '\n')
706                 data++;
707
708         if (data[0] == '/' && data[1] == '/')
709         {
710                 // comment
711                 while (*data && *data != '\n' && *data != '\r')
712                         data++;
713                 goto skipwhite;
714         }
715         else if (data[0] == '/' && data[1] == '*')
716         {
717                 // comment
718                 data++;
719                 while (*data && (data[0] != '*' || data[1] != '/'))
720                         data++;
721                 data += 2;
722                 goto skipwhite;
723         }
724         else if (*data == '\"')
725         {
726                 // quoted string
727                 for (data++;*data != '\"';data++)
728                 {
729                         if (!*data || len >= (int)sizeof(com_token) - 1)
730                         {
731                                 com_token[0] = 0;
732                                 *datapointer = NULL;
733                                 return false;
734                         }
735                         c = *data;
736                         if (*data == '\\')
737                         {
738                                 if (data[1] == '"')
739                                 {
740                                         data++;
741                                         c = *data;
742                                 }
743                                 else if (data[1] == '\'')
744                                 {
745                                         data++;
746                                         c = *data;
747                                 }
748                                 else if (data[1] == 'n')
749                                 {
750                                         data++;
751                                         c = '\n';
752                                 }
753                                 else if (data[1] == '\\')
754                                         data++;
755                         }
756                         com_token[len++] = c;
757                 }
758                 com_token[len] = 0;
759                 *datapointer = data+1;
760                 return true;
761         }
762         else if (*data == '\'')
763         {
764                 // quoted string
765                 for (data++;*data != '\'';data++)
766                 {
767                         if (!*data || len >= (int)sizeof(com_token) - 1)
768                         {
769                                 com_token[0] = 0;
770                                 *datapointer = NULL;
771                                 return false;
772                         }
773                         c = *data;
774                         if (*data == '\\')
775                         {
776                                 if (data[1] == '"')
777                                 {
778                                         data++;
779                                         c = *data;
780                                 }
781                                 else if (data[1] == '\'')
782                                 {
783                                         data++;
784                                         c = *data;
785                                 }
786                                 else if (data[1] == 'n')
787                                 {
788                                         data++;
789                                         c = '\n';
790                                 }
791                                 else if (data[1] == '\\')
792                                         data++;
793                         }
794                         com_token[len++] = c;
795                 }
796                 com_token[len] = 0;
797                 *datapointer = data+1;
798                 return true;
799         }
800         else if (*data == '\r')
801         {
802                 // translate Mac line ending to UNIX
803                 com_token[len++] = '\n';
804                 com_token[len] = 0;
805                 *datapointer = data;
806                 return true;
807         }
808         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';')
809         {
810                 // single character
811                 com_token[len++] = *data++;
812                 com_token[len] = 0;
813                 *datapointer = data;
814                 return true;
815         }
816         else
817         {
818                 // regular word
819                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';' && *data != '\'' && *data != '"';data++)
820                 {
821                         if (len >= (int)sizeof(com_token) - 1)
822                         {
823                                 com_token[0] = 0;
824                                 *datapointer = NULL;
825                                 return false;
826                         }
827                         c = *data;
828                         if (*data == '\\')
829                         {
830                                 if (data[1] == '"')
831                                 {
832                                         data++;
833                                         c = *data;
834                                 }
835                                 else if (data[1] == '\'')
836                                 {
837                                         data++;
838                                         c = *data;
839                                 }
840                                 else if (data[1] == 'n')
841                                 {
842                                         data++;
843                                         c = '\n';
844                                 }
845                                 else if (data[1] == '\\')
846                                         data++;
847                         }
848                         com_token[len++] = c;
849                 }
850                 com_token[len] = 0;
851                 *datapointer = data;
852                 return true;
853         }
854 }
855
856 /*
857 ==============
858 COM_ParseTokenConsole
859
860 Parse a token out of a string, behaving like the qwcl console
861 ==============
862 */
863 int COM_ParseTokenConsole(const char **datapointer)
864 {
865         int len;
866         const char *data = *datapointer;
867
868         len = 0;
869         com_token[0] = 0;
870
871         if (!data)
872         {
873                 *datapointer = NULL;
874                 return false;
875         }
876
877 // skip whitespace
878 skipwhite:
879         for (;*data <= ' ';data++)
880         {
881                 if (*data == 0)
882                 {
883                         // end of file
884                         *datapointer = NULL;
885                         return false;
886                 }
887         }
888
889         if (*data == '/' && data[1] == '/')
890         {
891                 // comment
892                 while (*data && *data != '\n' && *data != '\r')
893                         data++;
894                 goto skipwhite;
895         }
896         else if (*data == '\"')
897         {
898                 // quoted string
899                 for (data++;*data != '\"';data++)
900                 {
901                         if (!*data || len >= (int)sizeof(com_token) - 1)
902                         {
903                                 com_token[0] = 0;
904                                 *datapointer = NULL;
905                                 return false;
906                         }
907                         // allow escaped " case
908                         if (*data == '\\' && data[1] == '\"')
909                                 data++;
910                         com_token[len++] = *data;
911                 }
912                 com_token[len] = 0;
913                 *datapointer = data+1;
914         }
915         else
916         {
917                 // regular word
918                 for (;*data > ' ';data++)
919                 {
920                         if (len >= (int)sizeof(com_token) - 1)
921                         {
922                                 com_token[0] = 0;
923                                 *datapointer = NULL;
924                                 return false;
925                         }
926                         com_token[len++] = *data;
927                 }
928                 com_token[len] = 0;
929                 *datapointer = data;
930         }
931
932         return true;
933 }
934
935
936 /*
937 ================
938 COM_CheckParm
939
940 Returns the position (1 to argc-1) in the program's argument list
941 where the given parameter apears, or 0 if not present
942 ================
943 */
944 int COM_CheckParm (const char *parm)
945 {
946         int i;
947
948         for (i=1 ; i<com_argc ; i++)
949         {
950                 if (!com_argv[i])
951                         continue;               // NEXTSTEP sometimes clears appkit vars.
952                 if (!strcmp (parm,com_argv[i]))
953                         return i;
954         }
955
956         return 0;
957 }
958
959 //===========================================================================
960
961 // Game mods
962
963 typedef struct gamemode_info_s
964 {
965         const char* prog_name;
966         const char* cmdline;
967         const char* gamename;
968         const char* gamedirname1;
969         const char* gamedirname2;
970         const char* gamescreenshotname;
971         const char* gameuserdirname;
972 } gamemode_info_t;
973
974 static const gamemode_info_t gamemode_info [] =
975 {// prog_name           cmdline                 gamename                                gamedirname     gamescreenshotname
976
977 // GAME_NORMAL
978 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
979 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
980 // GAME_HIPNOTIC
981 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
982 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
983 // GAME_ROGUE
984 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
985 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
986 // GAME_NEHAHRA
987 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
988 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
989 // GAME_NEXUIZ
990 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
991 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
992 // GAME_TRANSFUSION
993 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
994 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
995 // GAME_GOODVSBAD2
996 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
997 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
998 // GAME_TEU
999 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1000 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
1001 // GAME_BATTLEMECH
1002 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1003 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
1004 // GAME_ZYMOTIC
1005 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1006 { "zymotic",            "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
1007 // GAME_SETHERAL
1008 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1009 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
1010 // GAME_SOM
1011 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1012 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
1013 // GAME_TENEBRAE
1014 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1015 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
1016 // GAME_NEOTERIC
1017 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1018 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
1019 // GAME_OPENQUARTZ
1020 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1021 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
1022 // GAME_PRYDON
1023 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1024 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
1025 // GAME_NETHERWORLD
1026 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master
1027 { "netherworld",        "-netherworld", "Netherworld: Dark Master",     "id1",          "netherworld",  "nw",                   "darkplaces" },
1028 // GAME_THEHUNTED
1029 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1030 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
1031 // GAME_DEFEATINDETAIL2
1032 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1033 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
1034 // GAME_DARSANA
1035 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1036 { "darsana",            "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
1037 // GAME_CONTAGIONTHEORY
1038 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1039 { "contagiontheory",            "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
1040 };
1041
1042 void COM_InitGameType (void)
1043 {
1044         char name [MAX_OSPATH];
1045         unsigned int i;
1046
1047         FS_StripExtension (com_argv[0], name, sizeof (name));
1048         COM_ToLowerString (name, name, sizeof (name));
1049
1050         // Check the binary name; default to GAME_NORMAL (0)
1051         gamemode = GAME_NORMAL;
1052         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1053                 if (strstr (name, gamemode_info[i].prog_name))
1054                 {
1055                         gamemode = (gamemode_t)i;
1056                         break;
1057                 }
1058
1059         // Look for a command-line option
1060         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1061                 if (COM_CheckParm (gamemode_info[i].cmdline))
1062                 {
1063                         gamemode = (gamemode_t)i;
1064                         break;
1065                 }
1066
1067         gamename = gamemode_info[gamemode].gamename;
1068         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1069         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1070         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1071         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1072 }
1073
1074
1075 /*
1076 ================
1077 COM_Init
1078 ================
1079 */
1080 void COM_Init_Commands (void)
1081 {
1082         int i, j, n;
1083         char com_cmdline[MAX_INPUTLINE];
1084
1085         Cvar_RegisterVariable (&registered);
1086         Cvar_RegisterVariable (&cmdline);
1087
1088         // reconstitute the command line for the cmdline externally visible cvar
1089         n = 0;
1090         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1091         {
1092                 i = 0;
1093                 if (strstr(com_argv[j], " "))
1094                 {
1095                         // arg contains whitespace, store quotes around it
1096                         com_cmdline[n++] = '\"';
1097                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1098                                 com_cmdline[n++] = com_argv[j][i++];
1099                         com_cmdline[n++] = '\"';
1100                 }
1101                 else
1102                 {
1103                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1104                                 com_cmdline[n++] = com_argv[j][i++];
1105                 }
1106                 if (n < ((int)sizeof(com_cmdline) - 1))
1107                         com_cmdline[n++] = ' ';
1108                 else
1109                         break;
1110         }
1111         com_cmdline[n] = 0;
1112         Cvar_Set ("cmdline", com_cmdline);
1113 }
1114
1115 /*
1116 ============
1117 va
1118
1119 does a varargs printf into a temp buffer, so I don't need to have
1120 varargs versions of all text functions.
1121 FIXME: make this buffer size safe someday
1122 ============
1123 */
1124 char *va(const char *format, ...)
1125 {
1126         va_list argptr;
1127         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1128         static char string[8][1024], *s;
1129         static int stringindex = 0;
1130
1131         s = string[stringindex];
1132         stringindex = (stringindex + 1) & 7;
1133         va_start (argptr, format);
1134         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1135         va_end (argptr);
1136
1137         return s;
1138 }
1139
1140
1141 //======================================
1142
1143 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1144
1145 #undef snprintf
1146 #undef vsnprintf
1147
1148 #ifdef WIN32
1149 # define snprintf _snprintf
1150 # define vsnprintf _vsnprintf
1151 #endif
1152
1153
1154 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1155 {
1156         va_list args;
1157         int result;
1158
1159         va_start (args, format);
1160         result = dpvsnprintf (buffer, buffersize, format, args);
1161         va_end (args);
1162
1163         return result;
1164 }
1165
1166
1167 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1168 {
1169         int result;
1170
1171         result = vsnprintf (buffer, buffersize, format, args);
1172         if (result < 0 || (size_t)result >= buffersize)
1173         {
1174                 buffer[buffersize - 1] = '\0';
1175                 return -1;
1176         }
1177
1178         return result;
1179 }
1180
1181
1182 //======================================
1183
1184 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1185 {
1186         if (size_out == 0)
1187                 return;
1188
1189         while (*in && size_out > 1)
1190         {
1191                 if (*in >= 'A' && *in <= 'Z')
1192                         *out++ = *in++ + 'a' - 'A';
1193                 else
1194                         *out++ = *in++;
1195                 size_out--;
1196         }
1197         *out = '\0';
1198 }
1199
1200 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1201 {
1202         if (size_out == 0)
1203                 return;
1204
1205         while (*in && size_out > 1)
1206         {
1207                 if (*in >= 'a' && *in <= 'z')
1208                         *out++ = *in++ + 'A' - 'a';
1209                 else
1210                         *out++ = *in++;
1211                 size_out--;
1212         }
1213         *out = '\0';
1214 }
1215
1216 int COM_StringBeginsWith(const char *s, const char *match)
1217 {
1218         for (;*s && *match;s++, match++)
1219                 if (*s != *match)
1220                         return false;
1221         return true;
1222 }
1223
1224 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1225 {
1226         int argc, commentprefixlength;
1227         char *tokenbufend;
1228         const char *l;
1229         argc = 0;
1230         tokenbufend = tokenbuf + tokenbufsize;
1231         l = *text;
1232         commentprefixlength = 0;
1233         if (commentprefix)
1234                 commentprefixlength = (int)strlen(commentprefix);
1235         while (*l && *l != '\n' && *l != '\r')
1236         {
1237                 if (*l > ' ')
1238                 {
1239                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1240                         {
1241                                 while (*l && *l != '\n' && *l != '\r')
1242                                         l++;
1243                                 break;
1244                         }
1245                         if (argc >= maxargc)
1246                                 return -1;
1247                         argv[argc++] = tokenbuf;
1248                         if (*l == '"')
1249                         {
1250                                 l++;
1251                                 while (*l && *l != '"')
1252                                 {
1253                                         if (tokenbuf >= tokenbufend)
1254                                                 return -1;
1255                                         *tokenbuf++ = *l++;
1256                                 }
1257                                 if (*l == '"')
1258                                         l++;
1259                         }
1260                         else
1261                         {
1262                                 while (*l > ' ')
1263                                 {
1264                                         if (tokenbuf >= tokenbufend)
1265                                                 return -1;
1266                                         *tokenbuf++ = *l++;
1267                                 }
1268                         }
1269                         if (tokenbuf >= tokenbufend)
1270                                 return -1;
1271                         *tokenbuf++ = 0;
1272                 }
1273                 else
1274                         l++;
1275         }
1276         // line endings:
1277         // UNIX: \n
1278         // Mac: \r
1279         // Windows: \r\n
1280         if (*l == '\r')
1281                 l++;
1282         if (*l == '\n')
1283                 l++;
1284         *text = l;
1285         return argc;
1286 }
1287
1288 /*
1289 ============
1290 COM_StringLengthNoColors
1291
1292 calculates the visible width of a color coded string.
1293
1294 *valid is filled with TRUE if the string is a valid colored string (that is, if
1295 it does not end with an unfinished color code). If it gets filled with FALSE, a
1296 fix would be adding a STRING_COLOR_TAG at the end of the string.
1297
1298 valid can be set to NULL if the caller doesn't care.
1299
1300 For size_s, specify the maximum number of characters from s to use, or 0 to use
1301 all characters until the zero terminator.
1302 ============
1303 */
1304 size_t
1305 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1306 {
1307         const char *end = size_s ? (s + size_s) : NULL;
1308         size_t len = 0;
1309         for(;;)
1310         {
1311                 switch((s == end) ? 0 : *s)
1312                 {
1313                         case 0:
1314                                 if(valid)
1315                                         *valid = TRUE;
1316                                 return len;
1317                         case STRING_COLOR_TAG:
1318                                 ++s;
1319                                 switch((s == end) ? 0 : *s)
1320                                 {
1321                                         case 0: // ends with unfinished color code!
1322                                                 ++len;
1323                                                 if(valid)
1324                                                         *valid = FALSE;
1325                                                 return len;
1326                                         case STRING_COLOR_TAG: // escaped ^
1327                                                 ++len;
1328                                                 break;
1329                                         case '0': case '1': case '2': case '3': case '4':
1330                                         case '5': case '6': case '7': case '8': case '9': // color code
1331                                                 break;
1332                                         default: // not a color code
1333                                                 ++len; // STRING_COLOR_TAG
1334                                                 ++len; // the character
1335                                                 break;
1336                                 }
1337                                 break;
1338                         default:
1339                                 ++len;
1340                                 break;
1341                 }
1342                 ++s;
1343         }
1344         // never get here
1345 }
1346
1347 /*
1348 ============
1349 COM_StringDecolorize
1350
1351 removes color codes from a string.
1352
1353 If escape_carets is true, the resulting string will be safe for printing. If
1354 escape_carets is false, the function will just strip color codes (for logging
1355 for example).
1356
1357 If the output buffer size did not suffice for converting, the function returns
1358 FALSE. Generally, if escape_carets is false, the output buffer needs
1359 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1360 bytes. In any case, the function makes sure that the resulting string is
1361 zero terminated.
1362
1363 For size_in, specify the maximum number of characters from in to use, or 0 to use
1364 all characters until the zero terminator.
1365 ============
1366 */
1367 qboolean
1368 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1369 {
1370 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1371         const char *end = size_in ? (in + size_in) : NULL;
1372         if(size_out < 1)
1373                 return FALSE;
1374         for(;;)
1375         {
1376                 switch((in == end) ? 0 : *in)
1377                 {
1378                         case 0:
1379                                 *out++ = 0;
1380                                 return TRUE;
1381                         case STRING_COLOR_TAG:
1382                                 ++in;
1383                                 switch((in == end) ? 0 : *in)
1384                                 {
1385                                         case 0: // ends with unfinished color code!
1386                                                 APPEND(STRING_COLOR_TAG);
1387                                                 // finish the code by appending another caret when escaping
1388                                                 if(escape_carets)
1389                                                         APPEND(STRING_COLOR_TAG);
1390                                                 *out++ = 0;
1391                                                 return TRUE;
1392                                         case STRING_COLOR_TAG: // escaped ^
1393                                                 APPEND(STRING_COLOR_TAG);
1394                                                 // append a ^ twice when escaping
1395                                                 if(escape_carets)
1396                                                         APPEND(STRING_COLOR_TAG);
1397                                                 break;
1398                                         case '0': case '1': case '2': case '3': case '4':
1399                                         case '5': case '6': case '7': case '8': case '9': // color code
1400                                                 break;
1401                                         default: // not a color code
1402                                                 APPEND(STRING_COLOR_TAG);
1403                                                 APPEND(*in);
1404                                                 break;
1405                                 }
1406                                 break;
1407                         default:
1408                                 APPEND(*in);
1409                                 break;
1410                 }
1411                 ++in;
1412         }
1413         // never get here
1414 #undef APPEND
1415 }
1416
1417 // written by Elric, thanks Elric!
1418 char *SearchInfostring(const char *infostring, const char *key)
1419 {
1420         static char value [MAX_INPUTLINE];
1421         char crt_key [MAX_INPUTLINE];
1422         size_t value_ind, key_ind;
1423         char c;
1424
1425         if (*infostring++ != '\\')
1426                 return NULL;
1427
1428         value_ind = 0;
1429         for (;;)
1430         {
1431                 key_ind = 0;
1432
1433                 // Get the key name
1434                 for (;;)
1435                 {
1436                         c = *infostring++;
1437
1438                         if (c == '\0')
1439                                 return NULL;
1440                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1441                         {
1442                                 crt_key[key_ind] = '\0';
1443                                 break;
1444                         }
1445
1446                         crt_key[key_ind++] = c;
1447                 }
1448
1449                 // If it's the key we are looking for, save it in "value"
1450                 if (!strcmp(crt_key, key))
1451                 {
1452                         for (;;)
1453                         {
1454                                 c = *infostring++;
1455
1456                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1457                                 {
1458                                         value[value_ind] = '\0';
1459                                         return value;
1460                                 }
1461
1462                                 value[value_ind++] = c;
1463                         }
1464                 }
1465
1466                 // Else, skip the value
1467                 for (;;)
1468                 {
1469                         c = *infostring++;
1470
1471                         if (c == '\0')
1472                                 return NULL;
1473                         if (c == '\\')
1474                                 break;
1475                 }
1476         }
1477 }
1478
1479 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1480 {
1481         int pos = 0, j;
1482         size_t keylength;
1483         if (!key)
1484                 key = "";
1485         if (!value)
1486                 value = "";
1487         keylength = strlen(key);
1488         if (valuelength < 1 || !value)
1489         {
1490                 Con_Printf("InfoString_GetValue: no room in value\n");
1491                 return;
1492         }
1493         value[0] = 0;
1494         if (strchr(key, '\\'))
1495         {
1496                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1497                 return;
1498         }
1499         if (strchr(key, '\"'))
1500         {
1501                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1502                 return;
1503         }
1504         if (!key[0])
1505         {
1506                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1507                 return;
1508         }
1509         while (buffer[pos] == '\\')
1510         {
1511                 if (!memcmp(buffer + pos+1, key, keylength))
1512                 {
1513                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1514                         pos++;
1515                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1516                                 value[j] = buffer[pos+j];
1517                         value[j] = 0;
1518                         return;
1519                 }
1520                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1521                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1522         }
1523         // if we reach this point the key was not found
1524 }
1525
1526 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1527 {
1528         int pos = 0, pos2;
1529         size_t keylength;
1530         if (!key)
1531                 key = "";
1532         if (!value)
1533                 value = "";
1534         keylength = strlen(key);
1535         if (strchr(key, '\\') || strchr(value, '\\'))
1536         {
1537                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1538                 return;
1539         }
1540         if (strchr(key, '\"') || strchr(value, '\"'))
1541         {
1542                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1543                 return;
1544         }
1545         if (!key[0])
1546         {
1547                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1548                 return;
1549         }
1550         while (buffer[pos] == '\\')
1551         {
1552                 if (!memcmp(buffer + pos+1, key, keylength))
1553                         break;
1554                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1555                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1556         }
1557         // if we found the key, find the end of it because we will be replacing it
1558         pos2 = pos;
1559         if (buffer[pos] == '\\')
1560         {
1561                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1562                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1563         }
1564         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1565         {
1566                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1567                 return;
1568         }
1569         if (value && value[0])
1570         {
1571                 // set the key/value and append the remaining text
1572                 char tempbuffer[4096];
1573                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1574                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
1575         }
1576         else
1577         {
1578                 // just remove the key from the text
1579                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1580         }
1581 }
1582
1583 void InfoString_Print(char *buffer)
1584 {
1585         int i;
1586         char key[2048];
1587         char value[2048];
1588         while (*buffer)
1589         {
1590                 if (*buffer != '\\')
1591                 {
1592                         Con_Printf("InfoString_Print: corrupt string\n");
1593                         return;
1594                 }
1595                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1596                         if (i < (int)sizeof(key)-1)
1597                                 key[i++] = *buffer;
1598                 key[i] = 0;
1599                 if (*buffer != '\\')
1600                 {
1601                         Con_Printf("InfoString_Print: corrupt string\n");
1602                         return;
1603                 }
1604                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1605                         if (i < (int)sizeof(value)-1)
1606                                 value[i++] = *buffer;
1607                 value[i] = 0;
1608                 // empty value is an error case
1609                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1610         }
1611 }
1612
1613 //========================================================
1614 // strlcat and strlcpy, from OpenBSD
1615
1616 /*
1617  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1618  *
1619  * Permission to use, copy, modify, and distribute this software for any
1620  * purpose with or without fee is hereby granted, provided that the above
1621  * copyright notice and this permission notice appear in all copies.
1622  *
1623  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1624  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1625  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1626  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1627  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1628  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1629  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1630  */
1631
1632 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1633 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1634
1635
1636 #ifndef HAVE_STRLCAT
1637 size_t
1638 strlcat(char *dst, const char *src, size_t siz)
1639 {
1640         register char *d = dst;
1641         register const char *s = src;
1642         register size_t n = siz;
1643         size_t dlen;
1644
1645         /* Find the end of dst and adjust bytes left but don't go past end */
1646         while (n-- != 0 && *d != '\0')
1647                 d++;
1648         dlen = d - dst;
1649         n = siz - dlen;
1650
1651         if (n == 0)
1652                 return(dlen + strlen(s));
1653         while (*s != '\0') {
1654                 if (n != 1) {
1655                         *d++ = *s;
1656                         n--;
1657                 }
1658                 s++;
1659         }
1660         *d = '\0';
1661
1662         return(dlen + (s - src));       /* count does not include NUL */
1663 }
1664 #endif  // #ifndef HAVE_STRLCAT
1665
1666
1667 #ifndef HAVE_STRLCPY
1668 size_t
1669 strlcpy(char *dst, const char *src, size_t siz)
1670 {
1671         register char *d = dst;
1672         register const char *s = src;
1673         register size_t n = siz;
1674
1675         /* Copy as many bytes as will fit */
1676         if (n != 0 && --n != 0) {
1677                 do {
1678                         if ((*d++ = *s++) == 0)
1679                                 break;
1680                 } while (--n != 0);
1681         }
1682
1683         /* Not enough room in dst, add NUL and traverse rest of src */
1684         if (n == 0) {
1685                 if (siz != 0)
1686                         *d = '\0';              /* NUL-terminate dst */
1687                 while (*s++)
1688                         ;
1689         }
1690
1691         return(s - src - 1);    /* count does not include NUL */
1692 }
1693
1694 #endif  // #ifndef HAVE_STRLCPY