]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
very very minor optimization
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <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_SETHERAL
1006 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1007 { "setheral",           "-setheral",    "Setheral",                             "data",         NULL,                   "setheral",             "setheral" },
1008 // GAME_SOM
1009 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1010 { "som",                        "-som",                 "Son of Man",                   "id1",          "sonofman",             "som",                  "darkplaces" },
1011 // GAME_TENEBRAE
1012 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1013 { "tenebrae",           "-tenebrae",    "DarkPlaces-Tenebrae",  "id1",          "tenebrae",             "dp",                   "darkplaces" },
1014 // GAME_NEOTERIC
1015 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1016 { "neoteric",           "-neoteric",    "Neoteric",                             "id1",          "neobase",              "neo",                  "darkplaces" },
1017 // GAME_OPENQUARTZ
1018 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1019 { "openquartz",         "-openquartz",  "OpenQuartz",                   "id1",          NULL,                   "openquartz",   "darkplaces" },
1020 // GAME_PRYDON
1021 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1022 { "prydon",                     "-prydon",              "PrydonGate",                   "id1",          "prydon",               "prydon",               "darkplaces" },
1023 // GAME_NETHERWORLD
1024 // COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master
1025 { "netherworld",        "-netherworld", "Netherworld: Dark Master",     "id1",          "netherworld",  "nw",                   "darkplaces" },
1026 // GAME_THEHUNTED
1027 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1028 { "thehunted",          "-thehunted",   "The Hunted",                   "thdata",       NULL,                   "th",                   "thehunted" },
1029 // GAME_DEFEATINDETAIL2
1030 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1031 { "did2",                       "-did2",                "Defeat In Detail 2",   "data",         NULL,                   "did2_",                "did2" },
1032 };
1033
1034 void COM_InitGameType (void)
1035 {
1036         char name [MAX_OSPATH];
1037         unsigned int i;
1038
1039         FS_StripExtension (com_argv[0], name, sizeof (name));
1040         COM_ToLowerString (name, name, sizeof (name));
1041
1042         // Check the binary name; default to GAME_NORMAL (0)
1043         gamemode = GAME_NORMAL;
1044         for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1045                 if (strstr (name, gamemode_info[i].prog_name))
1046                 {
1047                         gamemode = (gamemode_t)i;
1048                         break;
1049                 }
1050
1051         // Look for a command-line option
1052         for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1053                 if (COM_CheckParm (gamemode_info[i].cmdline))
1054                 {
1055                         gamemode = (gamemode_t)i;
1056                         break;
1057                 }
1058
1059         gamename = gamemode_info[gamemode].gamename;
1060         gamedirname1 = gamemode_info[gamemode].gamedirname1;
1061         gamedirname2 = gamemode_info[gamemode].gamedirname2;
1062         gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1063         gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1064 }
1065
1066
1067 /*
1068 ================
1069 COM_Init
1070 ================
1071 */
1072 void COM_Init_Commands (void)
1073 {
1074         Cvar_RegisterVariable (&registered);
1075         Cvar_RegisterVariable (&cmdline);
1076 }
1077
1078 /*
1079 ============
1080 va
1081
1082 does a varargs printf into a temp buffer, so I don't need to have
1083 varargs versions of all text functions.
1084 FIXME: make this buffer size safe someday
1085 ============
1086 */
1087 char *va(const char *format, ...)
1088 {
1089         va_list argptr;
1090         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1091         static char string[8][1024], *s;
1092         static int stringindex = 0;
1093
1094         s = string[stringindex];
1095         stringindex = (stringindex + 1) & 7;
1096         va_start (argptr, format);
1097         dpvsnprintf (s, sizeof (string[0]), format,argptr);
1098         va_end (argptr);
1099
1100         return s;
1101 }
1102
1103
1104 //======================================
1105
1106 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1107
1108 #undef snprintf
1109 #undef vsnprintf
1110
1111 #ifdef WIN32
1112 # define snprintf _snprintf
1113 # define vsnprintf _vsnprintf
1114 #endif
1115
1116
1117 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1118 {
1119         va_list args;
1120         int result;
1121
1122         va_start (args, format);
1123         result = dpvsnprintf (buffer, buffersize, format, args);
1124         va_end (args);
1125
1126         return result;
1127 }
1128
1129
1130 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1131 {
1132         int result;
1133
1134         result = vsnprintf (buffer, buffersize, format, args);
1135         if (result < 0 || (size_t)result >= buffersize)
1136         {
1137                 buffer[buffersize - 1] = '\0';
1138                 return -1;
1139         }
1140
1141         return result;
1142 }
1143
1144
1145 //======================================
1146
1147 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1148 {
1149         if (size_out == 0)
1150                 return;
1151
1152         while (*in && size_out > 1)
1153         {
1154                 if (*in >= 'A' && *in <= 'Z')
1155                         *out++ = *in++ + 'a' - 'A';
1156                 else
1157                         *out++ = *in++;
1158                 size_out--;
1159         }
1160         *out = '\0';
1161 }
1162
1163 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1164 {
1165         if (size_out == 0)
1166                 return;
1167
1168         while (*in && size_out > 1)
1169         {
1170                 if (*in >= 'a' && *in <= 'z')
1171                         *out++ = *in++ + 'A' - 'a';
1172                 else
1173                         *out++ = *in++;
1174                 size_out--;
1175         }
1176         *out = '\0';
1177 }
1178
1179 int COM_StringBeginsWith(const char *s, const char *match)
1180 {
1181         for (;*s && *match;s++, match++)
1182                 if (*s != *match)
1183                         return false;
1184         return true;
1185 }
1186
1187 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1188 {
1189         int argc, commentprefixlength;
1190         char *tokenbufend;
1191         const char *l;
1192         argc = 0;
1193         tokenbufend = tokenbuf + tokenbufsize;
1194         l = *text;
1195         commentprefixlength = 0;
1196         if (commentprefix)
1197                 commentprefixlength = (int)strlen(commentprefix);
1198         while (*l && *l != '\n' && *l != '\r')
1199         {
1200                 if (*l > ' ')
1201                 {
1202                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1203                         {
1204                                 while (*l && *l != '\n' && *l != '\r')
1205                                         l++;
1206                                 break;
1207                         }
1208                         if (argc >= maxargc)
1209                                 return -1;
1210                         argv[argc++] = tokenbuf;
1211                         if (*l == '"')
1212                         {
1213                                 l++;
1214                                 while (*l && *l != '"')
1215                                 {
1216                                         if (tokenbuf >= tokenbufend)
1217                                                 return -1;
1218                                         *tokenbuf++ = *l++;
1219                                 }
1220                                 if (*l == '"')
1221                                         l++;
1222                         }
1223                         else
1224                         {
1225                                 while (*l > ' ')
1226                                 {
1227                                         if (tokenbuf >= tokenbufend)
1228                                                 return -1;
1229                                         *tokenbuf++ = *l++;
1230                                 }
1231                         }
1232                         if (tokenbuf >= tokenbufend)
1233                                 return -1;
1234                         *tokenbuf++ = 0;
1235                 }
1236                 else
1237                         l++;
1238         }
1239         // line endings:
1240         // UNIX: \n
1241         // Mac: \r
1242         // Windows: \r\n
1243         if (*l == '\r')
1244                 l++;
1245         if (*l == '\n')
1246                 l++;
1247         *text = l;
1248         return argc;
1249 }
1250
1251 // written by Elric, thanks Elric!
1252 char *SearchInfostring(const char *infostring, const char *key)
1253 {
1254         static char value [MAX_INPUTLINE];
1255         char crt_key [MAX_INPUTLINE];
1256         size_t value_ind, key_ind;
1257         char c;
1258
1259         if (*infostring++ != '\\')
1260                 return NULL;
1261
1262         value_ind = 0;
1263         for (;;)
1264         {
1265                 key_ind = 0;
1266
1267                 // Get the key name
1268                 for (;;)
1269                 {
1270                         c = *infostring++;
1271
1272                         if (c == '\0')
1273                                 return NULL;
1274                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1275                         {
1276                                 crt_key[key_ind] = '\0';
1277                                 break;
1278                         }
1279
1280                         crt_key[key_ind++] = c;
1281                 }
1282
1283                 // If it's the key we are looking for, save it in "value"
1284                 if (!strcmp(crt_key, key))
1285                 {
1286                         for (;;)
1287                         {
1288                                 c = *infostring++;
1289
1290                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1291                                 {
1292                                         value[value_ind] = '\0';
1293                                         return value;
1294                                 }
1295
1296                                 value[value_ind++] = c;
1297                         }
1298                 }
1299
1300                 // Else, skip the value
1301                 for (;;)
1302                 {
1303                         c = *infostring++;
1304
1305                         if (c == '\0')
1306                                 return NULL;
1307                         if (c == '\\')
1308                                 break;
1309                 }
1310         }
1311 }
1312
1313 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1314 {
1315         int pos = 0, j;
1316         size_t keylength;
1317         if (!key)
1318                 key = "";
1319         if (!value)
1320                 value = "";
1321         keylength = strlen(key);
1322         if (valuelength < 1 || !value)
1323         {
1324                 Con_Printf("InfoString_GetValue: no room in value\n");
1325                 return;
1326         }
1327         value[0] = 0;
1328         if (strchr(key, '\\'))
1329         {
1330                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1331                 return;
1332         }
1333         if (strchr(key, '\"'))
1334         {
1335                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1336                 return;
1337         }
1338         if (!key[0])
1339         {
1340                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1341                 return;
1342         }
1343         while (buffer[pos] == '\\')
1344         {
1345                 if (!memcmp(buffer + pos+1, key, keylength))
1346                 {
1347                         for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1348                         pos++;
1349                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1350                                 value[j] = buffer[pos+j];
1351                         value[j] = 0;
1352                         return;
1353                 }
1354                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1355                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1356         }
1357         // if we reach this point the key was not found
1358 }
1359
1360 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1361 {
1362         int pos = 0, pos2;
1363         size_t keylength;
1364         if (!key)
1365                 key = "";
1366         if (!value)
1367                 value = "";
1368         keylength = strlen(key);
1369         if (strchr(key, '\\') || strchr(value, '\\'))
1370         {
1371                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1372                 return;
1373         }
1374         if (strchr(key, '\"') || strchr(value, '\"'))
1375         {
1376                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1377                 return;
1378         }
1379         if (!key[0])
1380         {
1381                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1382                 return;
1383         }
1384         while (buffer[pos] == '\\')
1385         {
1386                 if (!memcmp(buffer + pos+1, key, keylength))
1387                         break;
1388                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1389                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1390         }
1391         // if we found the key, find the end of it because we will be replacing it
1392         pos2 = pos;
1393         if (buffer[pos] == '\\')
1394         {
1395                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1396                 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1397         }
1398         if (bufferlength <= 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1399         {
1400                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1401                 return;
1402         }
1403         if (value && value[0])
1404         {
1405                 // set the key/value and append the remaining text
1406                 char tempbuffer[4096];
1407                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1408                 sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
1409         }
1410         else
1411         {
1412                 // just remove the key from the text
1413                 strcpy(buffer + pos, buffer + pos2);
1414         }
1415 }
1416
1417 void InfoString_Print(char *buffer)
1418 {
1419         int i;
1420         char key[2048];
1421         char value[2048];
1422         while (*buffer)
1423         {
1424                 if (*buffer != '\\')
1425                 {
1426                         Con_Printf("InfoString_Print: corrupt string\n");
1427                         return;
1428                 }
1429                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1430                         if (i < (int)sizeof(key)-1)
1431                                 key[i++] = *buffer;
1432                 key[i] = 0;
1433                 if (*buffer != '\\')
1434                 {
1435                         Con_Printf("InfoString_Print: corrupt string\n");
1436                         return;
1437                 }
1438                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1439                         if (i < (int)sizeof(value)-1)
1440                                 value[i++] = *buffer;
1441                 value[i] = 0;
1442                 // empty value is an error case
1443                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1444         }
1445 }
1446
1447 //========================================================
1448 // strlcat and strlcpy, from OpenBSD
1449
1450 /*
1451  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1452  *
1453  * Permission to use, copy, modify, and distribute this software for any
1454  * purpose with or without fee is hereby granted, provided that the above
1455  * copyright notice and this permission notice appear in all copies.
1456  *
1457  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1458  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1459  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1460  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1462  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1463  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1464  */
1465
1466 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1467 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1468
1469
1470 #ifndef HAVE_STRLCAT
1471 size_t
1472 strlcat(char *dst, const char *src, size_t siz)
1473 {
1474         register char *d = dst;
1475         register const char *s = src;
1476         register size_t n = siz;
1477         size_t dlen;
1478
1479         /* Find the end of dst and adjust bytes left but don't go past end */
1480         while (n-- != 0 && *d != '\0')
1481                 d++;
1482         dlen = d - dst;
1483         n = siz - dlen;
1484
1485         if (n == 0)
1486                 return(dlen + strlen(s));
1487         while (*s != '\0') {
1488                 if (n != 1) {
1489                         *d++ = *s;
1490                         n--;
1491                 }
1492                 s++;
1493         }
1494         *d = '\0';
1495
1496         return(dlen + (s - src));       /* count does not include NUL */
1497 }
1498 #endif  // #ifndef HAVE_STRLCAT
1499
1500
1501 #ifndef HAVE_STRLCPY
1502 size_t
1503 strlcpy(char *dst, const char *src, size_t siz)
1504 {
1505         register char *d = dst;
1506         register const char *s = src;
1507         register size_t n = siz;
1508
1509         /* Copy as many bytes as will fit */
1510         if (n != 0 && --n != 0) {
1511                 do {
1512                         if ((*d++ = *s++) == 0)
1513                                 break;
1514                 } while (--n != 0);
1515         }
1516
1517         /* Not enough room in dst, add NUL and traverse rest of src */
1518         if (n == 0) {
1519                 if (siz != 0)
1520                         *d = '\0';              /* NUL-terminate dst */
1521                 while (*s++)
1522                         ;
1523         }
1524
1525         return(s - src - 1);    /* count does not include NUL */
1526 }
1527
1528 #endif  // #ifndef HAVE_STRLCPY