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