]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
implemented better \ parsing, matching stock Quake better
[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 "quakedef.h"
23
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #ifndef WIN32
27 #include <unistd.h>
28 #endif
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 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
177 {
178         unsigned short crc = CRC_INIT_VALUE;
179         while (size--)
180                 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
181         return crc ^ CRC_XOR_VALUE;
182 }
183
184 // QuakeWorld
185 static unsigned char chktbl[1024 + 4] =
186 {
187         0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
188         0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
189         0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
190         0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
191         0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
192         0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
193         0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
194         0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
195         0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
196         0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
197         0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
198         0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
199         0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
200         0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
201         0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
202         0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
203         0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
204         0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
205         0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
206         0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
207         0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
208         0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
209         0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
210         0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
211         0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
212         0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
213         0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
214         0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
215         0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
216         0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
217         0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
218         0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
219
220         // map checksum goes here
221         0x00,0x00,0x00,0x00
222 };
223
224 // QuakeWorld
225 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
226 {
227         unsigned char *p;
228         unsigned char chkb[60 + 4];
229
230         p = chktbl + (sequence % (sizeof(chktbl) - 8));
231
232         if (length > 60)
233                 length = 60;
234         memcpy(chkb, base, length);
235
236         chkb[length] = (sequence & 0xff) ^ p[0];
237         chkb[length+1] = p[1];
238         chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
239         chkb[length+3] = p[3];
240
241         return CRC_Block(chkb, length + 4) & 0xff;
242 }
243
244 /*
245 ==============================================================================
246
247                         MESSAGE IO FUNCTIONS
248
249 Handles byte ordering and avoids alignment errors
250 ==============================================================================
251 */
252
253 //
254 // writing functions
255 //
256
257 void MSG_WriteChar (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_WriteByte (sizebuf_t *sb, int c)
266 {
267         unsigned char    *buf;
268
269         buf = SZ_GetSpace (sb, 1);
270         buf[0] = c;
271 }
272
273 void MSG_WriteShort (sizebuf_t *sb, int c)
274 {
275         unsigned char    *buf;
276
277         buf = SZ_GetSpace (sb, 2);
278         buf[0] = c&0xff;
279         buf[1] = c>>8;
280 }
281
282 void MSG_WriteLong (sizebuf_t *sb, int c)
283 {
284         unsigned char    *buf;
285
286         buf = SZ_GetSpace (sb, 4);
287         buf[0] = c&0xff;
288         buf[1] = (c>>8)&0xff;
289         buf[2] = (c>>16)&0xff;
290         buf[3] = c>>24;
291 }
292
293 void MSG_WriteFloat (sizebuf_t *sb, float f)
294 {
295         union
296         {
297                 float   f;
298                 int     l;
299         } dat;
300
301
302         dat.f = f;
303         dat.l = LittleLong (dat.l);
304
305         SZ_Write (sb, (unsigned char *)&dat.l, 4);
306 }
307
308 void MSG_WriteString (sizebuf_t *sb, const char *s)
309 {
310         if (!s || !*s)
311                 MSG_WriteChar (sb, 0);
312         else
313                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
314 }
315
316 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
317 {
318         if (s && *s)
319                 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
320 }
321
322 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
323 {
324         if (f >= 0)
325                 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
326         else
327                 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
328 }
329
330 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
331 {
332         if (f >= 0)
333                 MSG_WriteShort (sb, (int)(f + 0.5));
334         else
335                 MSG_WriteShort (sb, (int)(f - 0.5));
336 }
337
338 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
339 {
340         MSG_WriteFloat (sb, f);
341 }
342
343 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
344 {
345         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
346                 MSG_WriteCoord13i (sb, f);
347         else if (protocol == PROTOCOL_DARKPLACES1)
348                 MSG_WriteCoord32f (sb, f);
349         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
350                 MSG_WriteCoord16i (sb, f);
351         else
352                 MSG_WriteCoord32f (sb, f);
353 }
354
355 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
356 {
357         MSG_WriteCoord (sb, v[0], protocol);
358         MSG_WriteCoord (sb, v[1], protocol);
359         MSG_WriteCoord (sb, v[2], protocol);
360 }
361
362 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
363 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
364 {
365         if (f >= 0)
366                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
367         else
368                 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
369 }
370
371 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
372 {
373         if (f >= 0)
374                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
375         else
376                 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
377 }
378
379 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
380 {
381         MSG_WriteFloat (sb, f);
382 }
383
384 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
385 {
386         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)
387                 MSG_WriteAngle8i (sb, f);
388         else
389                 MSG_WriteAngle16i (sb, f);
390 }
391
392 //
393 // reading functions
394 //
395 int msg_readcount;
396 qboolean msg_badread;
397
398 void MSG_BeginReading (void)
399 {
400         msg_readcount = 0;
401         msg_badread = false;
402 }
403
404 int MSG_ReadLittleShort (void)
405 {
406         if (msg_readcount+2 > net_message.cursize)
407         {
408                 msg_badread = true;
409                 return -1;
410         }
411         msg_readcount += 2;
412         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
413 }
414
415 int MSG_ReadBigShort (void)
416 {
417         if (msg_readcount+2 > net_message.cursize)
418         {
419                 msg_badread = true;
420                 return -1;
421         }
422         msg_readcount += 2;
423         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
424 }
425
426 int MSG_ReadLittleLong (void)
427 {
428         if (msg_readcount+4 > net_message.cursize)
429         {
430                 msg_badread = true;
431                 return -1;
432         }
433         msg_readcount += 4;
434         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);
435 }
436
437 int MSG_ReadBigLong (void)
438 {
439         if (msg_readcount+4 > net_message.cursize)
440         {
441                 msg_badread = true;
442                 return -1;
443         }
444         msg_readcount += 4;
445         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];
446 }
447
448 float MSG_ReadLittleFloat (void)
449 {
450         union
451         {
452                 float f;
453                 int l;
454         } dat;
455         if (msg_readcount+4 > net_message.cursize)
456         {
457                 msg_badread = true;
458                 return -1;
459         }
460         msg_readcount += 4;
461         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);
462         return dat.f;
463 }
464
465 float MSG_ReadBigFloat (void)
466 {
467         union
468         {
469                 float f;
470                 int l;
471         } dat;
472         if (msg_readcount+4 > net_message.cursize)
473         {
474                 msg_badread = true;
475                 return -1;
476         }
477         msg_readcount += 4;
478         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];
479         return dat.f;
480 }
481
482 char *MSG_ReadString (void)
483 {
484         static char string[MAX_INPUTLINE];
485         int l,c;
486         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
487                 string[l] = c;
488         string[l] = 0;
489         return string;
490 }
491
492 int MSG_ReadBytes (int numbytes, unsigned char *out)
493 {
494         int l, c;
495         for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
496                 out[l] = c;
497         return l;
498 }
499
500 float MSG_ReadCoord13i (void)
501 {
502         return MSG_ReadLittleShort() * (1.0/8.0);
503 }
504
505 float MSG_ReadCoord16i (void)
506 {
507         return (signed short) MSG_ReadLittleShort();
508 }
509
510 float MSG_ReadCoord32f (void)
511 {
512         return MSG_ReadLittleFloat();
513 }
514
515 float MSG_ReadCoord (protocolversion_t protocol)
516 {
517         if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
518                 return MSG_ReadCoord13i();
519         else if (protocol == PROTOCOL_DARKPLACES1)
520                 return MSG_ReadCoord32f();
521         else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
522                 return MSG_ReadCoord16i();
523         else
524                 return MSG_ReadCoord32f();
525 }
526
527 void MSG_ReadVector (float *v, protocolversion_t protocol)
528 {
529         v[0] = MSG_ReadCoord(protocol);
530         v[1] = MSG_ReadCoord(protocol);
531         v[2] = MSG_ReadCoord(protocol);
532 }
533
534 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
535 float MSG_ReadAngle8i (void)
536 {
537         return (signed char) MSG_ReadByte () * (360.0/256.0);
538 }
539
540 float MSG_ReadAngle16i (void)
541 {
542         return (signed short)MSG_ReadShort () * (360.0/65536.0);
543 }
544
545 float MSG_ReadAngle32f (void)
546 {
547         return MSG_ReadFloat ();
548 }
549
550 float MSG_ReadAngle (protocolversion_t protocol)
551 {
552         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)
553                 return MSG_ReadAngle8i ();
554         else
555                 return MSG_ReadAngle16i ();
556 }
557
558
559 //===========================================================================
560
561 void SZ_Clear (sizebuf_t *buf)
562 {
563         buf->cursize = 0;
564 }
565
566 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
567 {
568         unsigned char *data;
569
570         if (buf->cursize + length > buf->maxsize)
571         {
572                 if (!buf->allowoverflow)
573                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
574
575                 if (length > buf->maxsize)
576                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
577
578                 buf->overflowed = true;
579                 Con_Print("SZ_GetSpace: overflow\n");
580                 SZ_Clear (buf);
581         }
582
583         data = buf->data + buf->cursize;
584         buf->cursize += length;
585
586         return data;
587 }
588
589 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
590 {
591         memcpy (SZ_GetSpace(buf,length),data,length);
592 }
593
594 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
595 // attention, it has been eradicated from here, its only (former) use in
596 // all of darkplaces.
597
598 static char *hexchar = "0123456789ABCDEF";
599 void Com_HexDumpToConsole(const unsigned char *data, int size)
600 {
601         int i, j, n;
602         char text[1024];
603         char *cur, *flushpointer;
604         const unsigned char *d;
605         cur = text;
606         flushpointer = text + 512;
607         for (i = 0;i < size;)
608         {
609                 n = 16;
610                 if (n > size - i)
611                         n = size - i;
612                 d = data + i;
613                 // print offset
614                 *cur++ = hexchar[(i >> 12) & 15];
615                 *cur++ = hexchar[(i >>  8) & 15];
616                 *cur++ = hexchar[(i >>  4) & 15];
617                 *cur++ = hexchar[(i >>  0) & 15];
618                 *cur++ = ':';
619                 // print hex
620                 for (j = 0;j < 16;j++)
621                 {
622                         if (j < n)
623                         {
624                                 *cur++ = hexchar[(d[j] >> 4) & 15];
625                                 *cur++ = hexchar[(d[j] >> 0) & 15];
626                         }
627                         else
628                         {
629                                 *cur++ = ' ';
630                                 *cur++ = ' ';
631                         }
632                         if ((j & 3) == 3)
633                                 *cur++ = ' ';
634                 }
635                 // print text
636                 for (j = 0;j < 16;j++)
637                 {
638                         if (j < n)
639                         {
640                                 // color change prefix character has to be treated specially
641                                 if (d[j] == STRING_COLOR_TAG)
642                                 {
643                                         *cur++ = STRING_COLOR_TAG;
644                                         *cur++ = STRING_COLOR_TAG;
645                                 }
646                                 else if (d[j] >= ' ')
647                                         *cur++ = d[j];
648                                 else
649                                         *cur++ = '.';
650                         }
651                         else
652                                 *cur++ = ' ';
653                 }
654                 *cur++ = '\n';
655                 i += n;
656                 if (cur >= flushpointer || i >= size)
657                 {
658                         *cur++ = 0;
659                         Con_Print(text);
660                         cur = text;
661                 }
662         }
663 }
664
665 void SZ_HexDumpToConsole(const sizebuf_t *buf)
666 {
667         Com_HexDumpToConsole(buf->data, buf->cursize);
668 }
669
670
671 //============================================================================
672
673 /*
674 ==============
675 COM_Wordwrap
676
677 Word wraps a string. The wordWidth function is guaranteed to be called exactly
678 once for each word in the string, so it may be stateful, no idea what that
679 would be good for any more. At the beginning of the string, it will be called
680 for the char 0 to initialize a clean state, and then once with the string " "
681 (a space) so the routine knows how long a space is.
682
683 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
684
685 The sum of the return values of the processLine function will be returned.
686 ==============
687 */
688 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
689 {
690         // Logic is as follows:
691         //
692         // For each word or whitespace:
693         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
694         //   Space found? Always add it to the current line, no matter if it fits.
695         //   Word found? Check if current line + current word fits.
696         //     If it fits, append it. Continue.
697         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
698
699         qboolean isContinuation = false;
700         float spaceWidth;
701         const char *startOfLine = string;
702         const char *cursor = string;
703         const char *end = string + length;
704         float spaceUsedInLine = 0;
705         float spaceUsedForWord;
706         int result = 0;
707         size_t wordLen;
708         size_t dummy;
709
710         dummy = 0;
711         wordWidth(passthroughCW, NULL, &dummy, -1);
712         dummy = 1;
713         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
714
715         for(;;)
716         {
717                 char ch = (cursor < end) ? *cursor : 0;
718                 switch(ch)
719                 {
720                         case 0: // end of string
721                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
722                                 isContinuation = false;
723                                 goto out;
724                                 break;
725                         case '\n': // end of line
726                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
727                                 isContinuation = false;
728                                 ++cursor;
729                                 startOfLine = cursor;
730                                 break;
731                         case ' ': // space
732                                 ++cursor;
733                                 spaceUsedInLine += spaceWidth;
734                                 break;
735                         default: // word
736                                 wordLen = 1;
737                                 while(cursor + wordLen < end)
738                                 {
739                                         switch(cursor[wordLen])
740                                         {
741                                                 case 0:
742                                                 case '\n':
743                                                 case ' ':
744                                                         goto out_inner;
745                                                 default:
746                                                         ++wordLen;
747                                                         break;
748                                         }
749                                 }
750                                 out_inner:
751                                 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
752                                 if(wordLen < 1)
753                                 {
754                                         wordLen = 1;
755                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
756                                 }
757                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
758                                 {
759                                         // we can simply append it
760                                         cursor += wordLen;
761                                         spaceUsedInLine += spaceUsedForWord;
762                                 }
763                                 else
764                                 {
765                                         // output current line
766                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
767                                         isContinuation = true;
768                                         startOfLine = cursor;
769                                         cursor += wordLen;
770                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
771                                 }
772                 }
773         }
774         out:
775
776         return result;
777
778 /*
779         qboolean isContinuation = false;
780         float currentWordSpace = 0;
781         const char *currentWord = 0;
782         float minReserve = 0;
783
784         float spaceUsedInLine = 0;
785         const char *currentLine = 0;
786         const char *currentLineEnd = 0;
787         float currentLineFinalWhitespace = 0;
788         const char *p;
789
790         int result = 0;
791         minReserve = charWidth(passthroughCW, 0);
792         minReserve += charWidth(passthroughCW, ' ');
793
794         if(maxWidth < continuationWidth + minReserve)
795                 maxWidth = continuationWidth + minReserve;
796
797         charWidth(passthroughCW, 0);
798
799         for(p = string; p < string + length; ++p)
800         {
801                 char c = *p;
802                 float w = charWidth(passthroughCW, c);
803
804                 if(!currentWord)
805                 {
806                         currentWord = p;
807                         currentWordSpace = 0;
808                 }
809
810                 if(!currentLine)
811                 {
812                         currentLine = p;
813                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
814                         currentLineEnd = 0;
815                 }
816
817                 if(c == ' ')
818                 {
819                         // 1. I can add the word AND a space - then just append it.
820                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
821                         {
822                                 currentLineEnd = p; // note: space not included here
823                                 currentLineFinalWhitespace = w;
824                                 spaceUsedInLine += currentWordSpace + w;
825                         }
826                         // 2. I can just add the word - then append it, output current line and go to next one.
827                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
828                         {
829                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
830                                 currentLine = 0;
831                                 isContinuation = true;
832                         }
833                         // 3. Otherwise, output current line and go to next one, where I can add the word.
834                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
835                         {
836                                 if(currentLineEnd)
837                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
838                                 currentLine = currentWord;
839                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
840                                 currentLineEnd = p;
841                                 currentLineFinalWhitespace = w;
842                                 isContinuation = true;
843                         }
844                         // 4. We can't even do that? Then output both current and next word as new lines.
845                         else
846                         {
847                                 if(currentLineEnd)
848                                 {
849                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
850                                         isContinuation = true;
851                                 }
852                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
853                                 currentLine = 0;
854                                 isContinuation = true;
855                         }
856                         currentWord = 0;
857                 }
858                 else if(c == '\n')
859                 {
860                         // 1. I can add the word - then do it.
861                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
862                         {
863                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
864                         }
865                         // 2. Otherwise, output current line, next one and make tabula rasa.
866                         else
867                         {
868                                 if(currentLineEnd)
869                                 {
870                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
871                                         isContinuation = true;
872                                 }
873                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
874                         }
875                         currentWord = 0;
876                         currentLine = 0;
877                         isContinuation = false;
878                 }
879                 else
880                 {
881                         currentWordSpace += w;
882                         if(
883                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
884                                 &&
885                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
886                         )
887                         {
888                                 // this word cannot join ANY line...
889                                 // so output the current line...
890                                 if(currentLineEnd)
891                                 {
892                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
893                                         isContinuation = true;
894                                 }
895
896                                 // then this word's beginning...
897                                 if(isContinuation)
898                                 {
899                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
900                                         float pieceWidth = maxWidth - continuationWidth;
901                                         const char *pos = currentWord;
902                                         currentWordSpace = 0;
903
904                                         // reset the char width function to a state where no kerning occurs (start of word)
905                                         charWidth(passthroughCW, ' ');
906                                         while(pos <= p)
907                                         {
908                                                 float w = charWidth(passthroughCW, *pos);
909                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
910                                                 {
911                                                         // print everything until it
912                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
913                                                         // go to here
914                                                         currentWord = pos;
915                                                         currentWordSpace = 0;
916                                                 }
917                                                 currentWordSpace += w;
918                                                 ++pos;
919                                         }
920                                         // now we have a currentWord that fits... set up its next line
921                                         // currentWordSpace has been set
922                                         // currentWord has been set
923                                         spaceUsedInLine = continuationWidth;
924                                         currentLine = currentWord;
925                                         currentLineEnd = 0;
926                                         isContinuation = true;
927                                 }
928                                 else
929                                 {
930                                         // we have a guarantee that it will fix (see if clause)
931                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
932
933                                         // and use the rest of this word as new start of a line
934                                         currentWordSpace = w;
935                                         currentWord = p;
936                                         spaceUsedInLine = continuationWidth;
937                                         currentLine = p;
938                                         currentLineEnd = 0;
939                                         isContinuation = true;
940                                 }
941                         }
942                 }
943         }
944
945         if(!currentWord)
946         {
947                 currentWord = p;
948                 currentWordSpace = 0;
949         }
950
951         if(currentLine) // Same procedure as \n
952         {
953                 // Can I append the current word?
954                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
955                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
956                 else
957                 {
958                         if(currentLineEnd)
959                         {
960                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
961                                 isContinuation = true;
962                         }
963                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
964                 }
965         }
966
967         return result;
968 */
969 }
970
971 /*
972 ==============
973 COM_ParseToken_Simple
974
975 Parse a token out of a string
976 ==============
977 */
978 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
979 {
980         int len;
981         int c;
982         const char *data = *datapointer;
983
984         len = 0;
985         com_token[0] = 0;
986
987         if (!data)
988         {
989                 *datapointer = NULL;
990                 return false;
991         }
992
993 // skip whitespace
994 skipwhite:
995         // line endings:
996         // UNIX: \n
997         // Mac: \r
998         // Windows: \r\n
999         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1000         {
1001                 if (*data == 0)
1002                 {
1003                         // end of file
1004                         *datapointer = NULL;
1005                         return false;
1006                 }
1007         }
1008
1009         // handle Windows line ending
1010         if (data[0] == '\r' && data[1] == '\n')
1011                 data++;
1012
1013         if (data[0] == '/' && data[1] == '/')
1014         {
1015                 // comment
1016                 while (*data && *data != '\n' && *data != '\r')
1017                         data++;
1018                 goto skipwhite;
1019         }
1020         else if (data[0] == '/' && data[1] == '*')
1021         {
1022                 // comment
1023                 data++;
1024                 while (*data && (data[0] != '*' || data[1] != '/'))
1025                         data++;
1026                 if (*data)
1027                         data++;
1028                 if (*data)
1029                         data++;
1030                 goto skipwhite;
1031         }
1032         else if (*data == '\"')
1033         {
1034                 // quoted string
1035                 for (data++;*data && *data != '\"';data++)
1036                 {
1037                         c = *data;
1038                         if (*data == '\\' && parsebackslash)
1039                         {
1040                                 data++;
1041                                 c = *data;
1042                                 if (c == 'n')
1043                                         c = '\n';
1044                                 else if (c == 't')
1045                                         c = '\t';
1046                         }
1047                         if (len < (int)sizeof(com_token) - 1)
1048                                 com_token[len++] = c;
1049                 }
1050                 com_token[len] = 0;
1051                 if (*data == '\"')
1052                         data++;
1053                 *datapointer = data;
1054                 return true;
1055         }
1056         else if (*data == '\r')
1057         {
1058                 // translate Mac line ending to UNIX
1059                 com_token[len++] = '\n';data++;
1060                 com_token[len] = 0;
1061                 *datapointer = data;
1062                 return true;
1063         }
1064         else if (*data == '\n')
1065         {
1066                 // single character
1067                 com_token[len++] = *data++;
1068                 com_token[len] = 0;
1069                 *datapointer = data;
1070                 return true;
1071         }
1072         else
1073         {
1074                 // regular word
1075                 for (;*data > ' ';data++)
1076                         if (len < (int)sizeof(com_token) - 1)
1077                                 com_token[len++] = *data;
1078                 com_token[len] = 0;
1079                 *datapointer = data;
1080                 return true;
1081         }
1082 }
1083
1084 /*
1085 ==============
1086 COM_ParseToken_QuakeC
1087
1088 Parse a token out of a string
1089 ==============
1090 */
1091 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1092 {
1093         int len;
1094         int c;
1095         const char *data = *datapointer;
1096
1097         len = 0;
1098         com_token[0] = 0;
1099
1100         if (!data)
1101         {
1102                 *datapointer = NULL;
1103                 return false;
1104         }
1105
1106 // skip whitespace
1107 skipwhite:
1108         // line endings:
1109         // UNIX: \n
1110         // Mac: \r
1111         // Windows: \r\n
1112         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1113         {
1114                 if (*data == 0)
1115                 {
1116                         // end of file
1117                         *datapointer = NULL;
1118                         return false;
1119                 }
1120         }
1121
1122         // handle Windows line ending
1123         if (data[0] == '\r' && data[1] == '\n')
1124                 data++;
1125
1126         if (data[0] == '/' && data[1] == '/')
1127         {
1128                 // comment
1129                 while (*data && *data != '\n' && *data != '\r')
1130                         data++;
1131                 goto skipwhite;
1132         }
1133         else if (data[0] == '/' && data[1] == '*')
1134         {
1135                 // comment
1136                 data++;
1137                 while (*data && (data[0] != '*' || data[1] != '/'))
1138                         data++;
1139                 if (*data)
1140                         data++;
1141                 if (*data)
1142                         data++;
1143                 goto skipwhite;
1144         }
1145         else if (*data == '\"' || *data == '\'')
1146         {
1147                 // quoted string
1148                 char quote = *data;
1149                 for (data++;*data && *data != quote;data++)
1150                 {
1151                         c = *data;
1152                         if (*data == '\\')
1153                         {
1154                                 data++;
1155                                 c = *data;
1156                                 if (c == 'n')
1157                                         c = '\n';
1158                                 else if (c == 't')
1159                                         c = '\t';
1160                         }
1161                         if (len < (int)sizeof(com_token) - 1)
1162                                 com_token[len++] = c;
1163                 }
1164                 com_token[len] = 0;
1165                 if (*data == quote)
1166                         data++;
1167                 *datapointer = data;
1168                 return true;
1169         }
1170         else if (*data == '\r')
1171         {
1172                 // translate Mac line ending to UNIX
1173                 com_token[len++] = '\n';data++;
1174                 com_token[len] = 0;
1175                 *datapointer = data;
1176                 return true;
1177         }
1178         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1179         {
1180                 // single character
1181                 com_token[len++] = *data++;
1182                 com_token[len] = 0;
1183                 *datapointer = data;
1184                 return true;
1185         }
1186         else
1187         {
1188                 // regular word
1189                 for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1190                         if (len < (int)sizeof(com_token) - 1)
1191                                 com_token[len++] = *data;
1192                 com_token[len] = 0;
1193                 *datapointer = data;
1194                 return true;
1195         }
1196 }
1197
1198 /*
1199 ==============
1200 COM_ParseToken_VM_Tokenize
1201
1202 Parse a token out of a string
1203 ==============
1204 */
1205 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1206 {
1207         int len;
1208         int c;
1209         const char *data = *datapointer;
1210
1211         len = 0;
1212         com_token[0] = 0;
1213
1214         if (!data)
1215         {
1216                 *datapointer = NULL;
1217                 return false;
1218         }
1219
1220 // skip whitespace
1221 skipwhite:
1222         // line endings:
1223         // UNIX: \n
1224         // Mac: \r
1225         // Windows: \r\n
1226         for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1227         {
1228                 if (*data == 0)
1229                 {
1230                         // end of file
1231                         *datapointer = NULL;
1232                         return false;
1233                 }
1234         }
1235
1236         // handle Windows line ending
1237         if (data[0] == '\r' && data[1] == '\n')
1238                 data++;
1239
1240         if (data[0] == '/' && data[1] == '/')
1241         {
1242                 // comment
1243                 while (*data && *data != '\n' && *data != '\r')
1244                         data++;
1245                 goto skipwhite;
1246         }
1247         else if (data[0] == '/' && data[1] == '*')
1248         {
1249                 // comment
1250                 data++;
1251                 while (*data && (data[0] != '*' || data[1] != '/'))
1252                         data++;
1253                 if (*data)
1254                         data++;
1255                 if (*data)
1256                         data++;
1257                 goto skipwhite;
1258         }
1259         else if (*data == '\"' || *data == '\'')
1260         {
1261                 char quote = *data;
1262                 // quoted string
1263                 for (data++;*data && *data != quote;data++)
1264                 {
1265                         c = *data;
1266                         if (*data == '\\')
1267                         {
1268                                 data++;
1269                                 c = *data;
1270                                 if (c == 'n')
1271                                         c = '\n';
1272                                 else if (c == 't')
1273                                         c = '\t';
1274                         }
1275                         if (len < (int)sizeof(com_token) - 1)
1276                                 com_token[len++] = c;
1277                 }
1278                 com_token[len] = 0;
1279                 if (*data == quote)
1280                         data++;
1281                 *datapointer = data;
1282                 return true;
1283         }
1284         else if (*data == '\r')
1285         {
1286                 // translate Mac line ending to UNIX
1287                 com_token[len++] = '\n';data++;
1288                 com_token[len] = 0;
1289                 *datapointer = data;
1290                 return true;
1291         }
1292         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1293         {
1294                 // single character
1295                 com_token[len++] = *data++;
1296                 com_token[len] = 0;
1297                 *datapointer = data;
1298                 return true;
1299         }
1300         else
1301         {
1302                 // regular word
1303                 for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1304                         if (len < (int)sizeof(com_token) - 1)
1305                                 com_token[len++] = *data;
1306                 com_token[len] = 0;
1307                 *datapointer = data;
1308                 return true;
1309         }
1310 }
1311
1312 /*
1313 ==============
1314 COM_ParseToken_Console
1315
1316 Parse a token out of a string, behaving like the qwcl console
1317 ==============
1318 */
1319 int COM_ParseToken_Console(const char **datapointer)
1320 {
1321         int len;
1322         const char *data = *datapointer;
1323
1324         len = 0;
1325         com_token[0] = 0;
1326
1327         if (!data)
1328         {
1329                 *datapointer = NULL;
1330                 return false;
1331         }
1332
1333 // skip whitespace
1334 skipwhite:
1335         for (;*data <= ' ';data++)
1336         {
1337                 if (*data == 0)
1338                 {
1339                         // end of file
1340                         *datapointer = NULL;
1341                         return false;
1342                 }
1343         }
1344
1345         if (*data == '/' && data[1] == '/')
1346         {
1347                 // comment
1348                 while (*data && *data != '\n' && *data != '\r')
1349                         data++;
1350                 goto skipwhite;
1351         }
1352         else if (*data == '\"')
1353         {
1354                 // quoted string
1355                 for (data++;*data && *data != '\"';data++)
1356                 {
1357                         // allow escaped " and \ case
1358                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1359                                 data++;
1360                         if (len < (int)sizeof(com_token) - 1)
1361                                 com_token[len++] = *data;
1362                 }
1363                 com_token[len] = 0;
1364                 if (*data == '\"')
1365                         data++;
1366                 *datapointer = data;
1367         }
1368         else
1369         {
1370                 // regular word
1371                 for (;*data > ' ';data++)
1372                         if (len < (int)sizeof(com_token) - 1)
1373                                 com_token[len++] = *data;
1374                 com_token[len] = 0;
1375                 *datapointer = data;
1376         }
1377
1378         return true;
1379 }
1380
1381
1382 /*
1383 ================
1384 COM_CheckParm
1385
1386 Returns the position (1 to argc-1) in the program's argument list
1387 where the given parameter apears, or 0 if not present
1388 ================
1389 */
1390 int COM_CheckParm (const char *parm)
1391 {
1392         int i;
1393
1394         for (i=1 ; i<com_argc ; i++)
1395         {
1396                 if (!com_argv[i])
1397                         continue;               // NEXTSTEP sometimes clears appkit vars.
1398                 if (!strcmp (parm,com_argv[i]))
1399                         return i;
1400         }
1401
1402         return 0;
1403 }
1404
1405 //===========================================================================
1406
1407 // Game mods
1408
1409 typedef struct gamemode_info_s
1410 {
1411         const char* prog_name;
1412         const char* cmdline;
1413         const char* gamename;
1414         const char* gamedirname1;
1415         const char* gamedirname2;
1416         const char* gamescreenshotname;
1417         const char* gameuserdirname;
1418 } gamemode_info_t;
1419
1420 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1421 {// prog_name           cmdline                 gamename                                basegame        modgame                 screenshotprefix        userdir
1422
1423 // GAME_NORMAL
1424 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1425 { "",                           "-quake",               "DarkPlaces-Quake",             "id1",          NULL,                   "dp",                   "darkplaces" },
1426 // GAME_HIPNOTIC
1427 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1428 { "hipnotic",           "-hipnotic",    "Darkplaces-Hipnotic",  "id1",          "hipnotic",             "dp",                   "darkplaces" },
1429 // GAME_ROGUE
1430 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1431 { "rogue",                      "-rogue",               "Darkplaces-Rogue",             "id1",          "rogue",                "dp",                   "darkplaces" },
1432 // GAME_NEHAHRA
1433 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1434 { "nehahra",            "-nehahra",             "DarkPlaces-Nehahra",   "id1",          "nehahra",              "dp",                   "darkplaces" },
1435 // GAME_NEXUIZ
1436 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1437 { "nexuiz",                     "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
1438 // GAME_TRANSFUSION
1439 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1440 { "transfusion",        "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
1441 // GAME_GOODVSBAD2
1442 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1443 { "gvb2",                       "-goodvsbad2",  "GoodVs.Bad2",                  "rts",          NULL,                   "gvb2",                 "gvb2" },
1444 // GAME_TEU
1445 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1446 { "teu",                        "-teu",                 "TheEvilUnleashed",             "baseteu",      NULL,                   "teu",                  "teu" },
1447 // GAME_BATTLEMECH
1448 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1449 { "battlemech",         "-battlemech",  "Battlemech",                   "base",         NULL,                   "battlemech",   "battlemech" },
1450 // GAME_ZYMOTIC
1451 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1452 { "zymotic",            "-zymotic",             "Zymotic",                              "basezym",              NULL,                   "zymotic",              "zymotic" },
1453 // GAME_SETHERAL
1454 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1455 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
1456 // GAME_SOM
1457 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1458 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
1459 // GAME_TENEBRAE
1460 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1461 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
1462 // GAME_NEOTERIC
1463 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1464 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
1465 // GAME_OPENQUARTZ
1466 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1467 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
1468 // GAME_PRYDON
1469 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1470 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
1471 // GAME_DELUXEQUAKE
1472 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1473 { "dq", "-dq",  "Deluxe Quake",         "basedq",               "extradq",              "basedq",               "dq" },
1474 // GAME_THEHUNTED
1475 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1476 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
1477 // GAME_DEFEATINDETAIL2
1478 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1479 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
1480 // GAME_DARSANA
1481 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1482 { "darsana",            "-darsana",     "Darsana",                      "ddata",        NULL,                   "darsana",                      "darsana" },
1483 // GAME_CONTAGIONTHEORY
1484 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1485 { "contagiontheory",            "-contagiontheory",     "Contagion Theory",                     "ctdata",       NULL,                   "ct",                   "contagiontheory" },
1486 // GAME_EDU2P
1487 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1488 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1489 // GAME_BLADEMASTER
1490 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1491 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1492 };
1493
1494 void COM_InitGameType (void)
1495 {
1496         char name [MAX_OSPATH];
1497         unsigned int i;
1498
1499         FS_StripExtension (com_argv[0], name, sizeof (name));
1500         COM_ToLowerString (name, name, sizeof (name));
1501
1502         // Check the binary name; default to GAME_NORMAL (0)
1503         gamemode = GAME_NORMAL;
1504         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1505                 if (strstr (name, gamemode_info[i].prog_name))
1506                 {
1507                         gamemode = (gamemode_t)i;
1508                         break;
1509                 }
1510
1511         // Look for a command-line option
1512         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1513                 if (COM_CheckParm (gamemode_info[i].cmdline))
1514                 {
1515                         gamemode = (gamemode_t)i;
1516                         break;
1517                 }
1518
1519         gamename = gamemode_info[gamemode].gamename;
1520         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1521         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1522         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1523         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1524 }
1525
1526
1527 /*
1528 ================
1529 COM_Init
1530 ================
1531 */
1532 void COM_Init_Commands (void)
1533 {
1534         int i, j, n;
1535         char com_cmdline[MAX_INPUTLINE];
1536
1537         Cvar_RegisterVariable (&registered);
1538         Cvar_RegisterVariable (&cmdline);
1539
1540         // reconstitute the command line for the cmdline externally visible cvar
1541         n = 0;
1542         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1543         {
1544                 i = 0;
1545                 if (strstr(com_argv[j], " "))
1546                 {
1547                         // arg contains whitespace, store quotes around it
1548                         com_cmdline[n++] = '\"';
1549                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1550                                 com_cmdline[n++] = com_argv[j][i++];
1551                         com_cmdline[n++] = '\"';
1552                 }
1553                 else
1554                 {
1555                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1556                                 com_cmdline[n++] = com_argv[j][i++];
1557                 }
1558                 if (n < ((int)sizeof(com_cmdline) - 1))
1559                         com_cmdline[n++] = ' ';
1560                 else
1561                         break;
1562         }
1563         com_cmdline[n] = 0;
1564         Cvar_Set ("cmdline", com_cmdline);
1565 }
1566
1567 /*
1568 ============
1569 va
1570
1571 does a varargs printf into a temp buffer, so I don't need to have
1572 varargs versions of all text functions.
1573 FIXME: make this buffer size safe someday
1574 ============
1575 */
1576 char *va(const char *format, ...)
1577 {
1578         va_list argptr;
1579         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1580         static char string[8][1024], *s;
1581         static int stringindex = 0;
1582
1583         s = string[stringindex];
1584         stringindex = (stringindex + 1) & 7;
1585         va_start (argptr, format);
1586         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1587         va_end (argptr);
1588
1589         return s;
1590 }
1591
1592
1593 //======================================
1594
1595 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1596
1597 #undef snprintf
1598 #undef vsnprintf
1599
1600 #ifdef WIN32
1601 # define snprintf _snprintf
1602 # define vsnprintf _vsnprintf
1603 #endif
1604
1605
1606 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1607 {
1608         va_list args;
1609         int result;
1610
1611         va_start (args, format);
1612         result = dpvsnprintf (buffer, buffersize, format, args);
1613         va_end (args);
1614
1615         return result;
1616 }
1617
1618
1619 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1620 {
1621         int result;
1622
1623         result = vsnprintf (buffer, buffersize, format, args);
1624         if (result < 0 || (size_t)result >= buffersize)
1625         {
1626                 buffer[buffersize - 1] = '\0';
1627                 return -1;
1628         }
1629
1630         return result;
1631 }
1632
1633
1634 //======================================
1635
1636 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1637 {
1638         if (size_out == 0)
1639                 return;
1640
1641         while (*in && size_out > 1)
1642         {
1643                 if (*in >= 'A' && *in <= 'Z')
1644                         *out++ = *in++ + 'a' - 'A';
1645                 else
1646                         *out++ = *in++;
1647                 size_out--;
1648         }
1649         *out = '\0';
1650 }
1651
1652 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1653 {
1654         if (size_out == 0)
1655                 return;
1656
1657         while (*in && size_out > 1)
1658         {
1659                 if (*in >= 'a' && *in <= 'z')
1660                         *out++ = *in++ + 'A' - 'a';
1661                 else
1662                         *out++ = *in++;
1663                 size_out--;
1664         }
1665         *out = '\0';
1666 }
1667
1668 int COM_StringBeginsWith(const char *s, const char *match)
1669 {
1670         for (;*s && *match;s++, match++)
1671                 if (*s != *match)
1672                         return false;
1673         return true;
1674 }
1675
1676 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1677 {
1678         int argc, commentprefixlength;
1679         char *tokenbufend;
1680         const char *l;
1681         argc = 0;
1682         tokenbufend = tokenbuf + tokenbufsize;
1683         l = *text;
1684         commentprefixlength = 0;
1685         if (commentprefix)
1686                 commentprefixlength = (int)strlen(commentprefix);
1687         while (*l && *l != '\n' && *l != '\r')
1688         {
1689                 if (*l > ' ')
1690                 {
1691                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1692                         {
1693                                 while (*l && *l != '\n' && *l != '\r')
1694                                         l++;
1695                                 break;
1696                         }
1697                         if (argc >= maxargc)
1698                                 return -1;
1699                         argv[argc++] = tokenbuf;
1700                         if (*l == '"')
1701                         {
1702                                 l++;
1703                                 while (*l && *l != '"')
1704                                 {
1705                                         if (tokenbuf >= tokenbufend)
1706                                                 return -1;
1707                                         *tokenbuf++ = *l++;
1708                                 }
1709                                 if (*l == '"')
1710                                         l++;
1711                         }
1712                         else
1713                         {
1714                                 while (*l > ' ')
1715                                 {
1716                                         if (tokenbuf >= tokenbufend)
1717                                                 return -1;
1718                                         *tokenbuf++ = *l++;
1719                                 }
1720                         }
1721                         if (tokenbuf >= tokenbufend)
1722                                 return -1;
1723                         *tokenbuf++ = 0;
1724                 }
1725                 else
1726                         l++;
1727         }
1728         // line endings:
1729         // UNIX: \n
1730         // Mac: \r
1731         // Windows: \r\n
1732         if (*l == '\r')
1733                 l++;
1734         if (*l == '\n')
1735                 l++;
1736         *text = l;
1737         return argc;
1738 }
1739
1740 /*
1741 ============
1742 COM_StringLengthNoColors
1743
1744 calculates the visible width of a color coded string.
1745
1746 *valid is filled with TRUE if the string is a valid colored string (that is, if
1747 it does not end with an unfinished color code). If it gets filled with FALSE, a
1748 fix would be adding a STRING_COLOR_TAG at the end of the string.
1749
1750 valid can be set to NULL if the caller doesn't care.
1751
1752 For size_s, specify the maximum number of characters from s to use, or 0 to use
1753 all characters until the zero terminator.
1754 ============
1755 */
1756 size_t
1757 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1758 {
1759         const char *end = size_s ? (s + size_s) : NULL;
1760         size_t len = 0;
1761         for(;;)
1762         {
1763                 switch((s == end) ? 0 : *s)
1764                 {
1765                         case 0:
1766                                 if(valid)
1767                                         *valid = TRUE;
1768                                 return len;
1769                         case STRING_COLOR_TAG:
1770                                 ++s;
1771                                 switch((s == end) ? 0 : *s)
1772                                 {
1773                                         case 0: // ends with unfinished color code!
1774                                                 ++len;
1775                                                 if(valid)
1776                                                         *valid = FALSE;
1777                                                 return len;
1778                                         case STRING_COLOR_TAG: // escaped ^
1779                                                 ++len;
1780                                                 break;
1781                                         case '0': case '1': case '2': case '3': case '4':
1782                                         case '5': case '6': case '7': case '8': case '9': // color code
1783                                                 break;
1784                                         default: // not a color code
1785                                                 ++len; // STRING_COLOR_TAG
1786                                                 ++len; // the character
1787                                                 break;
1788                                 }
1789                                 break;
1790                         default:
1791                                 ++len;
1792                                 break;
1793                 }
1794                 ++s;
1795         }
1796         // never get here
1797 }
1798
1799 /*
1800 ============
1801 COM_StringDecolorize
1802
1803 removes color codes from a string.
1804
1805 If escape_carets is true, the resulting string will be safe for printing. If
1806 escape_carets is false, the function will just strip color codes (for logging
1807 for example).
1808
1809 If the output buffer size did not suffice for converting, the function returns
1810 FALSE. Generally, if escape_carets is false, the output buffer needs
1811 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1812 bytes. In any case, the function makes sure that the resulting string is
1813 zero terminated.
1814
1815 For size_in, specify the maximum number of characters from in to use, or 0 to use
1816 all characters until the zero terminator.
1817 ============
1818 */
1819 qboolean
1820 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1821 {
1822 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1823         const char *end = size_in ? (in + size_in) : NULL;
1824         if(size_out < 1)
1825                 return FALSE;
1826         for(;;)
1827         {
1828                 switch((in == end) ? 0 : *in)
1829                 {
1830                         case 0:
1831                                 *out++ = 0;
1832                                 return TRUE;
1833                         case STRING_COLOR_TAG:
1834                                 ++in;
1835                                 switch((in == end) ? 0 : *in)
1836                                 {
1837                                         case 0: // ends with unfinished color code!
1838                                                 APPEND(STRING_COLOR_TAG);
1839                                                 // finish the code by appending another caret when escaping
1840                                                 if(escape_carets)
1841                                                         APPEND(STRING_COLOR_TAG);
1842                                                 *out++ = 0;
1843                                                 return TRUE;
1844                                         case STRING_COLOR_TAG: // escaped ^
1845                                                 APPEND(STRING_COLOR_TAG);
1846                                                 // append a ^ twice when escaping
1847                                                 if(escape_carets)
1848                                                         APPEND(STRING_COLOR_TAG);
1849                                                 break;
1850                                         case '0': case '1': case '2': case '3': case '4':
1851                                         case '5': case '6': case '7': case '8': case '9': // color code
1852                                                 break;
1853                                         default: // not a color code
1854                                                 APPEND(STRING_COLOR_TAG);
1855                                                 APPEND(*in);
1856                                                 break;
1857                                 }
1858                                 break;
1859                         default:
1860                                 APPEND(*in);
1861                                 break;
1862                 }
1863                 ++in;
1864         }
1865         // never get here
1866 #undef APPEND
1867 }
1868
1869 // written by Elric, thanks Elric!
1870 char *SearchInfostring(const char *infostring, const char *key)
1871 {
1872         static char value [MAX_INPUTLINE];
1873         char crt_key [MAX_INPUTLINE];
1874         size_t value_ind, key_ind;
1875         char c;
1876
1877         if (*infostring++ != '\\')
1878                 return NULL;
1879
1880         value_ind = 0;
1881         for (;;)
1882         {
1883                 key_ind = 0;
1884
1885                 // Get the key name
1886                 for (;;)
1887                 {
1888                         c = *infostring++;
1889
1890                         if (c == '\0')
1891                                 return NULL;
1892                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1893                         {
1894                                 crt_key[key_ind] = '\0';
1895                                 break;
1896                         }
1897
1898                         crt_key[key_ind++] = c;
1899                 }
1900
1901                 // If it's the key we are looking for, save it in "value"
1902                 if (!strcmp(crt_key, key))
1903                 {
1904                         for (;;)
1905                         {
1906                                 c = *infostring++;
1907
1908                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1909                                 {
1910                                         value[value_ind] = '\0';
1911                                         return value;
1912                                 }
1913
1914                                 value[value_ind++] = c;
1915                         }
1916                 }
1917
1918                 // Else, skip the value
1919                 for (;;)
1920                 {
1921                         c = *infostring++;
1922
1923                         if (c == '\0')
1924                                 return NULL;
1925                         if (c == '\\')
1926                                 break;
1927                 }
1928         }
1929 }
1930
1931 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1932 {
1933         int pos = 0, j;
1934         size_t keylength;
1935         if (!key)
1936                 key = "";
1937         if (!value)
1938                 value = "";
1939         keylength = strlen(key);
1940         if (valuelength < 1 || !value)
1941         {
1942                 Con_Printf("InfoString_GetValue: no room in value\n");
1943                 return;
1944         }
1945         value[0] = 0;
1946         if (strchr(key, '\\'))
1947         {
1948                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1949                 return;
1950         }
1951         if (strchr(key, '\"'))
1952         {
1953                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1954                 return;
1955         }
1956         if (!key[0])
1957         {
1958                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1959                 return;
1960         }
1961         while (buffer[pos] == '\\')
1962         {
1963                 if (!memcmp(buffer + pos+1, key, keylength))
1964                 {
1965                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1966                         pos++;
1967                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1968                                 value[j] = buffer[pos+j];
1969                         value[j] = 0;
1970                         return;
1971                 }
1972                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1973                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1974         }
1975         // if we reach this point the key was not found
1976 }
1977
1978 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1979 {
1980         int pos = 0, pos2;
1981         size_t keylength;
1982         if (!key)
1983                 key = "";
1984         if (!value)
1985                 value = "";
1986         keylength = strlen(key);
1987         if (strchr(key, '\\') || strchr(value, '\\'))
1988         {
1989                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1990                 return;
1991         }
1992         if (strchr(key, '\"') || strchr(value, '\"'))
1993         {
1994                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1995                 return;
1996         }
1997         if (!key[0])
1998         {
1999                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2000                 return;
2001         }
2002         while (buffer[pos] == '\\')
2003         {
2004                 if (!memcmp(buffer + pos+1, key, keylength))
2005                         break;
2006                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2007                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2008         }
2009         // if we found the key, find the end of it because we will be replacing it
2010         pos2 = pos;
2011         if (buffer[pos] == '\\')
2012         {
2013                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2014                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2015         }
2016         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2017         {
2018                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2019                 return;
2020         }
2021         if (value && value[0])
2022         {
2023                 // set the key/value and append the remaining text
2024                 char tempbuffer[4096];
2025                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2026                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
2027         }
2028         else
2029         {
2030                 // just remove the key from the text
2031                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2032         }
2033 }
2034
2035 void InfoString_Print(char *buffer)
2036 {
2037         int i;
2038         char key[2048];
2039         char value[2048];
2040         while (*buffer)
2041         {
2042                 if (*buffer != '\\')
2043                 {
2044                         Con_Printf("InfoString_Print: corrupt string\n");
2045                         return;
2046                 }
2047                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2048                         if (i < (int)sizeof(key)-1)
2049                                 key[i++] = *buffer;
2050                 key[i] = 0;
2051                 if (*buffer != '\\')
2052                 {
2053                         Con_Printf("InfoString_Print: corrupt string\n");
2054                         return;
2055                 }
2056                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2057                         if (i < (int)sizeof(value)-1)
2058                                 value[i++] = *buffer;
2059                 value[i] = 0;
2060                 // empty value is an error case
2061                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2062         }
2063 }
2064
2065 //========================================================
2066 // strlcat and strlcpy, from OpenBSD
2067
2068 /*
2069  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2070  *
2071  * Permission to use, copy, modify, and distribute this software for any
2072  * purpose with or without fee is hereby granted, provided that the above
2073  * copyright notice and this permission notice appear in all copies.
2074  *
2075  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2076  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2077  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2078  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2079  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2080  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2081  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2082  */
2083
2084 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2085 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2086
2087
2088 #ifndef HAVE_STRLCAT
2089 size_t
2090 strlcat(char *dst, const char *src, size_t siz)
2091 {
2092         register char *d = dst;
2093         register const char *s = src;
2094         register size_t n = siz;
2095         size_t dlen;
2096
2097         /* Find the end of dst and adjust bytes left but don't go past end */
2098         while (n-- != 0 && *d != '\0')
2099                 d++;
2100         dlen = d - dst;
2101         n = siz - dlen;
2102
2103         if (n == 0)
2104                 return(dlen + strlen(s));
2105         while (*s != '\0') {
2106                 if (n != 1) {
2107                         *d++ = *s;
2108                         n--;
2109                 }
2110                 s++;
2111         }
2112         *d = '\0';
2113
2114         return(dlen + (s - src));       /* count does not include NUL */
2115 }
2116 #endif  // #ifndef HAVE_STRLCAT
2117
2118
2119 #ifndef HAVE_STRLCPY
2120 size_t
2121 strlcpy(char *dst, const char *src, size_t siz)
2122 {
2123         register char *d = dst;
2124         register const char *s = src;
2125         register size_t n = siz;
2126
2127         /* Copy as many bytes as will fit */
2128         if (n != 0 && --n != 0) {
2129                 do {
2130                         if ((*d++ = *s++) == 0)
2131                                 break;
2132                 } while (--n != 0);
2133         }
2134
2135         /* Not enough room in dst, add NUL and traverse rest of src */
2136         if (n == 0) {
2137                 if (siz != 0)
2138                         *d = '\0';              /* NUL-terminate dst */
2139                 while (*s++)
2140                         ;
2141         }
2142
2143         return(s - src - 1);    /* count does not include NUL */
2144 }
2145
2146 #endif  // #ifndef HAVE_STRLCPY