reorganized Host_Init a bit, merged away a few functions (such as COM_CheckRegistered...
[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 };
974
975 void COM_InitGameType (void)
976 {
977         char name [MAX_OSPATH];
978         unsigned int i;
979
980         FS_StripExtension (com_argv[0], name, sizeof (name));
981         COM_ToLowerString (name, name, sizeof (name));
982
983         // Check the binary name; default to GAME_NORMAL (0)
984         gamemode = GAME_NORMAL;
985         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
986                 if (strstr (name, gamemode_info[i].prog_name))
987                 {
988                         gamemode = (gamemode_t)i;
989                         break;
990                 }
991
992         // Look for a command-line option
993         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
994                 if (COM_CheckParm (gamemode_info[i].cmdline))
995                 {
996                         gamemode = (gamemode_t)i;
997                         break;
998                 }
999
1000         gamename = gamemode_info[gamemode].gamename;
1001         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1002         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1003         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1004         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1005 }
1006
1007
1008 /*
1009 ================
1010 COM_Init
1011 ================
1012 */
1013 void COM_Init_Commands (void)
1014 {
1015         int i, j, n;
1016         char com_cmdline[MAX_INPUTLINE];
1017
1018         Cvar_RegisterVariable (&registered);
1019         Cvar_RegisterVariable (&cmdline);
1020
1021         // reconstitute the command line for the cmdline externally visible cvar
1022         n = 0;
1023         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1024         {
1025                 i = 0;
1026                 if (strstr(com_argv[j], " "))
1027                 {
1028                         // arg contains whitespace, store quotes around it
1029                         com_cmdline[n++] = '\"';
1030                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1031                                 com_cmdline[n++] = com_argv[j][i++];
1032                         com_cmdline[n++] = '\"';
1033                 }
1034                 else
1035                 {
1036                         while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1037                                 com_cmdline[n++] = com_argv[j][i++];
1038                 }
1039                 if (n < ((int)sizeof(com_cmdline) - 1))
1040                         com_cmdline[n++] = ' ';
1041                 else
1042                         break;
1043         }
1044         com_cmdline[n] = 0;
1045         Cvar_Set ("cmdline", com_cmdline);
1046 }
1047
1048 /*
1049 ============
1050 va
1051
1052 does a varargs printf into a temp buffer, so I don't need to have
1053 varargs versions of all text functions.
1054 FIXME: make this buffer size safe someday
1055 ============
1056 */
1057 char *va(const char *format, ...)
1058 {
1059         va_list argptr;
1060         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1061         static char string[8][1024], *s;
1062         static int stringindex = 0;
1063
1064         s = string[stringindex];
1065         stringindex = (stringindex + 1) & 7;
1066         va_start (argptr, format);
1067         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1068         va_end (argptr);
1069
1070         return s;
1071 }
1072
1073
1074 //======================================
1075
1076 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1077
1078 #undef snprintf
1079 #undef vsnprintf
1080
1081 #ifdef WIN32
1082 # define snprintf _snprintf
1083 # define vsnprintf _vsnprintf
1084 #endif
1085
1086
1087 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1088 {
1089         va_list args;
1090         int result;
1091
1092         va_start (args, format);
1093         result = dpvsnprintf (buffer, buffersize, format, args);
1094         va_end (args);
1095
1096         return result;
1097 }
1098
1099
1100 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1101 {
1102         int result;
1103
1104         result = vsnprintf (buffer, buffersize, format, args);
1105         if (result < 0 || (size_t)result >= buffersize)
1106         {
1107                 buffer[buffersize - 1] = '\0';
1108                 return -1;
1109         }
1110
1111         return result;
1112 }
1113
1114
1115 //======================================
1116
1117 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1118 {
1119         if (size_out == 0)
1120                 return;
1121
1122         while (*in && size_out > 1)
1123         {
1124                 if (*in >= 'A' && *in <= 'Z')
1125                         *out++ = *in++ + 'a' - 'A';
1126                 else
1127                         *out++ = *in++;
1128                 size_out--;
1129         }
1130         *out = '\0';
1131 }
1132
1133 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1134 {
1135         if (size_out == 0)
1136                 return;
1137
1138         while (*in && size_out > 1)
1139         {
1140                 if (*in >= 'a' && *in <= 'z')
1141                         *out++ = *in++ + 'A' - 'a';
1142                 else
1143                         *out++ = *in++;
1144                 size_out--;
1145         }
1146         *out = '\0';
1147 }
1148
1149 int COM_StringBeginsWith(const char *s, const char *match)
1150 {
1151         for (;*s && *match;s++, match++)
1152                 if (*s != *match)
1153                         return false;
1154         return true;
1155 }
1156
1157 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1158 {
1159         int argc, commentprefixlength;
1160         char *tokenbufend;
1161         const char *l;
1162         argc = 0;
1163         tokenbufend = tokenbuf + tokenbufsize;
1164         l = *text;
1165         commentprefixlength = 0;
1166         if (commentprefix)
1167                 commentprefixlength = (int)strlen(commentprefix);
1168         while (*l && *l != '\n' && *l != '\r')
1169         {
1170                 if (*l > ' ')
1171                 {
1172                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1173                         {
1174                                 while (*l && *l != '\n' && *l != '\r')
1175                                         l++;
1176                                 break;
1177                         }
1178                         if (argc >= maxargc)
1179                                 return -1;
1180                         argv[argc++] = tokenbuf;
1181                         if (*l == '"')
1182                         {
1183                                 l++;
1184                                 while (*l && *l != '"')
1185                                 {
1186                                         if (tokenbuf >= tokenbufend)
1187                                                 return -1;
1188                                         *tokenbuf++ = *l++;
1189                                 }
1190                                 if (*l == '"')
1191                                         l++;
1192                         }
1193                         else
1194                         {
1195                                 while (*l > ' ')
1196                                 {
1197                                         if (tokenbuf >= tokenbufend)
1198                                                 return -1;
1199                                         *tokenbuf++ = *l++;
1200                                 }
1201                         }
1202                         if (tokenbuf >= tokenbufend)
1203                                 return -1;
1204                         *tokenbuf++ = 0;
1205                 }
1206                 else
1207                         l++;
1208         }
1209         // line endings:
1210         // UNIX: \n
1211         // Mac: \r
1212         // Windows: \r\n
1213         if (*l == '\r')
1214                 l++;
1215         if (*l == '\n')
1216                 l++;
1217         *text = l;
1218         return argc;
1219 }
1220
1221 // written by Elric, thanks Elric!
1222 char *SearchInfostring(const char *infostring, const char *key)
1223 {
1224         static char value [MAX_INPUTLINE];
1225         char crt_key [MAX_INPUTLINE];
1226         size_t value_ind, key_ind;
1227         char c;
1228
1229         if (*infostring++ != '\\')
1230                 return NULL;
1231
1232         value_ind = 0;
1233         for (;;)
1234         {
1235                 key_ind = 0;
1236
1237                 // Get the key name
1238                 for (;;)
1239                 {
1240                         c = *infostring++;
1241
1242                         if (c == '\0')
1243                                 return NULL;
1244                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1245                         {
1246                                 crt_key[key_ind] = '\0';
1247                                 break;
1248                         }
1249
1250                         crt_key[key_ind++] = c;
1251                 }
1252
1253                 // If it's the key we are looking for, save it in "value"
1254                 if (!strcmp(crt_key, key))
1255                 {
1256                         for (;;)
1257                         {
1258                                 c = *infostring++;
1259
1260                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1261                                 {
1262                                         value[value_ind] = '\0';
1263                                         return value;
1264                                 }
1265
1266                                 value[value_ind++] = c;
1267                         }
1268                 }
1269
1270                 // Else, skip the value
1271                 for (;;)
1272                 {
1273                         c = *infostring++;
1274
1275                         if (c == '\0')
1276                                 return NULL;
1277                         if (c == '\\')
1278                                 break;
1279                 }
1280         }
1281 }
1282
1283 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1284 {
1285         int pos = 0, j;
1286         size_t keylength;
1287         if (!key)
1288                 key = "";
1289         if (!value)
1290                 value = "";
1291         keylength = strlen(key);
1292         if (valuelength < 1 || !value)
1293         {
1294                 Con_Printf("InfoString_GetValue: no room in value\n");
1295                 return;
1296         }
1297         value[0] = 0;
1298         if (strchr(key, '\\'))
1299         {
1300                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1301                 return;
1302         }
1303         if (strchr(key, '\"'))
1304         {
1305                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1306                 return;
1307         }
1308         if (!key[0])
1309         {
1310                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1311                 return;
1312         }
1313         while (buffer[pos] == '\\')
1314         {
1315                 if (!memcmp(buffer + pos+1, key, keylength))
1316                 {
1317                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1318                         pos++;
1319                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1320                                 value[j] = buffer[pos+j];
1321                         value[j] = 0;
1322                         return;
1323                 }
1324                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1325                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1326         }
1327         // if we reach this point the key was not found
1328 }
1329
1330 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1331 {
1332         int pos = 0, pos2;
1333         size_t keylength;
1334         if (!key)
1335                 key = "";
1336         if (!value)
1337                 value = "";
1338         keylength = strlen(key);
1339         if (strchr(key, '\\') || strchr(value, '\\'))
1340         {
1341                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1342                 return;
1343         }
1344         if (strchr(key, '\"') || strchr(value, '\"'))
1345         {
1346                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1347                 return;
1348         }
1349         if (!key[0])
1350         {
1351                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1352                 return;
1353         }
1354         while (buffer[pos] == '\\')
1355         {
1356                 if (!memcmp(buffer + pos+1, key, keylength))
1357                         break;
1358                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1359                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1360         }
1361         // if we found the key, find the end of it because we will be replacing it
1362         pos2 = pos;
1363         if (buffer[pos] == '\\')
1364         {
1365                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1366                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1367         }
1368         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1369         {
1370                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1371                 return;
1372         }
1373         if (value && value[0])
1374         {
1375                 // set the key/value and append the remaining text
1376                 char tempbuffer[4096];
1377                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1378                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
1379         }
1380         else
1381         {
1382                 // just remove the key from the text
1383                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1384         }
1385 }
1386
1387 void InfoString_Print(char *buffer)
1388 {
1389         int i;
1390         char key[2048];
1391         char value[2048];
1392         while (*buffer)
1393         {
1394                 if (*buffer != '\\')
1395                 {
1396                         Con_Printf("InfoString_Print: corrupt string\n");
1397                         return;
1398                 }
1399                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1400                         if (i < (int)sizeof(key)-1)
1401                                 key[i++] = *buffer;
1402                 key[i] = 0;
1403                 if (*buffer != '\\')
1404                 {
1405                         Con_Printf("InfoString_Print: corrupt string\n");
1406                         return;
1407                 }
1408                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1409                         if (i < (int)sizeof(value)-1)
1410                                 value[i++] = *buffer;
1411                 value[i] = 0;
1412                 // empty value is an error case
1413                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1414         }
1415 }
1416
1417 //========================================================
1418 // strlcat and strlcpy, from OpenBSD
1419
1420 /*
1421  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1422  *
1423  * Permission to use, copy, modify, and distribute this software for any
1424  * purpose with or without fee is hereby granted, provided that the above
1425  * copyright notice and this permission notice appear in all copies.
1426  *
1427  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1428  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1429  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1430  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1431  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1432  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1433  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1434  */
1435
1436 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1437 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1438
1439
1440 #ifndef HAVE_STRLCAT
1441 size_t
1442 strlcat(char *dst, const char *src, size_t siz)
1443 {
1444         register char *d = dst;
1445         register const char *s = src;
1446         register size_t n = siz;
1447         size_t dlen;
1448
1449         /* Find the end of dst and adjust bytes left but don't go past end */
1450         while (n-- != 0 && *d != '\0')
1451                 d++;
1452         dlen = d - dst;
1453         n = siz - dlen;
1454
1455         if (n == 0)
1456                 return(dlen + strlen(s));
1457         while (*s != '\0') {
1458                 if (n != 1) {
1459                         *d++ = *s;
1460                         n--;
1461                 }
1462                 s++;
1463         }
1464         *d = '\0';
1465
1466         return(dlen + (s - src));       /* count does not include NUL */
1467 }
1468 #endif  // #ifndef HAVE_STRLCAT
1469
1470
1471 #ifndef HAVE_STRLCPY
1472 size_t
1473 strlcpy(char *dst, const char *src, size_t siz)
1474 {
1475         register char *d = dst;
1476         register const char *s = src;
1477         register size_t n = siz;
1478
1479         /* Copy as many bytes as will fit */
1480         if (n != 0 && --n != 0) {
1481                 do {
1482                         if ((*d++ = *s++) == 0)
1483                                 break;
1484                 } while (--n != 0);
1485         }
1486
1487         /* Not enough room in dst, add NUL and traverse rest of src */
1488         if (n == 0) {
1489                 if (siz != 0)
1490                         *d = '\0';              /* NUL-terminate dst */
1491                 while (*s++)
1492                         ;
1493         }
1494
1495         return(s - src - 1);    /* count does not include NUL */
1496 }
1497
1498 #endif  // #ifndef HAVE_STRLCPY