]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
Support file symlinks inside pk3 files. Directory symlinks, as well as symlinks with...
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include "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 // GAME_PROPHECY
1493 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1494 { "prophecy",                           "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
1495 };
1496
1497 void COM_InitGameType (void)
1498 {
1499         char name [MAX_OSPATH];
1500         unsigned int i;
1501
1502         FS_StripExtension (com_argv[0], name, sizeof (name));
1503         COM_ToLowerString (name, name, sizeof (name));
1504
1505         // Check the binary name; default to GAME_NORMAL (0)
1506         gamemode = GAME_NORMAL;
1507         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1508                 if (strstr (name, gamemode_info[i].prog_name))
1509                 {
1510                         gamemode = (gamemode_t)i;
1511                         break;
1512                 }
1513
1514         // Look for a command-line option
1515         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1516                 if (COM_CheckParm (gamemode_info[i].cmdline))
1517                 {
1518                         gamemode = (gamemode_t)i;
1519                         break;
1520                 }
1521
1522         gamename = gamemode_info[gamemode].gamename;
1523         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1524         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1525         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1526         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1527 }
1528
1529
1530 /*
1531 ================
1532 COM_Init
1533 ================
1534 */
1535 void COM_Init_Commands (void)
1536 {
1537         int i, j, n;
1538         char com_cmdline[MAX_INPUTLINE];
1539
1540         Cvar_RegisterVariable (&registered);
1541         Cvar_RegisterVariable (&cmdline);
1542
1543         // reconstitute the command line for the cmdline externally visible cvar
1544         n = 0;
1545         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1546         {
1547                 i = 0;
1548                 if (strstr(com_argv[j], " "))
1549                 {
1550                         // arg contains whitespace, store quotes around it
1551                         com_cmdline[n++] = '\"';
1552                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1553                                 com_cmdline[n++] = com_argv[j][i++];
1554                         com_cmdline[n++] = '\"';
1555                 }
1556                 else
1557                 {
1558                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1559                                 com_cmdline[n++] = com_argv[j][i++];
1560                 }
1561                 if (n < ((int)sizeof(com_cmdline) - 1))
1562                         com_cmdline[n++] = ' ';
1563                 else
1564                         break;
1565         }
1566         com_cmdline[n] = 0;
1567         Cvar_Set ("cmdline", com_cmdline);
1568 }
1569
1570 /*
1571 ============
1572 va
1573
1574 does a varargs printf into a temp buffer, so I don't need to have
1575 varargs versions of all text functions.
1576 FIXME: make this buffer size safe someday
1577 ============
1578 */
1579 char *va(const char *format, ...)
1580 {
1581         va_list argptr;
1582         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1583         static char string[8][1024], *s;
1584         static int stringindex = 0;
1585
1586         s = string[stringindex];
1587         stringindex = (stringindex + 1) & 7;
1588         va_start (argptr, format);
1589         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1590         va_end (argptr);
1591
1592         return s;
1593 }
1594
1595
1596 //======================================
1597
1598 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1599
1600 #undef snprintf
1601 #undef vsnprintf
1602
1603 #ifdef WIN32
1604 # define snprintf _snprintf
1605 # define vsnprintf _vsnprintf
1606 #endif
1607
1608
1609 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1610 {
1611         va_list args;
1612         int result;
1613
1614         va_start (args, format);
1615         result = dpvsnprintf (buffer, buffersize, format, args);
1616         va_end (args);
1617
1618         return result;
1619 }
1620
1621
1622 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1623 {
1624         int result;
1625
1626 #if _MSC_VER >= 1400
1627         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1628 #else
1629         result = vsnprintf (buffer, buffersize, format, args);
1630 #endif
1631         if (result < 0 || (size_t)result >= buffersize)
1632         {
1633                 buffer[buffersize - 1] = '\0';
1634                 return -1;
1635         }
1636
1637         return result;
1638 }
1639
1640
1641 //======================================
1642
1643 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1644 {
1645         if (size_out == 0)
1646                 return;
1647
1648         while (*in && size_out > 1)
1649         {
1650                 if (*in >= 'A' && *in <= 'Z')
1651                         *out++ = *in++ + 'a' - 'A';
1652                 else
1653                         *out++ = *in++;
1654                 size_out--;
1655         }
1656         *out = '\0';
1657 }
1658
1659 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1660 {
1661         if (size_out == 0)
1662                 return;
1663
1664         while (*in && size_out > 1)
1665         {
1666                 if (*in >= 'a' && *in <= 'z')
1667                         *out++ = *in++ + 'A' - 'a';
1668                 else
1669                         *out++ = *in++;
1670                 size_out--;
1671         }
1672         *out = '\0';
1673 }
1674
1675 int COM_StringBeginsWith(const char *s, const char *match)
1676 {
1677         for (;*s && *match;s++, match++)
1678                 if (*s != *match)
1679                         return false;
1680         return true;
1681 }
1682
1683 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1684 {
1685         int argc, commentprefixlength;
1686         char *tokenbufend;
1687         const char *l;
1688         argc = 0;
1689         tokenbufend = tokenbuf + tokenbufsize;
1690         l = *text;
1691         commentprefixlength = 0;
1692         if (commentprefix)
1693                 commentprefixlength = (int)strlen(commentprefix);
1694         while (*l && *l != '\n' && *l != '\r')
1695         {
1696                 if (*l > ' ')
1697                 {
1698                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1699                         {
1700                                 while (*l && *l != '\n' && *l != '\r')
1701                                         l++;
1702                                 break;
1703                         }
1704                         if (argc >= maxargc)
1705                                 return -1;
1706                         argv[argc++] = tokenbuf;
1707                         if (*l == '"')
1708                         {
1709                                 l++;
1710                                 while (*l && *l != '"')
1711                                 {
1712                                         if (tokenbuf >= tokenbufend)
1713                                                 return -1;
1714                                         *tokenbuf++ = *l++;
1715                                 }
1716                                 if (*l == '"')
1717                                         l++;
1718                         }
1719                         else
1720                         {
1721                                 while (*l > ' ')
1722                                 {
1723                                         if (tokenbuf >= tokenbufend)
1724                                                 return -1;
1725                                         *tokenbuf++ = *l++;
1726                                 }
1727                         }
1728                         if (tokenbuf >= tokenbufend)
1729                                 return -1;
1730                         *tokenbuf++ = 0;
1731                 }
1732                 else
1733                         l++;
1734         }
1735         // line endings:
1736         // UNIX: \n
1737         // Mac: \r
1738         // Windows: \r\n
1739         if (*l == '\r')
1740                 l++;
1741         if (*l == '\n')
1742                 l++;
1743         *text = l;
1744         return argc;
1745 }
1746
1747 /*
1748 ============
1749 COM_StringLengthNoColors
1750
1751 calculates the visible width of a color coded string.
1752
1753 *valid is filled with TRUE if the string is a valid colored string (that is, if
1754 it does not end with an unfinished color code). If it gets filled with FALSE, a
1755 fix would be adding a STRING_COLOR_TAG at the end of the string.
1756
1757 valid can be set to NULL if the caller doesn't care.
1758
1759 For size_s, specify the maximum number of characters from s to use, or 0 to use
1760 all characters until the zero terminator.
1761 ============
1762 */
1763 size_t
1764 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1765 {
1766         const char *end = size_s ? (s + size_s) : NULL;
1767         size_t len = 0;
1768         for(;;)
1769         {
1770                 switch((s == end) ? 0 : *s)
1771                 {
1772                         case 0:
1773                                 if(valid)
1774                                         *valid = TRUE;
1775                                 return len;
1776                         case STRING_COLOR_TAG:
1777                                 ++s;
1778                                 switch((s == end) ? 0 : *s)
1779                                 {
1780                                         case 0: // ends with unfinished color code!
1781                                                 ++len;
1782                                                 if(valid)
1783                                                         *valid = FALSE;
1784                                                 return len;
1785                                         case STRING_COLOR_TAG: // escaped ^
1786                                                 ++len;
1787                                                 break;
1788                                         case '0': case '1': case '2': case '3': case '4':
1789                                         case '5': case '6': case '7': case '8': case '9': // color code
1790                                                 break;
1791                                         default: // not a color code
1792                                                 ++len; // STRING_COLOR_TAG
1793                                                 ++len; // the character
1794                                                 break;
1795                                 }
1796                                 break;
1797                         default:
1798                                 ++len;
1799                                 break;
1800                 }
1801                 ++s;
1802         }
1803         // never get here
1804 }
1805
1806 /*
1807 ============
1808 COM_StringDecolorize
1809
1810 removes color codes from a string.
1811
1812 If escape_carets is true, the resulting string will be safe for printing. If
1813 escape_carets is false, the function will just strip color codes (for logging
1814 for example).
1815
1816 If the output buffer size did not suffice for converting, the function returns
1817 FALSE. Generally, if escape_carets is false, the output buffer needs
1818 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)+2
1819 bytes. In any case, the function makes sure that the resulting string is
1820 zero terminated.
1821
1822 For size_in, specify the maximum number of characters from in to use, or 0 to use
1823 all characters until the zero terminator.
1824 ============
1825 */
1826 qboolean
1827 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1828 {
1829 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1830         const char *end = size_in ? (in + size_in) : NULL;
1831         if(size_out < 1)
1832                 return FALSE;
1833         for(;;)
1834         {
1835                 switch((in == end) ? 0 : *in)
1836                 {
1837                         case 0:
1838                                 *out++ = 0;
1839                                 return TRUE;
1840                         case STRING_COLOR_TAG:
1841                                 ++in;
1842                                 switch((in == end) ? 0 : *in)
1843                                 {
1844                                         case 0: // ends with unfinished color code!
1845                                                 APPEND(STRING_COLOR_TAG);
1846                                                 // finish the code by appending another caret when escaping
1847                                                 if(escape_carets)
1848                                                         APPEND(STRING_COLOR_TAG);
1849                                                 *out++ = 0;
1850                                                 return TRUE;
1851                                         case STRING_COLOR_TAG: // escaped ^
1852                                                 APPEND(STRING_COLOR_TAG);
1853                                                 // append a ^ twice when escaping
1854                                                 if(escape_carets)
1855                                                         APPEND(STRING_COLOR_TAG);
1856                                                 break;
1857                                         case '0': case '1': case '2': case '3': case '4':
1858                                         case '5': case '6': case '7': case '8': case '9': // color code
1859                                                 break;
1860                                         default: // not a color code
1861                                                 APPEND(STRING_COLOR_TAG);
1862                                                 APPEND(*in);
1863                                                 break;
1864                                 }
1865                                 break;
1866                         default:
1867                                 APPEND(*in);
1868                                 break;
1869                 }
1870                 ++in;
1871         }
1872         // never get here
1873 #undef APPEND
1874 }
1875
1876 // written by Elric, thanks Elric!
1877 char *SearchInfostring(const char *infostring, const char *key)
1878 {
1879         static char value [MAX_INPUTLINE];
1880         char crt_key [MAX_INPUTLINE];
1881         size_t value_ind, key_ind;
1882         char c;
1883
1884         if (*infostring++ != '\\')
1885                 return NULL;
1886
1887         value_ind = 0;
1888         for (;;)
1889         {
1890                 key_ind = 0;
1891
1892                 // Get the key name
1893                 for (;;)
1894                 {
1895                         c = *infostring++;
1896
1897                         if (c == '\0')
1898                                 return NULL;
1899                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1900                         {
1901                                 crt_key[key_ind] = '\0';
1902                                 break;
1903                         }
1904
1905                         crt_key[key_ind++] = c;
1906                 }
1907
1908                 // If it's the key we are looking for, save it in "value"
1909                 if (!strcmp(crt_key, key))
1910                 {
1911                         for (;;)
1912                         {
1913                                 c = *infostring++;
1914
1915                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1916                                 {
1917                                         value[value_ind] = '\0';
1918                                         return value;
1919                                 }
1920
1921                                 value[value_ind++] = c;
1922                         }
1923                 }
1924
1925                 // Else, skip the value
1926                 for (;;)
1927                 {
1928                         c = *infostring++;
1929
1930                         if (c == '\0')
1931                                 return NULL;
1932                         if (c == '\\')
1933                                 break;
1934                 }
1935         }
1936 }
1937
1938 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1939 {
1940         int pos = 0, j;
1941         size_t keylength;
1942         if (!key)
1943                 key = "";
1944         if (!value)
1945                 value = "";
1946         keylength = strlen(key);
1947         if (valuelength < 1 || !value)
1948         {
1949                 Con_Printf("InfoString_GetValue: no room in value\n");
1950                 return;
1951         }
1952         value[0] = 0;
1953         if (strchr(key, '\\'))
1954         {
1955                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1956                 return;
1957         }
1958         if (strchr(key, '\"'))
1959         {
1960                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1961                 return;
1962         }
1963         if (!key[0])
1964         {
1965                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1966                 return;
1967         }
1968         while (buffer[pos] == '\\')
1969         {
1970                 if (!memcmp(buffer + pos+1, key, keylength))
1971                 {
1972                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1973                         pos++;
1974                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1975                                 value[j] = buffer[pos+j];
1976                         value[j] = 0;
1977                         return;
1978                 }
1979                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1980                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1981         }
1982         // if we reach this point the key was not found
1983 }
1984
1985 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1986 {
1987         int pos = 0, pos2;
1988         size_t keylength;
1989         if (!key)
1990                 key = "";
1991         if (!value)
1992                 value = "";
1993         keylength = strlen(key);
1994         if (strchr(key, '\\') || strchr(value, '\\'))
1995         {
1996                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1997                 return;
1998         }
1999         if (strchr(key, '\"') || strchr(value, '\"'))
2000         {
2001                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2002                 return;
2003         }
2004         if (!key[0])
2005         {
2006                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2007                 return;
2008         }
2009         while (buffer[pos] == '\\')
2010         {
2011                 if (!memcmp(buffer + pos+1, key, keylength))
2012                         break;
2013                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2014                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2015         }
2016         // if we found the key, find the end of it because we will be replacing it
2017         pos2 = pos;
2018         if (buffer[pos] == '\\')
2019         {
2020                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2021                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2022         }
2023         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2024         {
2025                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2026                 return;
2027         }
2028         if (value && value[0])
2029         {
2030                 // set the key/value and append the remaining text
2031                 char tempbuffer[4096];
2032                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2033                 dpsnprintf(buffer + pos, sizeof(buffer) - pos, "\\%s\\%s%s", key, value, tempbuffer);
2034         }
2035         else
2036         {
2037                 // just remove the key from the text
2038                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2039         }
2040 }
2041
2042 void InfoString_Print(char *buffer)
2043 {
2044         int i;
2045         char key[2048];
2046         char value[2048];
2047         while (*buffer)
2048         {
2049                 if (*buffer != '\\')
2050                 {
2051                         Con_Printf("InfoString_Print: corrupt string\n");
2052                         return;
2053                 }
2054                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2055                         if (i < (int)sizeof(key)-1)
2056                                 key[i++] = *buffer;
2057                 key[i] = 0;
2058                 if (*buffer != '\\')
2059                 {
2060                         Con_Printf("InfoString_Print: corrupt string\n");
2061                         return;
2062                 }
2063                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2064                         if (i < (int)sizeof(value)-1)
2065                                 value[i++] = *buffer;
2066                 value[i] = 0;
2067                 // empty value is an error case
2068                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2069         }
2070 }
2071
2072 //========================================================
2073 // strlcat and strlcpy, from OpenBSD
2074
2075 /*
2076  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2077  *
2078  * Permission to use, copy, modify, and distribute this software for any
2079  * purpose with or without fee is hereby granted, provided that the above
2080  * copyright notice and this permission notice appear in all copies.
2081  *
2082  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2083  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2084  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2085  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2086  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2087  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2088  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2089  */
2090
2091 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
2092 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
2093
2094
2095 #ifndef HAVE_STRLCAT
2096 size_t
2097 strlcat(char *dst, const char *src, size_t siz)
2098 {
2099         register char *d = dst;
2100         register const char *s = src;
2101         register size_t n = siz;
2102         size_t dlen;
2103
2104         /* Find the end of dst and adjust bytes left but don't go past end */
2105         while (n-- != 0 && *d != '\0')
2106                 d++;
2107         dlen = d - dst;
2108         n = siz - dlen;
2109
2110         if (n == 0)
2111                 return(dlen + strlen(s));
2112         while (*s != '\0') {
2113                 if (n != 1) {
2114                         *d++ = *s;
2115                         n--;
2116                 }
2117                 s++;
2118         }
2119         *d = '\0';
2120
2121         return(dlen + (s - src));       /* count does not include NUL */
2122 }
2123 #endif  // #ifndef HAVE_STRLCAT
2124
2125
2126 #ifndef HAVE_STRLCPY
2127 size_t
2128 strlcpy(char *dst, const char *src, size_t siz)
2129 {
2130         register char *d = dst;
2131         register const char *s = src;
2132         register size_t n = siz;
2133
2134         /* Copy as many bytes as will fit */
2135         if (n != 0 && --n != 0) {
2136                 do {
2137                         if ((*d++ = *s++) == 0)
2138                                 break;
2139                 } while (--n != 0);
2140         }
2141
2142         /* Not enough room in dst, add NUL and traverse rest of src */
2143         if (n == 0) {
2144                 if (siz != 0)
2145                         *d = '\0';              /* NUL-terminate dst */
2146                 while (*s++)
2147                         ;
2148         }
2149
2150         return(s - src - 1);    /* count does not include NUL */
2151 }
2152
2153 #endif  // #ifndef HAVE_STRLCPY