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 *gamedirname1;
42 const char *gamedirname2;
43 const char *gamescreenshotname;
44 const char *gameuserdirname;
45 char com_modname[MAX_OSPATH] = "";
49 ============================================================================
53 ============================================================================
57 float BuffBigFloat (const unsigned char *buffer)
65 u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
69 int BuffBigLong (const unsigned char *buffer)
71 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
74 short BuffBigShort (const unsigned char *buffer)
76 return (buffer[0] << 8) | buffer[1];
79 float BuffLittleFloat (const unsigned char *buffer)
87 u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
91 int BuffLittleLong (const unsigned char *buffer)
93 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
96 short BuffLittleShort (const unsigned char *buffer)
98 return (buffer[1] << 8) | buffer[0];
101 void StoreBigLong (unsigned char *buffer, unsigned int i)
103 buffer[0] = (i >> 24) & 0xFF;
104 buffer[1] = (i >> 16) & 0xFF;
105 buffer[2] = (i >> 8) & 0xFF;
106 buffer[3] = i & 0xFF;
109 void StoreBigShort (unsigned char *buffer, unsigned short i)
111 buffer[0] = (i >> 8) & 0xFF;
112 buffer[1] = i & 0xFF;
115 void StoreLittleLong (unsigned char *buffer, unsigned int i)
117 buffer[0] = i & 0xFF;
118 buffer[1] = (i >> 8) & 0xFF;
119 buffer[2] = (i >> 16) & 0xFF;
120 buffer[3] = (i >> 24) & 0xFF;
123 void StoreLittleShort (unsigned char *buffer, unsigned short i)
125 buffer[0] = i & 0xFF;
126 buffer[1] = (i >> 8) & 0xFF;
130 ============================================================================
134 ============================================================================
137 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
138 // and the initial and final xor values shown below... in other words, the
139 // CCITT standard CRC used by XMODEM
141 #define CRC_INIT_VALUE 0xffff
142 #define CRC_XOR_VALUE 0x0000
144 static unsigned short crctable[256] =
146 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
147 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
148 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
149 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
150 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
151 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
152 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
153 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
154 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
155 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
156 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
157 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
158 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
159 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
160 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
161 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
162 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
163 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
164 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
165 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
166 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
167 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
168 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
169 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
170 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
171 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
172 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
173 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
174 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
175 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
176 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
177 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
180 unsigned short CRC_Block(const unsigned char *data, size_t size)
182 unsigned short crc = CRC_INIT_VALUE;
184 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
185 return crc ^ CRC_XOR_VALUE;
188 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
190 unsigned short crc = CRC_INIT_VALUE;
192 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
193 return crc ^ CRC_XOR_VALUE;
197 static unsigned char chktbl[1024 + 4] =
199 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
200 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
201 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
202 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
203 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
204 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
205 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
206 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
207 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
208 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
209 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
210 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
211 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
212 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
213 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
214 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
215 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
216 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
217 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
218 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
219 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
220 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
221 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
222 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
223 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
224 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
225 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
226 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
227 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
228 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
229 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
230 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
232 // map checksum goes here
237 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
240 unsigned char chkb[60 + 4];
242 p = chktbl + (sequence % (sizeof(chktbl) - 8));
246 memcpy(chkb, base, length);
248 chkb[length] = (sequence & 0xff) ^ p[0];
249 chkb[length+1] = p[1];
250 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
251 chkb[length+3] = p[3];
253 return CRC_Block(chkb, length + 4) & 0xff;
257 ==============================================================================
261 Handles byte ordering and avoids alignment errors
262 ==============================================================================
269 void MSG_WriteChar (sizebuf_t *sb, int c)
273 buf = SZ_GetSpace (sb, 1);
277 void MSG_WriteByte (sizebuf_t *sb, int c)
281 buf = SZ_GetSpace (sb, 1);
285 void MSG_WriteShort (sizebuf_t *sb, int c)
289 buf = SZ_GetSpace (sb, 2);
294 void MSG_WriteLong (sizebuf_t *sb, int c)
298 buf = SZ_GetSpace (sb, 4);
300 buf[1] = (c>>8)&0xff;
301 buf[2] = (c>>16)&0xff;
305 void MSG_WriteFloat (sizebuf_t *sb, float f)
315 dat.l = LittleLong (dat.l);
317 SZ_Write (sb, (unsigned char *)&dat.l, 4);
320 void MSG_WriteString (sizebuf_t *sb, const char *s)
323 MSG_WriteChar (sb, 0);
325 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
328 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
331 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
334 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
337 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
339 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
342 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
345 MSG_WriteShort (sb, (int)(f + 0.5));
347 MSG_WriteShort (sb, (int)(f - 0.5));
350 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
352 MSG_WriteFloat (sb, f);
355 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
357 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
358 MSG_WriteCoord13i (sb, f);
359 else if (protocol == PROTOCOL_DARKPLACES1)
360 MSG_WriteCoord32f (sb, f);
361 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
362 MSG_WriteCoord16i (sb, f);
364 MSG_WriteCoord32f (sb, f);
367 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
369 MSG_WriteCoord (sb, v[0], protocol);
370 MSG_WriteCoord (sb, v[1], protocol);
371 MSG_WriteCoord (sb, v[2], protocol);
374 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
375 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
378 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
380 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
383 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
386 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
388 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
391 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
393 MSG_WriteFloat (sb, f);
396 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
398 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)
399 MSG_WriteAngle8i (sb, f);
401 MSG_WriteAngle16i (sb, f);
408 qboolean msg_badread;
410 void MSG_BeginReading (void)
416 int MSG_ReadLittleShort (void)
418 if (msg_readcount+2 > net_message.cursize)
424 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
427 int MSG_ReadBigShort (void)
429 if (msg_readcount+2 > net_message.cursize)
435 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
438 int MSG_ReadLittleLong (void)
440 if (msg_readcount+4 > net_message.cursize)
446 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);
449 int MSG_ReadBigLong (void)
451 if (msg_readcount+4 > net_message.cursize)
457 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];
460 float MSG_ReadLittleFloat (void)
467 if (msg_readcount+4 > net_message.cursize)
473 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);
477 float MSG_ReadBigFloat (void)
484 if (msg_readcount+4 > net_message.cursize)
490 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];
494 char *MSG_ReadString (void)
496 static char string[MAX_INPUTLINE];
497 const int maxstring = sizeof(string);
499 // read string into buffer, but only store as many characters as will fit
500 while ((c = MSG_ReadByte()) > 0)
501 if (l < maxstring - 1)
507 int MSG_ReadBytes (int numbytes, unsigned char *out)
510 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
515 float MSG_ReadCoord13i (void)
517 return MSG_ReadLittleShort() * (1.0/8.0);
520 float MSG_ReadCoord16i (void)
522 return (signed short) MSG_ReadLittleShort();
525 float MSG_ReadCoord32f (void)
527 return MSG_ReadLittleFloat();
530 float MSG_ReadCoord (protocolversion_t protocol)
532 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
533 return MSG_ReadCoord13i();
534 else if (protocol == PROTOCOL_DARKPLACES1)
535 return MSG_ReadCoord32f();
536 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
537 return MSG_ReadCoord16i();
539 return MSG_ReadCoord32f();
542 void MSG_ReadVector (float *v, protocolversion_t protocol)
544 v[0] = MSG_ReadCoord(protocol);
545 v[1] = MSG_ReadCoord(protocol);
546 v[2] = MSG_ReadCoord(protocol);
549 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
550 float MSG_ReadAngle8i (void)
552 return (signed char) MSG_ReadByte () * (360.0/256.0);
555 float MSG_ReadAngle16i (void)
557 return (signed short)MSG_ReadShort () * (360.0/65536.0);
560 float MSG_ReadAngle32f (void)
562 return MSG_ReadFloat ();
565 float MSG_ReadAngle (protocolversion_t protocol)
567 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)
568 return MSG_ReadAngle8i ();
570 return MSG_ReadAngle16i ();
574 //===========================================================================
576 void SZ_Clear (sizebuf_t *buf)
581 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
585 if (buf->cursize + length > buf->maxsize)
587 if (!buf->allowoverflow)
588 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
590 if (length > buf->maxsize)
591 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
593 buf->overflowed = true;
594 Con_Print("SZ_GetSpace: overflow\n");
598 data = buf->data + buf->cursize;
599 buf->cursize += length;
604 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
606 memcpy (SZ_GetSpace(buf,length),data,length);
609 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
610 // attention, it has been eradicated from here, its only (former) use in
611 // all of darkplaces.
613 static const char *hexchar = "0123456789ABCDEF";
614 void Com_HexDumpToConsole(const unsigned char *data, int size)
618 char *cur, *flushpointer;
619 const unsigned char *d;
621 flushpointer = text + 512;
622 for (i = 0;i < size;)
629 *cur++ = hexchar[(i >> 12) & 15];
630 *cur++ = hexchar[(i >> 8) & 15];
631 *cur++ = hexchar[(i >> 4) & 15];
632 *cur++ = hexchar[(i >> 0) & 15];
635 for (j = 0;j < 16;j++)
639 *cur++ = hexchar[(d[j] >> 4) & 15];
640 *cur++ = hexchar[(d[j] >> 0) & 15];
651 for (j = 0;j < 16;j++)
655 // color change prefix character has to be treated specially
656 if (d[j] == STRING_COLOR_TAG)
658 *cur++ = STRING_COLOR_TAG;
659 *cur++ = STRING_COLOR_TAG;
661 else if (d[j] >= (unsigned char) ' ')
671 if (cur >= flushpointer || i >= size)
680 void SZ_HexDumpToConsole(const sizebuf_t *buf)
682 Com_HexDumpToConsole(buf->data, buf->cursize);
686 //============================================================================
692 Word wraps a string. The wordWidth function is guaranteed to be called exactly
693 once for each word in the string, so it may be stateful, no idea what that
694 would be good for any more. At the beginning of the string, it will be called
695 for the char 0 to initialize a clean state, and then once with the string " "
696 (a space) so the routine knows how long a space is.
698 In case no single character fits into the given width, the wordWidth function
699 must return the width of exactly one character.
701 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
703 The sum of the return values of the processLine function will be returned.
706 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
708 // Logic is as follows:
710 // For each word or whitespace:
711 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
712 // Space found? Always add it to the current line, no matter if it fits.
713 // Word found? Check if current line + current word fits.
714 // If it fits, append it. Continue.
715 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
717 qboolean isContinuation = false;
719 const char *startOfLine = string;
720 const char *cursor = string;
721 const char *end = string + length;
722 float spaceUsedInLine = 0;
723 float spaceUsedForWord;
729 wordWidth(passthroughCW, NULL, &dummy, -1);
731 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
735 char ch = (cursor < end) ? *cursor : 0;
738 case 0: // end of string
739 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
740 isContinuation = false;
742 case '\n': // end of line
743 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
744 isContinuation = false;
746 startOfLine = cursor;
750 spaceUsedInLine += spaceWidth;
754 while(cursor + wordLen < end)
756 switch(cursor[wordLen])
768 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
769 if(wordLen < 1) // cannot happen according to current spec of wordWidth
772 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
774 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
776 // we can simply append it
778 spaceUsedInLine += spaceUsedForWord;
782 // output current line
783 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
784 isContinuation = true;
785 startOfLine = cursor;
787 spaceUsedInLine = continuationWidth + spaceUsedForWord;
796 qboolean isContinuation = false;
797 float currentWordSpace = 0;
798 const char *currentWord = 0;
799 float minReserve = 0;
801 float spaceUsedInLine = 0;
802 const char *currentLine = 0;
803 const char *currentLineEnd = 0;
804 float currentLineFinalWhitespace = 0;
808 minReserve = charWidth(passthroughCW, 0);
809 minReserve += charWidth(passthroughCW, ' ');
811 if(maxWidth < continuationWidth + minReserve)
812 maxWidth = continuationWidth + minReserve;
814 charWidth(passthroughCW, 0);
816 for(p = string; p < string + length; ++p)
819 float w = charWidth(passthroughCW, c);
824 currentWordSpace = 0;
830 spaceUsedInLine = isContinuation ? continuationWidth : 0;
836 // 1. I can add the word AND a space - then just append it.
837 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
839 currentLineEnd = p; // note: space not included here
840 currentLineFinalWhitespace = w;
841 spaceUsedInLine += currentWordSpace + w;
843 // 2. I can just add the word - then append it, output current line and go to next one.
844 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
846 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
848 isContinuation = true;
850 // 3. Otherwise, output current line and go to next one, where I can add the word.
851 else if(continuationWidth + currentWordSpace + w <= maxWidth)
854 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
855 currentLine = currentWord;
856 spaceUsedInLine = continuationWidth + currentWordSpace + w;
858 currentLineFinalWhitespace = w;
859 isContinuation = true;
861 // 4. We can't even do that? Then output both current and next word as new lines.
866 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
867 isContinuation = true;
869 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
871 isContinuation = true;
877 // 1. I can add the word - then do it.
878 if(spaceUsedInLine + currentWordSpace <= maxWidth)
880 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
882 // 2. Otherwise, output current line, next one and make tabula rasa.
887 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
888 isContinuation = true;
890 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
894 isContinuation = false;
898 currentWordSpace += w;
900 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
902 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
905 // this word cannot join ANY line...
906 // so output the current line...
909 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
910 isContinuation = true;
913 // then this word's beginning...
916 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
917 float pieceWidth = maxWidth - continuationWidth;
918 const char *pos = currentWord;
919 currentWordSpace = 0;
921 // reset the char width function to a state where no kerning occurs (start of word)
922 charWidth(passthroughCW, ' ');
925 float w = charWidth(passthroughCW, *pos);
926 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
928 // print everything until it
929 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
932 currentWordSpace = 0;
934 currentWordSpace += w;
937 // now we have a currentWord that fits... set up its next line
938 // currentWordSpace has been set
939 // currentWord has been set
940 spaceUsedInLine = continuationWidth;
941 currentLine = currentWord;
943 isContinuation = true;
947 // we have a guarantee that it will fix (see if clause)
948 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
950 // and use the rest of this word as new start of a line
951 currentWordSpace = w;
953 spaceUsedInLine = continuationWidth;
956 isContinuation = true;
965 currentWordSpace = 0;
968 if(currentLine) // Same procedure as \n
970 // Can I append the current word?
971 if(spaceUsedInLine + currentWordSpace <= maxWidth)
972 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
977 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
978 isContinuation = true;
980 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
990 COM_ParseToken_Simple
992 Parse a token out of a string
995 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
999 const char *data = *datapointer;
1006 *datapointer = NULL;
1016 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1021 *datapointer = NULL;
1026 // handle Windows line ending
1027 if (data[0] == '\r' && data[1] == '\n')
1030 if (data[0] == '/' && data[1] == '/')
1033 while (*data && *data != '\n' && *data != '\r')
1037 else if (data[0] == '/' && data[1] == '*')
1041 while (*data && (data[0] != '*' || data[1] != '/'))
1049 else if (*data == '\"')
1052 for (data++;*data && *data != '\"';data++)
1055 if (*data == '\\' && parsebackslash)
1064 if (len < (int)sizeof(com_token) - 1)
1065 com_token[len++] = c;
1070 *datapointer = data;
1073 else if (*data == '\r')
1075 // translate Mac line ending to UNIX
1076 com_token[len++] = '\n';data++;
1078 *datapointer = data;
1081 else if (*data == '\n')
1084 com_token[len++] = *data++;
1086 *datapointer = data;
1092 for (;!ISWHITESPACE(*data);data++)
1093 if (len < (int)sizeof(com_token) - 1)
1094 com_token[len++] = *data;
1096 *datapointer = data;
1103 COM_ParseToken_QuakeC
1105 Parse a token out of a string
1108 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1112 const char *data = *datapointer;
1119 *datapointer = NULL;
1129 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1134 *datapointer = NULL;
1139 // handle Windows line ending
1140 if (data[0] == '\r' && data[1] == '\n')
1143 if (data[0] == '/' && data[1] == '/')
1146 while (*data && *data != '\n' && *data != '\r')
1150 else if (data[0] == '/' && data[1] == '*')
1154 while (*data && (data[0] != '*' || data[1] != '/'))
1162 else if (*data == '\"' || *data == '\'')
1166 for (data++;*data && *data != quote;data++)
1178 if (len < (int)sizeof(com_token) - 1)
1179 com_token[len++] = c;
1184 *datapointer = data;
1187 else if (*data == '\r')
1189 // translate Mac line ending to UNIX
1190 com_token[len++] = '\n';data++;
1192 *datapointer = data;
1195 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1198 com_token[len++] = *data++;
1200 *datapointer = data;
1206 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1207 if (len < (int)sizeof(com_token) - 1)
1208 com_token[len++] = *data;
1210 *datapointer = data;
1217 COM_ParseToken_VM_Tokenize
1219 Parse a token out of a string
1222 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1226 const char *data = *datapointer;
1233 *datapointer = NULL;
1243 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1248 *datapointer = NULL;
1253 // handle Windows line ending
1254 if (data[0] == '\r' && data[1] == '\n')
1257 if (data[0] == '/' && data[1] == '/')
1260 while (*data && *data != '\n' && *data != '\r')
1264 else if (data[0] == '/' && data[1] == '*')
1268 while (*data && (data[0] != '*' || data[1] != '/'))
1276 else if (*data == '\"' || *data == '\'')
1280 for (data++;*data && *data != quote;data++)
1292 if (len < (int)sizeof(com_token) - 1)
1293 com_token[len++] = c;
1298 *datapointer = data;
1301 else if (*data == '\r')
1303 // translate Mac line ending to UNIX
1304 com_token[len++] = '\n';data++;
1306 *datapointer = data;
1309 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1312 com_token[len++] = *data++;
1314 *datapointer = data;
1320 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1321 if (len < (int)sizeof(com_token) - 1)
1322 com_token[len++] = *data;
1324 *datapointer = data;
1331 COM_ParseToken_Console
1333 Parse a token out of a string, behaving like the qwcl console
1336 int COM_ParseToken_Console(const char **datapointer)
1339 const char *data = *datapointer;
1346 *datapointer = NULL;
1352 for (;ISWHITESPACE(*data);data++)
1357 *datapointer = NULL;
1362 if (*data == '/' && data[1] == '/')
1365 while (*data && *data != '\n' && *data != '\r')
1369 else if (*data == '\"')
1372 for (data++;*data && *data != '\"';data++)
1374 // allow escaped " and \ case
1375 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1377 if (len < (int)sizeof(com_token) - 1)
1378 com_token[len++] = *data;
1383 *datapointer = data;
1388 for (;!ISWHITESPACE(*data);data++)
1389 if (len < (int)sizeof(com_token) - 1)
1390 com_token[len++] = *data;
1392 *datapointer = data;
1403 Returns the position (1 to argc-1) in the program's argument list
1404 where the given parameter apears, or 0 if not present
1407 int COM_CheckParm (const char *parm)
1411 for (i=1 ; i<com_argc ; i++)
1414 continue; // NEXTSTEP sometimes clears appkit vars.
1415 if (!strcmp (parm,com_argv[i]))
1422 //===========================================================================
1426 gamemode_t com_startupgamemode;
1427 gamemode_t com_startupgamegroup;
1429 typedef struct gamemode_info_s
1431 gamemode_t mode; // this gamemode
1432 gamemode_t group; // different games with same group can switch automatically when gamedirs change
1433 const char* prog_name; // not null
1434 const char* cmdline; // not null
1435 const char* gamename; // not null
1436 const char* gamedirname1; // not null
1437 const char* gamedirname2; // null
1438 const char* gamescreenshotname; // not nul
1439 const char* gameuserdirname; // not null
1442 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1443 {// game basegame prog_name cmdline gamename basegame modgame screenshot userdir // commandline option
1444 { GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1445 { GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1446 { GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1447 { GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1448 { GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1449 { GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
1450 { GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1451 { GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1452 { GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1453 { GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1454 { GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1455 { GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1456 { GAME_SOM, GAME_NORMAL, "sonofman", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" }, // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1457 { GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1458 { GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1459 { GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1460 { GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1461 { GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1462 { GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1463 { GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1464 { GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1465 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1466 { GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1467 { GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
1468 { GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1469 { GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
1470 { GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
1471 { GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
1474 static void COM_SetGameType(int index);
1475 void COM_InitGameType (void)
1477 char name [MAX_OSPATH];
1481 // check executable filename for keywords, but do it SMARTLY - only check the last path element
1482 FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
1483 COM_ToLowerString(name, name, sizeof (name));
1484 for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1485 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
1491 // check commandline options for keywords
1492 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1493 if (COM_CheckParm (gamemode_info[i].cmdline))
1499 com_startupgamemode = gamemode_info[index].mode;
1500 com_startupgamegroup = gamemode_info[index].group;
1501 COM_SetGameType(index);
1504 void COM_ChangeGameTypeForGameDirs(void)
1508 // this will not not change the gamegroup
1509 // first check if a base game (single gamedir) matches
1510 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1512 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
1518 // now that we have a base game, see if there is a matching derivative game (two gamedirs)
1521 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
1523 if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1530 // we now have a good guess at which game this is meant to be...
1531 if (index >= 0 && gamemode != gamemode_info[index].mode)
1532 COM_SetGameType(index);
1535 static void COM_SetGameType(int index)
1538 if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1540 gamemode = gamemode_info[index].mode;
1541 gamename = gamemode_info[index].gamename;
1542 gamedirname1 = gamemode_info[index].gamedirname1;
1543 gamedirname2 = gamemode_info[index].gamedirname2;
1544 gamescreenshotname = gamemode_info[index].gamescreenshotname;
1545 gameuserdirname = gamemode_info[index].gameuserdirname;
1547 if (gamemode == com_startupgamemode)
1549 if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
1550 gamename = com_argv[t+1];
1551 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
1552 gamedirname1 = com_argv[t+1];
1553 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
1554 gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
1555 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
1556 gamescreenshotname = com_argv[t+1];
1557 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
1558 gameuserdirname = com_argv[t+1];
1561 if (gamedirname2 && gamedirname2[0])
1562 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1564 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1565 for (i = 0;i < fs_numgamedirs;i++)
1568 Con_Printf(", with mod gamedirs");
1569 Con_Printf(" %s", fs_gamedirs[i]);
1580 void COM_Init_Commands (void)
1583 char com_cmdline[MAX_INPUTLINE];
1585 Cvar_RegisterVariable (®istered);
1586 Cvar_RegisterVariable (&cmdline);
1588 // reconstitute the command line for the cmdline externally visible cvar
1590 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1593 if (strstr(com_argv[j], " "))
1595 // arg contains whitespace, store quotes around it
1596 com_cmdline[n++] = '\"';
1597 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1598 com_cmdline[n++] = com_argv[j][i++];
1599 com_cmdline[n++] = '\"';
1603 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1604 com_cmdline[n++] = com_argv[j][i++];
1606 if (n < ((int)sizeof(com_cmdline) - 1))
1607 com_cmdline[n++] = ' ';
1612 Cvar_Set ("cmdline", com_cmdline);
1619 does a varargs printf into a temp buffer, so I don't need to have
1620 varargs versions of all text functions.
1621 FIXME: make this buffer size safe someday
1624 char *va(const char *format, ...)
1627 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1628 static char string[8][1024], *s;
1629 static int stringindex = 0;
1631 s = string[stringindex];
1632 stringindex = (stringindex + 1) & 7;
1633 va_start (argptr, format);
1634 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1641 //======================================
1643 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1649 # define snprintf _snprintf
1650 # define vsnprintf _vsnprintf
1654 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1659 va_start (args, format);
1660 result = dpvsnprintf (buffer, buffersize, format, args);
1667 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1671 #if _MSC_VER >= 1400
1672 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1674 result = vsnprintf (buffer, buffersize, format, args);
1676 if (result < 0 || (size_t)result >= buffersize)
1678 buffer[buffersize - 1] = '\0';
1686 //======================================
1688 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1693 if(utf8_enable.integer)
1696 while(*in && size_out > 1)
1699 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1700 ch = u8_tolower(ch);
1701 n = u8_fromchar(ch, out, size_out);
1710 while (*in && size_out > 1)
1712 if (*in >= 'A' && *in <= 'Z')
1713 *out++ = *in++ + 'a' - 'A';
1721 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1726 if(utf8_enable.integer)
1729 while(*in && size_out > 1)
1732 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1733 ch = u8_toupper(ch);
1734 n = u8_fromchar(ch, out, size_out);
1743 while (*in && size_out > 1)
1745 if (*in >= 'a' && *in <= 'z')
1746 *out++ = *in++ + 'A' - 'a';
1754 int COM_StringBeginsWith(const char *s, const char *match)
1756 for (;*s && *match;s++, match++)
1762 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1764 int argc, commentprefixlength;
1768 tokenbufend = tokenbuf + tokenbufsize;
1770 commentprefixlength = 0;
1772 commentprefixlength = (int)strlen(commentprefix);
1773 while (*l && *l != '\n' && *l != '\r')
1775 if (!ISWHITESPACE(*l))
1777 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1779 while (*l && *l != '\n' && *l != '\r')
1783 if (argc >= maxargc)
1785 argv[argc++] = tokenbuf;
1789 while (*l && *l != '"')
1791 if (tokenbuf >= tokenbufend)
1800 while (!ISWHITESPACE(*l))
1802 if (tokenbuf >= tokenbufend)
1807 if (tokenbuf >= tokenbufend)
1828 COM_StringLengthNoColors
1830 calculates the visible width of a color coded string.
1832 *valid is filled with TRUE if the string is a valid colored string (that is, if
1833 it does not end with an unfinished color code). If it gets filled with FALSE, a
1834 fix would be adding a STRING_COLOR_TAG at the end of the string.
1836 valid can be set to NULL if the caller doesn't care.
1838 For size_s, specify the maximum number of characters from s to use, or 0 to use
1839 all characters until the zero terminator.
1843 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1845 const char *end = size_s ? (s + size_s) : NULL;
1849 switch((s == end) ? 0 : *s)
1855 case STRING_COLOR_TAG:
1857 switch((s == end) ? 0 : *s)
1859 case STRING_COLOR_RGB_TAG_CHAR:
1860 if (s+1 != end && isxdigit(s[1]) &&
1861 s+2 != end && isxdigit(s[2]) &&
1862 s+3 != end && isxdigit(s[3]) )
1867 ++len; // STRING_COLOR_TAG
1868 ++len; // STRING_COLOR_RGB_TAG_CHAR
1870 case 0: // ends with unfinished color code!
1875 case STRING_COLOR_TAG: // escaped ^
1878 case '0': case '1': case '2': case '3': case '4':
1879 case '5': case '6': case '7': case '8': case '9': // color code
1881 default: // not a color code
1882 ++len; // STRING_COLOR_TAG
1883 ++len; // the character
1898 COM_StringDecolorize
1900 removes color codes from a string.
1902 If escape_carets is true, the resulting string will be safe for printing. If
1903 escape_carets is false, the function will just strip color codes (for logging
1906 If the output buffer size did not suffice for converting, the function returns
1907 FALSE. Generally, if escape_carets is false, the output buffer needs
1908 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1909 bytes. In any case, the function makes sure that the resulting string is
1912 For size_in, specify the maximum number of characters from in to use, or 0 to use
1913 all characters until the zero terminator.
1917 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1919 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1920 const char *end = size_in ? (in + size_in) : NULL;
1925 switch((in == end) ? 0 : *in)
1930 case STRING_COLOR_TAG:
1932 switch((in == end) ? 0 : *in)
1934 case STRING_COLOR_RGB_TAG_CHAR:
1935 if (in+1 != end && isxdigit(in[1]) &&
1936 in+2 != end && isxdigit(in[2]) &&
1937 in+3 != end && isxdigit(in[3]) )
1942 APPEND(STRING_COLOR_TAG);
1944 APPEND(STRING_COLOR_TAG);
1945 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1947 case 0: // ends with unfinished color code!
1948 APPEND(STRING_COLOR_TAG);
1949 // finish the code by appending another caret when escaping
1951 APPEND(STRING_COLOR_TAG);
1954 case STRING_COLOR_TAG: // escaped ^
1955 APPEND(STRING_COLOR_TAG);
1956 // append a ^ twice when escaping
1958 APPEND(STRING_COLOR_TAG);
1960 case '0': case '1': case '2': case '3': case '4':
1961 case '5': case '6': case '7': case '8': case '9': // color code
1963 default: // not a color code
1964 APPEND(STRING_COLOR_TAG);
1979 // written by Elric, thanks Elric!
1980 char *SearchInfostring(const char *infostring, const char *key)
1982 static char value [MAX_INPUTLINE];
1983 char crt_key [MAX_INPUTLINE];
1984 size_t value_ind, key_ind;
1987 if (*infostring++ != '\\')
2002 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
2004 crt_key[key_ind] = '\0';
2008 crt_key[key_ind++] = c;
2011 // If it's the key we are looking for, save it in "value"
2012 if (!strcmp(crt_key, key))
2018 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
2020 value[value_ind] = '\0';
2024 value[value_ind++] = c;
2028 // Else, skip the value
2041 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
2047 keylength = strlen(key);
2048 if (valuelength < 1 || !value)
2050 Con_Printf("InfoString_GetValue: no room in value\n");
2054 if (strchr(key, '\\'))
2056 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
2059 if (strchr(key, '\"'))
2061 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2066 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2069 while (buffer[pos] == '\\')
2071 if (!memcmp(buffer + pos+1, key, keylength))
2073 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2075 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2076 value[j] = buffer[pos+j];
2080 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2081 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2083 // if we reach this point the key was not found
2086 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2094 keylength = strlen(key);
2095 if (strchr(key, '\\') || strchr(value, '\\'))
2097 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2100 if (strchr(key, '\"') || strchr(value, '\"'))
2102 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2107 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2110 while (buffer[pos] == '\\')
2112 if (!memcmp(buffer + pos+1, key, keylength))
2114 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2115 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2117 // if we found the key, find the end of it because we will be replacing it
2119 if (buffer[pos] == '\\')
2121 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2122 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2124 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2126 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2129 if (value && value[0])
2131 // set the key/value and append the remaining text
2132 char tempbuffer[4096];
2133 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2134 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2138 // just remove the key from the text
2139 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2143 void InfoString_Print(char *buffer)
2150 if (*buffer != '\\')
2152 Con_Printf("InfoString_Print: corrupt string\n");
2155 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2156 if (i < (int)sizeof(key)-1)
2159 if (*buffer != '\\')
2161 Con_Printf("InfoString_Print: corrupt string\n");
2164 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2165 if (i < (int)sizeof(value)-1)
2166 value[i++] = *buffer;
2168 // empty value is an error case
2169 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2173 //========================================================
2174 // strlcat and strlcpy, from OpenBSD
2177 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2179 * Permission to use, copy, modify, and distribute this software for any
2180 * purpose with or without fee is hereby granted, provided that the above
2181 * copyright notice and this permission notice appear in all copies.
2183 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2184 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2185 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2186 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2187 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2188 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2189 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2192 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2193 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2196 #ifndef HAVE_STRLCAT
2198 strlcat(char *dst, const char *src, size_t siz)
2200 register char *d = dst;
2201 register const char *s = src;
2202 register size_t n = siz;
2205 /* Find the end of dst and adjust bytes left but don't go past end */
2206 while (n-- != 0 && *d != '\0')
2212 return(dlen + strlen(s));
2213 while (*s != '\0') {
2222 return(dlen + (s - src)); /* count does not include NUL */
2224 #endif // #ifndef HAVE_STRLCAT
2227 #ifndef HAVE_STRLCPY
2229 strlcpy(char *dst, const char *src, size_t siz)
2231 register char *d = dst;
2232 register const char *s = src;
2233 register size_t n = siz;
2235 /* Copy as many bytes as will fit */
2236 if (n != 0 && --n != 0) {
2238 if ((*d++ = *s++) == 0)
2243 /* Not enough room in dst, add NUL and traverse rest of src */
2246 *d = '\0'; /* NUL-terminate dst */
2251 return(s - src - 1); /* count does not include NUL */
2254 #endif // #ifndef HAVE_STRLCPY
2256 void FindFraction(double val, int *num, int *denom, int denomMax)
2261 bestdiff = fabs(val);
2265 for(i = 1; i <= denomMax; ++i)
2267 int inum = (int) floor(0.5 + val * i);
2268 double diff = fabs(val - inum / (double)i);
2278 // decodes an XPM from C syntax
2279 char **XPM_DecodeString(const char *in)
2281 static char *tokens[257];
2282 static char lines[257][512];
2285 // skip until "{" token
2286 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
2288 // now, read in succession: string, comma-or-}
2289 while(COM_ParseToken_QuakeC(&in, false))
2291 tokens[line] = lines[line];
2292 strlcpy(lines[line++], com_token, sizeof(lines[0]));
2293 if(!COM_ParseToken_QuakeC(&in, false))
2295 if(!strcmp(com_token, "}"))
2297 if(strcmp(com_token, ","))
2299 if(line >= sizeof(tokens) / sizeof(tokens[0]))
2306 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2307 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
2309 unsigned char i0 = (bytes > 0) ? in[0] : 0;
2310 unsigned char i1 = (bytes > 1) ? in[1] : 0;
2311 unsigned char i2 = (bytes > 2) ? in[2] : 0;
2312 unsigned char o0 = base64[i0 >> 2];
2313 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
2314 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
2315 unsigned char o3 = base64[i2 & 077];
2316 out[0] = (bytes > 0) ? o0 : '?';
2317 out[1] = (bytes > 0) ? o1 : '?';
2318 out[2] = (bytes > 1) ? o2 : '=';
2319 out[3] = (bytes > 2) ? o3 : '=';
2322 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
2325 // expand the out-buffer
2326 blocks = (buflen + 2) / 3;
2327 if(blocks*4 > outbuflen)
2329 for(i = blocks; i > 0; )
2332 base64_3to4(buf + 3*i, buf + 4*i, buflen - 3*i);