2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // common.c -- misc functions used in client and server
31 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
34 char com_token[MAX_INPUTLINE];
36 const char **com_argv;
41 const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the
42 const char *gamedirname1;
43 const char *gamedirname2;
44 const char *gamescreenshotname;
45 const char *gameuserdirname;
46 char com_modname[MAX_OSPATH] = "";
50 ============================================================================
54 ============================================================================
58 float BuffBigFloat (const unsigned char *buffer)
66 u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
70 int BuffBigLong (const unsigned char *buffer)
72 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
75 short BuffBigShort (const unsigned char *buffer)
77 return (buffer[0] << 8) | buffer[1];
80 float BuffLittleFloat (const unsigned char *buffer)
88 u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
92 int BuffLittleLong (const unsigned char *buffer)
94 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
97 short BuffLittleShort (const unsigned char *buffer)
99 return (buffer[1] << 8) | buffer[0];
102 void StoreBigLong (unsigned char *buffer, unsigned int i)
104 buffer[0] = (i >> 24) & 0xFF;
105 buffer[1] = (i >> 16) & 0xFF;
106 buffer[2] = (i >> 8) & 0xFF;
107 buffer[3] = i & 0xFF;
110 void StoreBigShort (unsigned char *buffer, unsigned short i)
112 buffer[0] = (i >> 8) & 0xFF;
113 buffer[1] = i & 0xFF;
116 void StoreLittleLong (unsigned char *buffer, unsigned int i)
118 buffer[0] = i & 0xFF;
119 buffer[1] = (i >> 8) & 0xFF;
120 buffer[2] = (i >> 16) & 0xFF;
121 buffer[3] = (i >> 24) & 0xFF;
124 void StoreLittleShort (unsigned char *buffer, unsigned short i)
126 buffer[0] = i & 0xFF;
127 buffer[1] = (i >> 8) & 0xFF;
131 ============================================================================
135 ============================================================================
138 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
139 // and the initial and final xor values shown below... in other words, the
140 // CCITT standard CRC used by XMODEM
142 #define CRC_INIT_VALUE 0xffff
143 #define CRC_XOR_VALUE 0x0000
145 static unsigned short crctable[256] =
147 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
148 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
149 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
150 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
151 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
152 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
153 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
154 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
155 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
156 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
157 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
158 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
159 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
160 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
161 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
162 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
163 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
164 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
165 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
166 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
167 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
168 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
169 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
170 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
171 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
172 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
173 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
174 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
175 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
176 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
177 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
178 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
181 unsigned short CRC_Block(const unsigned char *data, size_t size)
183 unsigned short crc = CRC_INIT_VALUE;
185 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
186 return crc ^ CRC_XOR_VALUE;
189 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
191 unsigned short crc = CRC_INIT_VALUE;
193 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
194 return crc ^ CRC_XOR_VALUE;
198 static unsigned char chktbl[1024 + 4] =
200 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
201 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
202 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
203 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
204 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
205 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
206 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
207 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
208 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
209 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
210 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
211 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
212 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
213 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
214 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
215 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
216 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
217 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
218 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
219 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
220 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
221 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
222 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
223 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
224 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
225 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
226 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
227 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
228 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
229 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
230 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
231 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
233 // map checksum goes here
238 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
241 unsigned char chkb[60 + 4];
243 p = chktbl + (sequence % (sizeof(chktbl) - 8));
247 memcpy(chkb, base, length);
249 chkb[length] = (sequence & 0xff) ^ p[0];
250 chkb[length+1] = p[1];
251 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
252 chkb[length+3] = p[3];
254 return CRC_Block(chkb, length + 4) & 0xff;
258 ==============================================================================
262 Handles byte ordering and avoids alignment errors
263 ==============================================================================
270 void MSG_WriteChar (sizebuf_t *sb, int c)
274 buf = SZ_GetSpace (sb, 1);
278 void MSG_WriteByte (sizebuf_t *sb, int c)
282 buf = SZ_GetSpace (sb, 1);
286 void MSG_WriteShort (sizebuf_t *sb, int c)
290 buf = SZ_GetSpace (sb, 2);
295 void MSG_WriteLong (sizebuf_t *sb, int c)
299 buf = SZ_GetSpace (sb, 4);
301 buf[1] = (c>>8)&0xff;
302 buf[2] = (c>>16)&0xff;
306 void MSG_WriteFloat (sizebuf_t *sb, float f)
316 dat.l = LittleLong (dat.l);
318 SZ_Write (sb, (unsigned char *)&dat.l, 4);
321 void MSG_WriteString (sizebuf_t *sb, const char *s)
324 MSG_WriteChar (sb, 0);
326 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
329 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
332 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
335 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
338 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
340 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
343 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
346 MSG_WriteShort (sb, (int)(f + 0.5));
348 MSG_WriteShort (sb, (int)(f - 0.5));
351 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
353 MSG_WriteFloat (sb, f);
356 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
358 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
359 MSG_WriteCoord13i (sb, f);
360 else if (protocol == PROTOCOL_DARKPLACES1)
361 MSG_WriteCoord32f (sb, f);
362 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
363 MSG_WriteCoord16i (sb, f);
365 MSG_WriteCoord32f (sb, f);
368 void MSG_WriteVector (sizebuf_t *sb, const vec3_t v, protocolversion_t protocol)
370 MSG_WriteCoord (sb, v[0], protocol);
371 MSG_WriteCoord (sb, v[1], protocol);
372 MSG_WriteCoord (sb, v[2], protocol);
375 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
376 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
379 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
381 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
384 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
387 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
389 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
392 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
394 MSG_WriteFloat (sb, f);
397 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
399 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
400 MSG_WriteAngle8i (sb, f);
402 MSG_WriteAngle16i (sb, f);
409 void MSG_InitReadBuffer (sizebuf_t *buf, unsigned char *data, int size)
411 memset(buf, 0, sizeof(*buf));
413 buf->maxsize = buf->cursize = size;
414 MSG_BeginReading(buf);
417 void MSG_BeginReading(sizebuf_t *sb)
423 int MSG_ReadLittleShort(sizebuf_t *sb)
425 if (sb->readcount+2 > sb->cursize)
431 return (short)(sb->data[sb->readcount-2] | (sb->data[sb->readcount-1]<<8));
434 int MSG_ReadBigShort (sizebuf_t *sb)
436 if (sb->readcount+2 > sb->cursize)
442 return (short)((sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]);
445 int MSG_ReadLittleLong (sizebuf_t *sb)
447 if (sb->readcount+4 > sb->cursize)
453 return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
456 int MSG_ReadBigLong (sizebuf_t *sb)
458 if (sb->readcount+4 > sb->cursize)
464 return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
467 float MSG_ReadLittleFloat (sizebuf_t *sb)
474 if (sb->readcount+4 > sb->cursize)
480 dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
484 float MSG_ReadBigFloat (sizebuf_t *sb)
491 if (sb->readcount+4 > sb->cursize)
497 dat.l = (sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1];
501 char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring)
505 // read string into sbfer, but only store as many characters as will fit
506 while ((c = MSG_ReadByte(sb)) > 0)
507 if (l < maxstring - 1)
513 int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
516 for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++)
521 float MSG_ReadCoord13i (sizebuf_t *sb)
523 return MSG_ReadLittleShort(sb) * (1.0/8.0);
526 float MSG_ReadCoord16i (sizebuf_t *sb)
528 return (signed short) MSG_ReadLittleShort(sb);
531 float MSG_ReadCoord32f (sizebuf_t *sb)
533 return MSG_ReadLittleFloat(sb);
536 float MSG_ReadCoord (sizebuf_t *sb, protocolversion_t protocol)
538 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
539 return MSG_ReadCoord13i(sb);
540 else if (protocol == PROTOCOL_DARKPLACES1)
541 return MSG_ReadCoord32f(sb);
542 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
543 return MSG_ReadCoord16i(sb);
545 return MSG_ReadCoord32f(sb);
548 void MSG_ReadVector (sizebuf_t *sb, vec3_t v, protocolversion_t protocol)
550 v[0] = MSG_ReadCoord(sb, protocol);
551 v[1] = MSG_ReadCoord(sb, protocol);
552 v[2] = MSG_ReadCoord(sb, protocol);
555 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
556 float MSG_ReadAngle8i (sizebuf_t *sb)
558 return (signed char) MSG_ReadByte (sb) * (360.0/256.0);
561 float MSG_ReadAngle16i (sizebuf_t *sb)
563 return (signed short)MSG_ReadShort (sb) * (360.0/65536.0);
566 float MSG_ReadAngle32f (sizebuf_t *sb)
568 return MSG_ReadFloat (sb);
571 float MSG_ReadAngle (sizebuf_t *sb, protocolversion_t protocol)
573 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
574 return MSG_ReadAngle8i (sb);
576 return MSG_ReadAngle16i (sb);
580 //===========================================================================
582 void SZ_Clear (sizebuf_t *buf)
587 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
591 if (buf->cursize + length > buf->maxsize)
593 if (!buf->allowoverflow)
594 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
596 if (length > buf->maxsize)
597 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
599 buf->overflowed = true;
600 Con_Print("SZ_GetSpace: overflow\n");
604 data = buf->data + buf->cursize;
605 buf->cursize += length;
610 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
612 memcpy (SZ_GetSpace(buf,length),data,length);
615 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
616 // attention, it has been eradicated from here, its only (former) use in
617 // all of darkplaces.
619 static const char *hexchar = "0123456789ABCDEF";
620 void Com_HexDumpToConsole(const unsigned char *data, int size)
624 char *cur, *flushpointer;
625 const unsigned char *d;
627 flushpointer = text + 512;
628 for (i = 0;i < size;)
635 *cur++ = hexchar[(i >> 12) & 15];
636 *cur++ = hexchar[(i >> 8) & 15];
637 *cur++ = hexchar[(i >> 4) & 15];
638 *cur++ = hexchar[(i >> 0) & 15];
641 for (j = 0;j < 16;j++)
645 *cur++ = hexchar[(d[j] >> 4) & 15];
646 *cur++ = hexchar[(d[j] >> 0) & 15];
657 for (j = 0;j < 16;j++)
661 // color change prefix character has to be treated specially
662 if (d[j] == STRING_COLOR_TAG)
664 *cur++ = STRING_COLOR_TAG;
665 *cur++ = STRING_COLOR_TAG;
667 else if (d[j] >= (unsigned char) ' ')
677 if (cur >= flushpointer || i >= size)
686 void SZ_HexDumpToConsole(const sizebuf_t *buf)
688 Com_HexDumpToConsole(buf->data, buf->cursize);
692 //============================================================================
698 Word wraps a string. The wordWidth function is guaranteed to be called exactly
699 once for each word in the string, so it may be stateful, no idea what that
700 would be good for any more. At the beginning of the string, it will be called
701 for the char 0 to initialize a clean state, and then once with the string " "
702 (a space) so the routine knows how long a space is.
704 In case no single character fits into the given width, the wordWidth function
705 must return the width of exactly one character.
707 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
709 The sum of the return values of the processLine function will be returned.
712 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
714 // Logic is as follows:
716 // For each word or whitespace:
717 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
718 // Space found? Always add it to the current line, no matter if it fits.
719 // Word found? Check if current line + current word fits.
720 // If it fits, append it. Continue.
721 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
723 qboolean isContinuation = false;
725 const char *startOfLine = string;
726 const char *cursor = string;
727 const char *end = string + length;
728 float spaceUsedInLine = 0;
729 float spaceUsedForWord;
735 wordWidth(passthroughCW, NULL, &dummy, -1);
737 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
741 char ch = (cursor < end) ? *cursor : 0;
744 case 0: // end of string
745 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
747 case '\n': // end of line
748 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
749 isContinuation = false;
751 startOfLine = cursor;
755 spaceUsedInLine += spaceWidth;
759 while(cursor + wordLen < end)
761 switch(cursor[wordLen])
773 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
774 if(wordLen < 1) // cannot happen according to current spec of wordWidth
777 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
779 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
781 // we can simply append it
783 spaceUsedInLine += spaceUsedForWord;
787 // output current line
788 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
789 isContinuation = true;
790 startOfLine = cursor;
792 spaceUsedInLine = continuationWidth + spaceUsedForWord;
801 qboolean isContinuation = false;
802 float currentWordSpace = 0;
803 const char *currentWord = 0;
804 float minReserve = 0;
806 float spaceUsedInLine = 0;
807 const char *currentLine = 0;
808 const char *currentLineEnd = 0;
809 float currentLineFinalWhitespace = 0;
813 minReserve = charWidth(passthroughCW, 0);
814 minReserve += charWidth(passthroughCW, ' ');
816 if(maxWidth < continuationWidth + minReserve)
817 maxWidth = continuationWidth + minReserve;
819 charWidth(passthroughCW, 0);
821 for(p = string; p < string + length; ++p)
824 float w = charWidth(passthroughCW, c);
829 currentWordSpace = 0;
835 spaceUsedInLine = isContinuation ? continuationWidth : 0;
841 // 1. I can add the word AND a space - then just append it.
842 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
844 currentLineEnd = p; // note: space not included here
845 currentLineFinalWhitespace = w;
846 spaceUsedInLine += currentWordSpace + w;
848 // 2. I can just add the word - then append it, output current line and go to next one.
849 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
851 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
853 isContinuation = true;
855 // 3. Otherwise, output current line and go to next one, where I can add the word.
856 else if(continuationWidth + currentWordSpace + w <= maxWidth)
859 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
860 currentLine = currentWord;
861 spaceUsedInLine = continuationWidth + currentWordSpace + w;
863 currentLineFinalWhitespace = w;
864 isContinuation = true;
866 // 4. We can't even do that? Then output both current and next word as new lines.
871 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
872 isContinuation = true;
874 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
876 isContinuation = true;
882 // 1. I can add the word - then do it.
883 if(spaceUsedInLine + currentWordSpace <= maxWidth)
885 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
887 // 2. Otherwise, output current line, next one and make tabula rasa.
892 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
893 isContinuation = true;
895 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
899 isContinuation = false;
903 currentWordSpace += w;
905 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
907 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
910 // this word cannot join ANY line...
911 // so output the current line...
914 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
915 isContinuation = true;
918 // then this word's beginning...
921 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
922 float pieceWidth = maxWidth - continuationWidth;
923 const char *pos = currentWord;
924 currentWordSpace = 0;
926 // reset the char width function to a state where no kerning occurs (start of word)
927 charWidth(passthroughCW, ' ');
930 float w = charWidth(passthroughCW, *pos);
931 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
933 // print everything until it
934 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
937 currentWordSpace = 0;
939 currentWordSpace += w;
942 // now we have a currentWord that fits... set up its next line
943 // currentWordSpace has been set
944 // currentWord has been set
945 spaceUsedInLine = continuationWidth;
946 currentLine = currentWord;
948 isContinuation = true;
952 // we have a guarantee that it will fix (see if clause)
953 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
955 // and use the rest of this word as new start of a line
956 currentWordSpace = w;
958 spaceUsedInLine = continuationWidth;
961 isContinuation = true;
970 currentWordSpace = 0;
973 if(currentLine) // Same procedure as \n
975 // Can I append the current word?
976 if(spaceUsedInLine + currentWordSpace <= maxWidth)
977 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
982 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
983 isContinuation = true;
985 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
995 COM_ParseToken_Simple
997 Parse a token out of a string
1000 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
1004 const char *data = *datapointer;
1011 *datapointer = NULL;
1021 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1026 *datapointer = NULL;
1031 // handle Windows line ending
1032 if (data[0] == '\r' && data[1] == '\n')
1035 if (parsecomments && data[0] == '/' && data[1] == '/')
1038 while (*data && *data != '\n' && *data != '\r')
1042 else if (parsecomments && data[0] == '/' && data[1] == '*')
1046 while (*data && (data[0] != '*' || data[1] != '/'))
1054 else if (*data == '\"')
1057 for (data++;*data && *data != '\"';data++)
1060 if (*data == '\\' && parsebackslash)
1069 if (len < (int)sizeof(com_token) - 1)
1070 com_token[len++] = c;
1075 *datapointer = data;
1078 else if (*data == '\r')
1080 // translate Mac line ending to UNIX
1081 com_token[len++] = '\n';data++;
1083 *datapointer = data;
1086 else if (*data == '\n')
1089 com_token[len++] = *data++;
1091 *datapointer = data;
1097 for (;!ISWHITESPACE(*data);data++)
1098 if (len < (int)sizeof(com_token) - 1)
1099 com_token[len++] = *data;
1101 *datapointer = data;
1108 COM_ParseToken_QuakeC
1110 Parse a token out of a string
1113 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1117 const char *data = *datapointer;
1124 *datapointer = NULL;
1134 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1139 *datapointer = NULL;
1144 // handle Windows line ending
1145 if (data[0] == '\r' && data[1] == '\n')
1148 if (data[0] == '/' && data[1] == '/')
1151 while (*data && *data != '\n' && *data != '\r')
1155 else if (data[0] == '/' && data[1] == '*')
1159 while (*data && (data[0] != '*' || data[1] != '/'))
1167 else if (*data == '\"' || *data == '\'')
1171 for (data++;*data && *data != quote;data++)
1183 if (len < (int)sizeof(com_token) - 1)
1184 com_token[len++] = c;
1189 *datapointer = data;
1192 else if (*data == '\r')
1194 // translate Mac line ending to UNIX
1195 com_token[len++] = '\n';data++;
1197 *datapointer = data;
1200 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1203 com_token[len++] = *data++;
1205 *datapointer = data;
1211 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1212 if (len < (int)sizeof(com_token) - 1)
1213 com_token[len++] = *data;
1215 *datapointer = data;
1222 COM_ParseToken_VM_Tokenize
1224 Parse a token out of a string
1227 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1231 const char *data = *datapointer;
1238 *datapointer = NULL;
1248 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1253 *datapointer = NULL;
1258 // handle Windows line ending
1259 if (data[0] == '\r' && data[1] == '\n')
1262 if (data[0] == '/' && data[1] == '/')
1265 while (*data && *data != '\n' && *data != '\r')
1269 else if (data[0] == '/' && data[1] == '*')
1273 while (*data && (data[0] != '*' || data[1] != '/'))
1281 else if (*data == '\"' || *data == '\'')
1285 for (data++;*data && *data != quote;data++)
1297 if (len < (int)sizeof(com_token) - 1)
1298 com_token[len++] = c;
1303 *datapointer = data;
1306 else if (*data == '\r')
1308 // translate Mac line ending to UNIX
1309 com_token[len++] = '\n';data++;
1311 *datapointer = data;
1314 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1317 com_token[len++] = *data++;
1319 *datapointer = data;
1325 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1326 if (len < (int)sizeof(com_token) - 1)
1327 com_token[len++] = *data;
1329 *datapointer = data;
1336 COM_ParseToken_Console
1338 Parse a token out of a string, behaving like the qwcl console
1341 int COM_ParseToken_Console(const char **datapointer)
1344 const char *data = *datapointer;
1351 *datapointer = NULL;
1357 for (;ISWHITESPACE(*data);data++)
1362 *datapointer = NULL;
1367 if (*data == '/' && data[1] == '/')
1370 while (*data && *data != '\n' && *data != '\r')
1374 else if (*data == '\"')
1377 for (data++;*data && *data != '\"';data++)
1379 // allow escaped " and \ case
1380 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1382 if (len < (int)sizeof(com_token) - 1)
1383 com_token[len++] = *data;
1388 *datapointer = data;
1393 for (;!ISWHITESPACE(*data);data++)
1394 if (len < (int)sizeof(com_token) - 1)
1395 com_token[len++] = *data;
1397 *datapointer = data;
1408 Returns the position (1 to argc-1) in the program's argument list
1409 where the given parameter apears, or 0 if not present
1412 int COM_CheckParm (const char *parm)
1416 for (i=1 ; i<com_argc ; i++)
1419 continue; // NEXTSTEP sometimes clears appkit vars.
1420 if (!strcmp (parm,com_argv[i]))
1427 //===========================================================================
1431 gamemode_t com_startupgamemode;
1432 gamemode_t com_startupgamegroup;
1434 typedef struct gamemode_info_s
1436 gamemode_t mode; // this gamemode
1437 gamemode_t group; // different games with same group can switch automatically when gamedirs change
1438 const char* prog_name; // not null
1439 const char* cmdline; // not null
1440 const char* gamename; // not null
1441 const char* gamenetworkfiltername; // not null
1442 const char* gamedirname1; // not null
1443 const char* gamedirname2; // null
1444 const char* gamescreenshotname; // not nul
1445 const char* gameuserdirname; // not null
1448 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1449 {// game basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option
1450 { GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1451 { GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1452 { GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1453 { GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1454 { GAME_QUOTH, GAME_NORMAL, "quoth", "-quoth", "Darkplaces-Quoth", "Darkplaces-Quoth", "id1", "quoth", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it
1455 { GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1456 { GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
1457 { GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1458 { GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1459 { GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1460 { GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1461 { GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1462 { GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1463 { GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1464 { GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1465 { GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1466 { GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1467 { GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1468 { GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1469 { GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "Defeat_In_Detail_2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1470 { GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1471 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1472 { GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1473 { GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
1474 { GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1475 { GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1476 { GAME_STEELSTORM2, GAME_STEELSTORM2, "steelstorm2", "-steelstorm2", "Steel Storm 2", "Steel_Storm_2", "gamedata", NULL, "ss2", "steelstorm2" }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2
1477 { GAME_SSAMMO, GAME_SSAMMO, "steelstorm-ammo", "-steelstormammo", "Steel Storm A.M.M.O.", "Steel_Storm_A.M.M.O.", "gamedata", NULL, "ssammo", "steelstorm-ammo" }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O.
1478 { GAME_STEELSTORMREVENANTS, GAME_STEELSTORMREVENANTS, "steelstorm-revenants", "-steelstormrev", "Steel Storm: Revenants", "Steel_Storm_Revenants", "base", NULL, "ssrev", "steelstorm-revenants" }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
1479 { GAME_TOMESOFMEPHISTOPHELES, GAME_TOMESOFMEPHISTOPHELES, "tomesofmephistopheles","-tomesofmephistopheles", "Tomes of Mephistopheles", "Tomes_of_Mephistopheles", "gamedata", NULL, "tom", "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles
1480 { GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
1481 { GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1482 { GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
1485 static void COM_SetGameType(int index);
1486 void COM_InitGameType (void)
1488 char name [MAX_OSPATH];
1493 COM_ToLowerString(FORCEGAME, name, sizeof (name));
1495 // check executable filename for keywords, but do it SMARTLY - only check the last path element
1496 FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
1497 COM_ToLowerString(name, name, sizeof (name));
1499 for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1500 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1503 // check commandline options for keywords
1504 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1505 if (COM_CheckParm (gamemode_info[i].cmdline))
1508 com_startupgamemode = gamemode_info[index].mode;
1509 com_startupgamegroup = gamemode_info[index].group;
1510 COM_SetGameType(index);
1513 void COM_ChangeGameTypeForGameDirs(void)
1517 // this will not not change the gamegroup
1518 // first check if a base game (single gamedir) matches
1519 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1521 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1527 // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1530 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1532 if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1539 // we now have a good guess at which game this is meant to be...
1540 if (index >= 0 && gamemode != gamemode_info[index].mode)
1541 COM_SetGameType(index);
1544 static void COM_SetGameType(int index)
1546 static char gamenetworkfilternamebuffer[64];
1548 if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1550 gamemode = gamemode_info[index].mode;
1551 gamename = gamemode_info[index].gamename;
1552 gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
1553 gamedirname1 = gamemode_info[index].gamedirname1;
1554 gamedirname2 = gamemode_info[index].gamedirname2;
1555 gamescreenshotname = gamemode_info[index].gamescreenshotname;
1556 gameuserdirname = gamemode_info[index].gameuserdirname;
1558 if (gamemode == com_startupgamemode)
1560 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1561 gamename = gamenetworkfiltername = com_argv[t+1];
1562 if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < com_argc)
1563 gamenetworkfiltername = com_argv[t+1];
1564 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1565 gamedirname1 = com_argv[t+1];
1566 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1567 gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1568 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1569 gamescreenshotname = com_argv[t+1];
1570 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1571 gameuserdirname = com_argv[t+1];
1574 if (gamedirname2 && gamedirname2[0])
1575 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1577 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1578 for (i = 0;i < fs_numgamedirs;i++)
1581 Con_Printf(", with mod gamedirs");
1582 Con_Printf(" %s", fs_gamedirs[i]);
1586 if (strchr(gamenetworkfiltername, ' '))
1589 // if there are spaces in the game's network filter name it would
1590 // cause parse errors in getservers in dpmaster, so we need to replace
1591 // them with _ characters
1592 strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
1593 while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
1595 gamenetworkfiltername = gamenetworkfilternamebuffer;
1598 Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
1607 void COM_Init_Commands (void)
1610 char com_cmdline[MAX_INPUTLINE];
1612 Cvar_RegisterVariable (®istered);
1613 Cvar_RegisterVariable (&cmdline);
1615 // reconstitute the command line for the cmdline externally visible cvar
1617 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1620 if (strstr(com_argv[j], " "))
1622 // arg contains whitespace, store quotes around it
1623 // This condition checks whether we can allow to put
1624 // in two quote characters.
1625 if (n >= ((int)sizeof(com_cmdline) - 2))
1627 com_cmdline[n++] = '\"';
1628 // This condition checks whether we can allow one
1629 // more character and a quote character.
1630 while ((n < ((int)sizeof(com_cmdline) - 2)) && com_argv[j][i])
1631 // FIXME: Doesn't quote special characters.
1632 com_cmdline[n++] = com_argv[j][i++];
1633 com_cmdline[n++] = '\"';
1637 // This condition checks whether we can allow one
1639 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1640 com_cmdline[n++] = com_argv[j][i++];
1642 if (n < ((int)sizeof(com_cmdline) - 1))
1643 com_cmdline[n++] = ' ';
1648 Cvar_Set ("cmdline", com_cmdline);
1655 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1658 char *va(char *buf, size_t buflen, const char *format, ...)
1662 va_start (argptr, format);
1663 dpvsnprintf (buf, buflen, format,argptr);
1670 //======================================
1672 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1678 # define snprintf _snprintf
1679 # define vsnprintf _vsnprintf
1683 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1688 va_start (args, format);
1689 result = dpvsnprintf (buffer, buffersize, format, args);
1696 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1700 #if _MSC_VER >= 1400
1701 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1703 result = vsnprintf (buffer, buffersize, format, args);
1705 if (result < 0 || (size_t)result >= buffersize)
1707 buffer[buffersize - 1] = '\0';
1715 //======================================
1717 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1722 if(utf8_enable.integer)
1725 while(*in && size_out > 1)
1728 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1729 ch = u8_tolower(ch);
1730 n = u8_fromchar(ch, out, size_out);
1739 while (*in && size_out > 1)
1741 if (*in >= 'A' && *in <= 'Z')
1742 *out++ = *in++ + 'a' - 'A';
1750 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1755 if(utf8_enable.integer)
1758 while(*in && size_out > 1)
1761 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1762 ch = u8_toupper(ch);
1763 n = u8_fromchar(ch, out, size_out);
1772 while (*in && size_out > 1)
1774 if (*in >= 'a' && *in <= 'z')
1775 *out++ = *in++ + 'A' - 'a';
1783 int COM_StringBeginsWith(const char *s, const char *match)
1785 for (;*s && *match;s++, match++)
1791 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1793 int argc, commentprefixlength;
1797 tokenbufend = tokenbuf + tokenbufsize;
1799 commentprefixlength = 0;
1801 commentprefixlength = (int)strlen(commentprefix);
1802 while (*l && *l != '\n' && *l != '\r')
1804 if (!ISWHITESPACE(*l))
1806 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1808 while (*l && *l != '\n' && *l != '\r')
1812 if (argc >= maxargc)
1814 argv[argc++] = tokenbuf;
1818 while (*l && *l != '"')
1820 if (tokenbuf >= tokenbufend)
1829 while (!ISWHITESPACE(*l))
1831 if (tokenbuf >= tokenbufend)
1836 if (tokenbuf >= tokenbufend)
1857 COM_StringLengthNoColors
1859 calculates the visible width of a color coded string.
1861 *valid is filled with TRUE if the string is a valid colored string (that is, if
1862 it does not end with an unfinished color code). If it gets filled with FALSE, a
1863 fix would be adding a STRING_COLOR_TAG at the end of the string.
1865 valid can be set to NULL if the caller doesn't care.
1867 For size_s, specify the maximum number of characters from s to use, or 0 to use
1868 all characters until the zero terminator.
1872 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1874 const char *end = size_s ? (s + size_s) : NULL;
1878 switch((s == end) ? 0 : *s)
1884 case STRING_COLOR_TAG:
1886 switch((s == end) ? 0 : *s)
1888 case STRING_COLOR_RGB_TAG_CHAR:
1889 if (s+1 != end && isxdigit(s[1]) &&
1890 s+2 != end && isxdigit(s[2]) &&
1891 s+3 != end && isxdigit(s[3]) )
1896 ++len; // STRING_COLOR_TAG
1897 ++len; // STRING_COLOR_RGB_TAG_CHAR
1899 case 0: // ends with unfinished color code!
1904 case STRING_COLOR_TAG: // escaped ^
1907 case '0': case '1': case '2': case '3': case '4':
1908 case '5': case '6': case '7': case '8': case '9': // color code
1910 default: // not a color code
1911 ++len; // STRING_COLOR_TAG
1912 ++len; // the character
1927 COM_StringDecolorize
1929 removes color codes from a string.
1931 If escape_carets is true, the resulting string will be safe for printing. If
1932 escape_carets is false, the function will just strip color codes (for logging
1935 If the output buffer size did not suffice for converting, the function returns
1936 FALSE. Generally, if escape_carets is false, the output buffer needs
1937 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1938 bytes. In any case, the function makes sure that the resulting string is
1941 For size_in, specify the maximum number of characters from in to use, or 0 to use
1942 all characters until the zero terminator.
1946 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1948 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1949 const char *end = size_in ? (in + size_in) : NULL;
1954 switch((in == end) ? 0 : *in)
1959 case STRING_COLOR_TAG:
1961 switch((in == end) ? 0 : *in)
1963 case STRING_COLOR_RGB_TAG_CHAR:
1964 if (in+1 != end && isxdigit(in[1]) &&
1965 in+2 != end && isxdigit(in[2]) &&
1966 in+3 != end && isxdigit(in[3]) )
1971 APPEND(STRING_COLOR_TAG);
1973 APPEND(STRING_COLOR_TAG);
1974 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1976 case 0: // ends with unfinished color code!
1977 APPEND(STRING_COLOR_TAG);
1978 // finish the code by appending another caret when escaping
1980 APPEND(STRING_COLOR_TAG);
1983 case STRING_COLOR_TAG: // escaped ^
1984 APPEND(STRING_COLOR_TAG);
1985 // append a ^ twice when escaping
1987 APPEND(STRING_COLOR_TAG);
1989 case '0': case '1': case '2': case '3': case '4':
1990 case '5': case '6': case '7': case '8': case '9': // color code
1992 default: // not a color code
1993 APPEND(STRING_COLOR_TAG);
2008 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
2014 keylength = strlen(key);
2015 if (valuelength < 1 || !value)
2017 Con_Printf("InfoString_GetValue: no room in value\n");
2021 if (strchr(key, '\\'))
2023 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
2026 if (strchr(key, '\"'))
2028 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2033 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2036 while (buffer[pos] == '\\')
2038 if (!memcmp(buffer + pos+1, key, keylength) &&
2039 (buffer[pos+1 + keylength] == 0 ||
2040 buffer[pos+1 + keylength] == '\\'))
2042 pos += 1 + keylength; // Skip \key
2043 if (buffer[pos] == '\\') pos++; // Skip \ before value.
2044 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2045 value[j] = buffer[pos+j];
2049 if (buffer[pos] == '\\') pos++; // Skip \ before value.
2050 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2051 if (buffer[pos] == '\\') pos++; // Skip \ before value.
2052 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2054 // if we reach this point the key was not found
2058 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2066 keylength = strlen(key);
2067 if (strchr(key, '\\') || strchr(value, '\\'))
2069 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2072 if (strchr(key, '\"') || strchr(value, '\"'))
2074 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2079 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2082 while (buffer[pos] == '\\')
2084 if (!memcmp(buffer + pos+1, key, keylength) &&
2085 (buffer[pos+1 + keylength] == 0 ||
2086 buffer[pos+1 + keylength] == '\\'))
2088 if (buffer[pos] == '\\') pos++; // Skip \ before value.
2089 for (;buffer[pos] && buffer[pos] != '\\';pos++);
2090 if (buffer[pos] == '\\') pos++; // Skip \ before value.
2091 for (;buffer[pos] && buffer[pos] != '\\';pos++);
2093 // if we found the key, find the end of it because we will be replacing it
2095 if (buffer[pos] == '\\')
2097 pos2 += 1 + keylength; // Skip \key
2098 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
2099 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2101 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2103 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2108 // set the key/value and append the remaining text
2109 char tempbuffer[MAX_INPUTLINE];
2110 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2111 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2115 // just remove the key from the text
2116 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2120 void InfoString_Print(char *buffer)
2123 char key[MAX_INPUTLINE];
2124 char value[MAX_INPUTLINE];
2127 if (*buffer != '\\')
2129 Con_Printf("InfoString_Print: corrupt string\n");
2132 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2133 if (i < (int)sizeof(key)-1)
2136 if (*buffer != '\\')
2138 Con_Printf("InfoString_Print: corrupt string\n");
2141 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2142 if (i < (int)sizeof(value)-1)
2143 value[i++] = *buffer;
2145 // empty value is an error case
2146 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2150 //========================================================
2151 // strlcat and strlcpy, from OpenBSD
2154 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2156 * Permission to use, copy, modify, and distribute this software for any
2157 * purpose with or without fee is hereby granted, provided that the above
2158 * copyright notice and this permission notice appear in all copies.
2160 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2161 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2162 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2163 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2164 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2165 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2166 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2169 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2170 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2173 #ifndef HAVE_STRLCAT
2175 strlcat(char *dst, const char *src, size_t siz)
2177 register char *d = dst;
2178 register const char *s = src;
2179 register size_t n = siz;
2182 /* Find the end of dst and adjust bytes left but don't go past end */
2183 while (n-- != 0 && *d != '\0')
2189 return(dlen + strlen(s));
2190 while (*s != '\0') {
2199 return(dlen + (s - src)); /* count does not include NUL */
2201 #endif // #ifndef HAVE_STRLCAT
2204 #ifndef HAVE_STRLCPY
2206 strlcpy(char *dst, const char *src, size_t siz)
2208 register char *d = dst;
2209 register const char *s = src;
2210 register size_t n = siz;
2212 /* Copy as many bytes as will fit */
2213 if (n != 0 && --n != 0) {
2215 if ((*d++ = *s++) == 0)
2220 /* Not enough room in dst, add NUL and traverse rest of src */
2223 *d = '\0'; /* NUL-terminate dst */
2228 return(s - src - 1); /* count does not include NUL */
2231 #endif // #ifndef HAVE_STRLCPY
2233 void FindFraction(double val, int *num, int *denom, int denomMax)
2238 bestdiff = fabs(val);
2242 for(i = 1; i <= denomMax; ++i)
2244 int inum = (int) floor(0.5 + val * i);
2245 double diff = fabs(val - inum / (double)i);
2255 // decodes an XPM from C syntax
2256 char **XPM_DecodeString(const char *in)
2258 static char *tokens[257];
2259 static char lines[257][512];
2262 // skip until "{" token
2263 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2265 // now, read in succession: string, comma-or-}
2266 while(COM_ParseToken_QuakeC(&in, false))
2268 tokens[line] = lines[line];
2269 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2270 if(!COM_ParseToken_QuakeC(&in, false))
2272 if(!strcmp(com_token, "}"))
2274 if(strcmp(com_token, ","))
2276 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2283 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2284 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2286 unsigned char i0 = (bytes > 0) ? in[0] : 0;
2287 unsigned char i1 = (bytes > 1) ? in[1] : 0;
2288 unsigned char i2 = (bytes > 2) ? in[2] : 0;
2289 unsigned char o0 = base64[i0 >> 2];
2290 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2291 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2292 unsigned char o3 = base64[i2 & 077];
2293 out[0] = (bytes > 0) ? o0 : '?';
2294 out[1] = (bytes > 0) ? o1 : '?';
2295 out[2] = (bytes > 1) ? o2 : '=';
2296 out[3] = (bytes > 2) ? o3 : '=';
2299 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2302 // expand the out-buffer
2303 blocks = (buflen + 2) / 3;
2304 if(blocks*4 > outbuflen)
2306 for(i = blocks; i > 0; )
2309 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));