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