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