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
30 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
31 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
33 char com_token[MAX_INPUTLINE];
35 const char **com_argv;
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
47 ============================================================================
51 ============================================================================
55 float BuffBigFloat (const unsigned char *buffer)
63 u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
67 int BuffBigLong (const unsigned char *buffer)
69 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
72 short BuffBigShort (const unsigned char *buffer)
74 return (buffer[0] << 8) | buffer[1];
77 float BuffLittleFloat (const unsigned char *buffer)
85 u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
89 int BuffLittleLong (const unsigned char *buffer)
91 return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
94 short BuffLittleShort (const unsigned char *buffer)
96 return (buffer[1] << 8) | buffer[0];
99 void StoreBigLong (unsigned char *buffer, unsigned int i)
101 buffer[0] = (i >> 24) & 0xFF;
102 buffer[1] = (i >> 16) & 0xFF;
103 buffer[2] = (i >> 8) & 0xFF;
104 buffer[3] = i & 0xFF;
107 void StoreBigShort (unsigned char *buffer, unsigned short i)
109 buffer[0] = (i >> 8) & 0xFF;
110 buffer[1] = i & 0xFF;
113 void StoreLittleLong (unsigned char *buffer, unsigned int i)
115 buffer[0] = i & 0xFF;
116 buffer[1] = (i >> 8) & 0xFF;
117 buffer[2] = (i >> 16) & 0xFF;
118 buffer[3] = (i >> 24) & 0xFF;
121 void StoreLittleShort (unsigned char *buffer, unsigned short i)
123 buffer[0] = i & 0xFF;
124 buffer[1] = (i >> 8) & 0xFF;
128 ============================================================================
132 ============================================================================
135 // this is a 16 bit, non-reflected CRC using the polynomial 0x1021
136 // and the initial and final xor values shown below... in other words, the
137 // CCITT standard CRC used by XMODEM
139 #define CRC_INIT_VALUE 0xffff
140 #define CRC_XOR_VALUE 0x0000
142 static unsigned short crctable[256] =
144 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
145 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
146 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
147 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
148 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
149 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
150 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
151 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
152 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
153 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
154 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
155 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
156 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
157 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
158 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
159 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
160 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
161 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
162 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
163 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
164 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
165 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
166 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
167 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
168 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
169 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
170 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
171 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
172 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
173 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
174 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
175 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
178 unsigned short CRC_Block(const unsigned char *data, size_t size)
180 unsigned short crc = CRC_INIT_VALUE;
182 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)];
183 return crc ^ CRC_XOR_VALUE;
186 unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size)
188 unsigned short crc = CRC_INIT_VALUE;
190 crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))];
191 return crc ^ CRC_XOR_VALUE;
195 static unsigned char chktbl[1024 + 4] =
197 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
198 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
199 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
200 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
201 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
202 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
203 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
204 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
205 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
206 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
207 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
208 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
209 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
210 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
211 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
212 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
213 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
214 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
215 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
216 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
217 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
218 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
219 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
220 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
221 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
222 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
223 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
224 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
225 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
226 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
227 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
228 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
230 // map checksum goes here
235 unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence)
238 unsigned char chkb[60 + 4];
240 p = chktbl + (sequence % (sizeof(chktbl) - 8));
244 memcpy(chkb, base, length);
246 chkb[length] = (sequence & 0xff) ^ p[0];
247 chkb[length+1] = p[1];
248 chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
249 chkb[length+3] = p[3];
251 return CRC_Block(chkb, length + 4) & 0xff;
255 ==============================================================================
259 Handles byte ordering and avoids alignment errors
260 ==============================================================================
267 void MSG_WriteChar (sizebuf_t *sb, int c)
271 buf = SZ_GetSpace (sb, 1);
275 void MSG_WriteByte (sizebuf_t *sb, int c)
279 buf = SZ_GetSpace (sb, 1);
283 void MSG_WriteShort (sizebuf_t *sb, int c)
287 buf = SZ_GetSpace (sb, 2);
292 void MSG_WriteLong (sizebuf_t *sb, int c)
296 buf = SZ_GetSpace (sb, 4);
298 buf[1] = (c>>8)&0xff;
299 buf[2] = (c>>16)&0xff;
303 void MSG_WriteFloat (sizebuf_t *sb, float f)
313 dat.l = LittleLong (dat.l);
315 SZ_Write (sb, (unsigned char *)&dat.l, 4);
318 void MSG_WriteString (sizebuf_t *sb, const char *s)
321 MSG_WriteChar (sb, 0);
323 SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1);
326 void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s)
329 SZ_Write (sb, (unsigned char *)s, (int)strlen(s));
332 void MSG_WriteCoord13i (sizebuf_t *sb, float f)
335 MSG_WriteShort (sb, (int)(f * 8.0 + 0.5));
337 MSG_WriteShort (sb, (int)(f * 8.0 - 0.5));
340 void MSG_WriteCoord16i (sizebuf_t *sb, float f)
343 MSG_WriteShort (sb, (int)(f + 0.5));
345 MSG_WriteShort (sb, (int)(f - 0.5));
348 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
350 MSG_WriteFloat (sb, f);
353 void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol)
355 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
356 MSG_WriteCoord13i (sb, f);
357 else if (protocol == PROTOCOL_DARKPLACES1)
358 MSG_WriteCoord32f (sb, f);
359 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
360 MSG_WriteCoord16i (sb, f);
362 MSG_WriteCoord32f (sb, f);
365 void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
367 MSG_WriteCoord (sb, v[0], protocol);
368 MSG_WriteCoord (sb, v[1], protocol);
369 MSG_WriteCoord (sb, v[2], protocol);
372 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
373 void MSG_WriteAngle8i (sizebuf_t *sb, float f)
376 MSG_WriteByte (sb, (int)(f*(256.0/360.0) + 0.5) & 255);
378 MSG_WriteByte (sb, (int)(f*(256.0/360.0) - 0.5) & 255);
381 void MSG_WriteAngle16i (sizebuf_t *sb, float f)
384 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) + 0.5) & 65535);
386 MSG_WriteShort (sb, (int)(f*(65536.0/360.0) - 0.5) & 65535);
389 void MSG_WriteAngle32f (sizebuf_t *sb, float f)
391 MSG_WriteFloat (sb, f);
394 void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol)
396 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)
397 MSG_WriteAngle8i (sb, f);
399 MSG_WriteAngle16i (sb, f);
406 qboolean msg_badread;
408 void MSG_BeginReading (void)
414 int MSG_ReadLittleShort (void)
416 if (msg_readcount+2 > net_message.cursize)
422 return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
425 int MSG_ReadBigShort (void)
427 if (msg_readcount+2 > net_message.cursize)
433 return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
436 int MSG_ReadLittleLong (void)
438 if (msg_readcount+4 > net_message.cursize)
444 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);
447 int MSG_ReadBigLong (void)
449 if (msg_readcount+4 > net_message.cursize)
455 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];
458 float MSG_ReadLittleFloat (void)
465 if (msg_readcount+4 > net_message.cursize)
471 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);
475 float MSG_ReadBigFloat (void)
482 if (msg_readcount+4 > net_message.cursize)
488 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];
492 char *MSG_ReadString (void)
494 static char string[MAX_INPUTLINE];
496 for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
502 int MSG_ReadBytes (int numbytes, unsigned char *out)
505 for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
510 float MSG_ReadCoord13i (void)
512 return MSG_ReadLittleShort() * (1.0/8.0);
515 float MSG_ReadCoord16i (void)
517 return (signed short) MSG_ReadLittleShort();
520 float MSG_ReadCoord32f (void)
522 return MSG_ReadLittleFloat();
525 float MSG_ReadCoord (protocolversion_t protocol)
527 if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
528 return MSG_ReadCoord13i();
529 else if (protocol == PROTOCOL_DARKPLACES1)
530 return MSG_ReadCoord32f();
531 else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
532 return MSG_ReadCoord16i();
534 return MSG_ReadCoord32f();
537 void MSG_ReadVector (float *v, protocolversion_t protocol)
539 v[0] = MSG_ReadCoord(protocol);
540 v[1] = MSG_ReadCoord(protocol);
541 v[2] = MSG_ReadCoord(protocol);
544 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
545 float MSG_ReadAngle8i (void)
547 return (signed char) MSG_ReadByte () * (360.0/256.0);
550 float MSG_ReadAngle16i (void)
552 return (signed short)MSG_ReadShort () * (360.0/65536.0);
555 float MSG_ReadAngle32f (void)
557 return MSG_ReadFloat ();
560 float MSG_ReadAngle (protocolversion_t protocol)
562 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)
563 return MSG_ReadAngle8i ();
565 return MSG_ReadAngle16i ();
569 //===========================================================================
571 void SZ_Clear (sizebuf_t *buf)
576 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
580 if (buf->cursize + length > buf->maxsize)
582 if (!buf->allowoverflow)
583 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
585 if (length > buf->maxsize)
586 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
588 buf->overflowed = true;
589 Con_Print("SZ_GetSpace: overflow\n");
593 data = buf->data + buf->cursize;
594 buf->cursize += length;
599 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
601 memcpy (SZ_GetSpace(buf,length),data,length);
604 // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
605 // attention, it has been eradicated from here, its only (former) use in
606 // all of darkplaces.
608 static char *hexchar = "0123456789ABCDEF";
609 void Com_HexDumpToConsole(const unsigned char *data, int size)
613 char *cur, *flushpointer;
614 const unsigned char *d;
616 flushpointer = text + 512;
617 for (i = 0;i < size;)
624 *cur++ = hexchar[(i >> 12) & 15];
625 *cur++ = hexchar[(i >> 8) & 15];
626 *cur++ = hexchar[(i >> 4) & 15];
627 *cur++ = hexchar[(i >> 0) & 15];
630 for (j = 0;j < 16;j++)
634 *cur++ = hexchar[(d[j] >> 4) & 15];
635 *cur++ = hexchar[(d[j] >> 0) & 15];
646 for (j = 0;j < 16;j++)
650 // color change prefix character has to be treated specially
651 if (d[j] == STRING_COLOR_TAG)
653 *cur++ = STRING_COLOR_TAG;
654 *cur++ = STRING_COLOR_TAG;
656 else if (d[j] >= (unsigned char) ' ')
666 if (cur >= flushpointer || i >= size)
675 void SZ_HexDumpToConsole(const sizebuf_t *buf)
677 Com_HexDumpToConsole(buf->data, buf->cursize);
681 //============================================================================
687 Word wraps a string. The wordWidth function is guaranteed to be called exactly
688 once for each word in the string, so it may be stateful, no idea what that
689 would be good for any more. At the beginning of the string, it will be called
690 for the char 0 to initialize a clean state, and then once with the string " "
691 (a space) so the routine knows how long a space is.
693 In case no single character fits into the given width, the wordWidth function
694 must return the width of exactly one character.
696 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
698 The sum of the return values of the processLine function will be returned.
701 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
703 // Logic is as follows:
705 // For each word or whitespace:
706 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
707 // Space found? Always add it to the current line, no matter if it fits.
708 // Word found? Check if current line + current word fits.
709 // If it fits, append it. Continue.
710 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
712 qboolean isContinuation = false;
714 const char *startOfLine = string;
715 const char *cursor = string;
716 const char *end = string + length;
717 float spaceUsedInLine = 0;
718 float spaceUsedForWord;
724 wordWidth(passthroughCW, NULL, &dummy, -1);
726 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
730 char ch = (cursor < end) ? *cursor : 0;
733 case 0: // end of string
734 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
735 isContinuation = false;
737 case '\n': // end of line
738 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
739 isContinuation = false;
741 startOfLine = cursor;
745 spaceUsedInLine += spaceWidth;
749 while(cursor + wordLen < end)
751 switch(cursor[wordLen])
763 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
764 if(wordLen < 1) // cannot happen according to current spec of wordWidth
767 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
769 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
771 // we can simply append it
773 spaceUsedInLine += spaceUsedForWord;
777 // output current line
778 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
779 isContinuation = true;
780 startOfLine = cursor;
782 spaceUsedInLine = continuationWidth + spaceUsedForWord;
791 qboolean isContinuation = false;
792 float currentWordSpace = 0;
793 const char *currentWord = 0;
794 float minReserve = 0;
796 float spaceUsedInLine = 0;
797 const char *currentLine = 0;
798 const char *currentLineEnd = 0;
799 float currentLineFinalWhitespace = 0;
803 minReserve = charWidth(passthroughCW, 0);
804 minReserve += charWidth(passthroughCW, ' ');
806 if(maxWidth < continuationWidth + minReserve)
807 maxWidth = continuationWidth + minReserve;
809 charWidth(passthroughCW, 0);
811 for(p = string; p < string + length; ++p)
814 float w = charWidth(passthroughCW, c);
819 currentWordSpace = 0;
825 spaceUsedInLine = isContinuation ? continuationWidth : 0;
831 // 1. I can add the word AND a space - then just append it.
832 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
834 currentLineEnd = p; // note: space not included here
835 currentLineFinalWhitespace = w;
836 spaceUsedInLine += currentWordSpace + w;
838 // 2. I can just add the word - then append it, output current line and go to next one.
839 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
841 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
843 isContinuation = true;
845 // 3. Otherwise, output current line and go to next one, where I can add the word.
846 else if(continuationWidth + currentWordSpace + w <= maxWidth)
849 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
850 currentLine = currentWord;
851 spaceUsedInLine = continuationWidth + currentWordSpace + w;
853 currentLineFinalWhitespace = w;
854 isContinuation = true;
856 // 4. We can't even do that? Then output both current and next word as new lines.
861 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
862 isContinuation = true;
864 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
866 isContinuation = true;
872 // 1. I can add the word - then do it.
873 if(spaceUsedInLine + currentWordSpace <= maxWidth)
875 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
877 // 2. Otherwise, output current line, next one and make tabula rasa.
882 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
883 isContinuation = true;
885 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
889 isContinuation = false;
893 currentWordSpace += w;
895 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
897 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
900 // this word cannot join ANY line...
901 // so output the current line...
904 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
905 isContinuation = true;
908 // then this word's beginning...
911 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
912 float pieceWidth = maxWidth - continuationWidth;
913 const char *pos = currentWord;
914 currentWordSpace = 0;
916 // reset the char width function to a state where no kerning occurs (start of word)
917 charWidth(passthroughCW, ' ');
920 float w = charWidth(passthroughCW, *pos);
921 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
923 // print everything until it
924 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
927 currentWordSpace = 0;
929 currentWordSpace += w;
932 // now we have a currentWord that fits... set up its next line
933 // currentWordSpace has been set
934 // currentWord has been set
935 spaceUsedInLine = continuationWidth;
936 currentLine = currentWord;
938 isContinuation = true;
942 // we have a guarantee that it will fix (see if clause)
943 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
945 // and use the rest of this word as new start of a line
946 currentWordSpace = w;
948 spaceUsedInLine = continuationWidth;
951 isContinuation = true;
960 currentWordSpace = 0;
963 if(currentLine) // Same procedure as \n
965 // Can I append the current word?
966 if(spaceUsedInLine + currentWordSpace <= maxWidth)
967 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
972 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
973 isContinuation = true;
975 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
985 COM_ParseToken_Simple
987 Parse a token out of a string
990 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
994 const char *data = *datapointer;
1001 *datapointer = NULL;
1011 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1016 *datapointer = NULL;
1021 // handle Windows line ending
1022 if (data[0] == '\r' && data[1] == '\n')
1025 if (data[0] == '/' && data[1] == '/')
1028 while (*data && *data != '\n' && *data != '\r')
1032 else if (data[0] == '/' && data[1] == '*')
1036 while (*data && (data[0] != '*' || data[1] != '/'))
1044 else if (*data == '\"')
1047 for (data++;*data && *data != '\"';data++)
1050 if (*data == '\\' && parsebackslash)
1059 if (len < (int)sizeof(com_token) - 1)
1060 com_token[len++] = c;
1065 *datapointer = data;
1068 else if (*data == '\r')
1070 // translate Mac line ending to UNIX
1071 com_token[len++] = '\n';data++;
1073 *datapointer = data;
1076 else if (*data == '\n')
1079 com_token[len++] = *data++;
1081 *datapointer = data;
1087 for (;!ISWHITESPACE(*data);data++)
1088 if (len < (int)sizeof(com_token) - 1)
1089 com_token[len++] = *data;
1091 *datapointer = data;
1098 COM_ParseToken_QuakeC
1100 Parse a token out of a string
1103 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1107 const char *data = *datapointer;
1114 *datapointer = NULL;
1124 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1129 *datapointer = NULL;
1134 // handle Windows line ending
1135 if (data[0] == '\r' && data[1] == '\n')
1138 if (data[0] == '/' && data[1] == '/')
1141 while (*data && *data != '\n' && *data != '\r')
1145 else if (data[0] == '/' && data[1] == '*')
1149 while (*data && (data[0] != '*' || data[1] != '/'))
1157 else if (*data == '\"' || *data == '\'')
1161 for (data++;*data && *data != quote;data++)
1173 if (len < (int)sizeof(com_token) - 1)
1174 com_token[len++] = c;
1179 *datapointer = data;
1182 else if (*data == '\r')
1184 // translate Mac line ending to UNIX
1185 com_token[len++] = '\n';data++;
1187 *datapointer = data;
1190 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1193 com_token[len++] = *data++;
1195 *datapointer = data;
1201 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1202 if (len < (int)sizeof(com_token) - 1)
1203 com_token[len++] = *data;
1205 *datapointer = data;
1212 COM_ParseToken_VM_Tokenize
1214 Parse a token out of a string
1217 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1221 const char *data = *datapointer;
1228 *datapointer = NULL;
1238 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1243 *datapointer = NULL;
1248 // handle Windows line ending
1249 if (data[0] == '\r' && data[1] == '\n')
1252 if (data[0] == '/' && data[1] == '/')
1255 while (*data && *data != '\n' && *data != '\r')
1259 else if (data[0] == '/' && data[1] == '*')
1263 while (*data && (data[0] != '*' || data[1] != '/'))
1271 else if (*data == '\"' || *data == '\'')
1275 for (data++;*data && *data != quote;data++)
1287 if (len < (int)sizeof(com_token) - 1)
1288 com_token[len++] = c;
1293 *datapointer = data;
1296 else if (*data == '\r')
1298 // translate Mac line ending to UNIX
1299 com_token[len++] = '\n';data++;
1301 *datapointer = data;
1304 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1307 com_token[len++] = *data++;
1309 *datapointer = data;
1315 for (;!ISWHITESPACE(*data) && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1316 if (len < (int)sizeof(com_token) - 1)
1317 com_token[len++] = *data;
1319 *datapointer = data;
1326 COM_ParseToken_Console
1328 Parse a token out of a string, behaving like the qwcl console
1331 int COM_ParseToken_Console(const char **datapointer)
1334 const char *data = *datapointer;
1341 *datapointer = NULL;
1347 for (;ISWHITESPACE(*data);data++)
1352 *datapointer = NULL;
1357 if (*data == '/' && data[1] == '/')
1360 while (*data && *data != '\n' && *data != '\r')
1364 else if (*data == '\"')
1367 for (data++;*data && *data != '\"';data++)
1369 // allow escaped " and \ case
1370 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1372 if (len < (int)sizeof(com_token) - 1)
1373 com_token[len++] = *data;
1378 *datapointer = data;
1383 for (;!ISWHITESPACE(*data);data++)
1384 if (len < (int)sizeof(com_token) - 1)
1385 com_token[len++] = *data;
1387 *datapointer = data;
1398 Returns the position (1 to argc-1) in the program's argument list
1399 where the given parameter apears, or 0 if not present
1402 int COM_CheckParm (const char *parm)
1406 for (i=1 ; i<com_argc ; i++)
1409 continue; // NEXTSTEP sometimes clears appkit vars.
1410 if (!strcmp (parm,com_argv[i]))
1417 //===========================================================================
1421 typedef struct gamemode_info_s
1423 const char* prog_name;
1424 const char* cmdline;
1425 const char* gamename;
1426 const char* gamedirname1;
1427 const char* gamedirname2;
1428 const char* gamescreenshotname;
1429 const char* gameuserdirname;
1432 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1433 {// prog_name cmdline gamename basegame modgame screenshotprefix userdir
1436 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1437 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1439 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1440 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1442 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1443 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1445 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1446 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1448 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1449 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1451 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1452 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1454 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1455 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1457 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1458 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1460 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1461 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1463 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1464 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1466 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1467 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1469 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1470 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1472 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1473 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1475 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1476 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1478 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1479 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1481 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1482 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1484 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1485 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1487 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1488 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1489 // GAME_DEFEATINDETAIL2
1490 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1491 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1493 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1494 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1495 // GAME_CONTAGIONTHEORY
1496 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1497 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1499 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1500 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1502 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1503 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1505 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1506 { "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" },
1507 // GAME_BLOODOMNICIDE
1508 // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1509 { "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
1512 void COM_InitGameType (void)
1514 char name [MAX_OSPATH];
1517 FS_StripExtension (com_argv[0], name, sizeof (name));
1518 COM_ToLowerString (name, name, sizeof (name));
1520 // Check the binary name; default to GAME_NORMAL (0)
1521 gamemode = GAME_NORMAL;
1522 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1523 if (strstr (name, gamemode_info[i].prog_name))
1525 gamemode = (gamemode_t)i;
1529 // Look for a command-line option
1530 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1531 if (COM_CheckParm (gamemode_info[i].cmdline))
1533 gamemode = (gamemode_t)i;
1537 gamename = gamemode_info[gamemode].gamename;
1538 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1539 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1540 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1541 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1550 void COM_Init_Commands (void)
1553 char com_cmdline[MAX_INPUTLINE];
1555 Cvar_RegisterVariable (®istered);
1556 Cvar_RegisterVariable (&cmdline);
1558 // reconstitute the command line for the cmdline externally visible cvar
1560 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1563 if (strstr(com_argv[j], " "))
1565 // arg contains whitespace, store quotes around it
1566 com_cmdline[n++] = '\"';
1567 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1568 com_cmdline[n++] = com_argv[j][i++];
1569 com_cmdline[n++] = '\"';
1573 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1574 com_cmdline[n++] = com_argv[j][i++];
1576 if (n < ((int)sizeof(com_cmdline) - 1))
1577 com_cmdline[n++] = ' ';
1582 Cvar_Set ("cmdline", com_cmdline);
1589 does a varargs printf into a temp buffer, so I don't need to have
1590 varargs versions of all text functions.
1591 FIXME: make this buffer size safe someday
1594 char *va(const char *format, ...)
1597 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1598 static char string[8][1024], *s;
1599 static int stringindex = 0;
1601 s = string[stringindex];
1602 stringindex = (stringindex + 1) & 7;
1603 va_start (argptr, format);
1604 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1611 //======================================
1613 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1619 # define snprintf _snprintf
1620 # define vsnprintf _vsnprintf
1624 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1629 va_start (args, format);
1630 result = dpvsnprintf (buffer, buffersize, format, args);
1637 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1641 #if _MSC_VER >= 1400
1642 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1644 result = vsnprintf (buffer, buffersize, format, args);
1646 if (result < 0 || (size_t)result >= buffersize)
1648 buffer[buffersize - 1] = '\0';
1656 //======================================
1658 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1663 while (*in && size_out > 1)
1665 if (*in >= 'A' && *in <= 'Z')
1666 *out++ = *in++ + 'a' - 'A';
1674 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1679 while (*in && size_out > 1)
1681 if (*in >= 'a' && *in <= 'z')
1682 *out++ = *in++ + 'A' - 'a';
1690 int COM_StringBeginsWith(const char *s, const char *match)
1692 for (;*s && *match;s++, match++)
1698 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1700 int argc, commentprefixlength;
1704 tokenbufend = tokenbuf + tokenbufsize;
1706 commentprefixlength = 0;
1708 commentprefixlength = (int)strlen(commentprefix);
1709 while (*l && *l != '\n' && *l != '\r')
1711 if (!ISWHITESPACE(*l))
1713 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1715 while (*l && *l != '\n' && *l != '\r')
1719 if (argc >= maxargc)
1721 argv[argc++] = tokenbuf;
1725 while (*l && *l != '"')
1727 if (tokenbuf >= tokenbufend)
1736 while (!ISWHITESPACE(*l))
1738 if (tokenbuf >= tokenbufend)
1743 if (tokenbuf >= tokenbufend)
1764 COM_StringLengthNoColors
1766 calculates the visible width of a color coded string.
1768 *valid is filled with TRUE if the string is a valid colored string (that is, if
1769 it does not end with an unfinished color code). If it gets filled with FALSE, a
1770 fix would be adding a STRING_COLOR_TAG at the end of the string.
1772 valid can be set to NULL if the caller doesn't care.
1774 For size_s, specify the maximum number of characters from s to use, or 0 to use
1775 all characters until the zero terminator.
1779 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1781 const char *end = size_s ? (s + size_s) : NULL;
1785 switch((s == end) ? 0 : *s)
1791 case STRING_COLOR_TAG:
1793 switch((s == end) ? 0 : *s)
1795 case STRING_COLOR_RGB_TAG_CHAR:
1796 if (s+1 != end && isxdigit(s[1]) &&
1797 s+2 != end && isxdigit(s[2]) &&
1798 s+3 != end && isxdigit(s[3]) )
1803 ++len; // STRING_COLOR_TAG
1804 ++len; // STRING_COLOR_RGB_TAG_CHAR
1806 case 0: // ends with unfinished color code!
1811 case STRING_COLOR_TAG: // escaped ^
1814 case '0': case '1': case '2': case '3': case '4':
1815 case '5': case '6': case '7': case '8': case '9': // color code
1817 default: // not a color code
1818 ++len; // STRING_COLOR_TAG
1819 ++len; // the character
1834 COM_StringDecolorize
1836 removes color codes from a string.
1838 If escape_carets is true, the resulting string will be safe for printing. If
1839 escape_carets is false, the function will just strip color codes (for logging
1842 If the output buffer size did not suffice for converting, the function returns
1843 FALSE. Generally, if escape_carets is false, the output buffer needs
1844 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1845 bytes. In any case, the function makes sure that the resulting string is
1848 For size_in, specify the maximum number of characters from in to use, or 0 to use
1849 all characters until the zero terminator.
1853 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1855 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1856 const char *end = size_in ? (in + size_in) : NULL;
1861 switch((in == end) ? 0 : *in)
1866 case STRING_COLOR_TAG:
1868 switch((in == end) ? 0 : *in)
1870 case STRING_COLOR_RGB_TAG_CHAR:
1871 if (in+1 != end && isxdigit(in[1]) &&
1872 in+2 != end && isxdigit(in[2]) &&
1873 in+3 != end && isxdigit(in[3]) )
1878 APPEND(STRING_COLOR_TAG);
1880 APPEND(STRING_COLOR_TAG);
1881 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1883 case 0: // ends with unfinished color code!
1884 APPEND(STRING_COLOR_TAG);
1885 // finish the code by appending another caret when escaping
1887 APPEND(STRING_COLOR_TAG);
1890 case STRING_COLOR_TAG: // escaped ^
1891 APPEND(STRING_COLOR_TAG);
1892 // append a ^ twice when escaping
1894 APPEND(STRING_COLOR_TAG);
1896 case '0': case '1': case '2': case '3': case '4':
1897 case '5': case '6': case '7': case '8': case '9': // color code
1899 default: // not a color code
1900 APPEND(STRING_COLOR_TAG);
1915 // written by Elric, thanks Elric!
1916 char *SearchInfostring(const char *infostring, const char *key)
1918 static char value [MAX_INPUTLINE];
1919 char crt_key [MAX_INPUTLINE];
1920 size_t value_ind, key_ind;
1923 if (*infostring++ != '\\')
1938 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1940 crt_key[key_ind] = '\0';
1944 crt_key[key_ind++] = c;
1947 // If it's the key we are looking for, save it in "value"
1948 if (!strcmp(crt_key, key))
1954 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1956 value[value_ind] = '\0';
1960 value[value_ind++] = c;
1964 // Else, skip the value
1977 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1985 keylength = strlen(key);
1986 if (valuelength < 1 || !value)
1988 Con_Printf("InfoString_GetValue: no room in value\n");
1992 if (strchr(key, '\\'))
1994 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1997 if (strchr(key, '\"'))
1999 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2004 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2007 while (buffer[pos] == '\\')
2009 if (!memcmp(buffer + pos+1, key, keylength))
2011 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2013 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2014 value[j] = buffer[pos+j];
2018 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2019 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2021 // if we reach this point the key was not found
2024 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2032 keylength = strlen(key);
2033 if (strchr(key, '\\') || strchr(value, '\\'))
2035 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2038 if (strchr(key, '\"') || strchr(value, '\"'))
2040 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2045 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2048 while (buffer[pos] == '\\')
2050 if (!memcmp(buffer + pos+1, key, keylength))
2052 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2053 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2055 // if we found the key, find the end of it because we will be replacing it
2057 if (buffer[pos] == '\\')
2059 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2060 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2062 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2064 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2067 if (value && value[0])
2069 // set the key/value and append the remaining text
2070 char tempbuffer[4096];
2071 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2072 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2076 // just remove the key from the text
2077 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2081 void InfoString_Print(char *buffer)
2088 if (*buffer != '\\')
2090 Con_Printf("InfoString_Print: corrupt string\n");
2093 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2094 if (i < (int)sizeof(key)-1)
2097 if (*buffer != '\\')
2099 Con_Printf("InfoString_Print: corrupt string\n");
2102 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2103 if (i < (int)sizeof(value)-1)
2104 value[i++] = *buffer;
2106 // empty value is an error case
2107 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2111 //========================================================
2112 // strlcat and strlcpy, from OpenBSD
2115 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2117 * Permission to use, copy, modify, and distribute this software for any
2118 * purpose with or without fee is hereby granted, provided that the above
2119 * copyright notice and this permission notice appear in all copies.
2121 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2122 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2123 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2124 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2125 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2126 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2127 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2130 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2131 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2134 #ifndef HAVE_STRLCAT
2136 strlcat(char *dst, const char *src, size_t siz)
2138 register char *d = dst;
2139 register const char *s = src;
2140 register size_t n = siz;
2143 /* Find the end of dst and adjust bytes left but don't go past end */
2144 while (n-- != 0 && *d != '\0')
2150 return(dlen + strlen(s));
2151 while (*s != '\0') {
2160 return(dlen + (s - src)); /* count does not include NUL */
2162 #endif // #ifndef HAVE_STRLCAT
2165 #ifndef HAVE_STRLCPY
2167 strlcpy(char *dst, const char *src, size_t siz)
2169 register char *d = dst;
2170 register const char *s = src;
2171 register size_t n = siz;
2173 /* Copy as many bytes as will fit */
2174 if (n != 0 && --n != 0) {
2176 if ((*d++ = *s++) == 0)
2181 /* Not enough room in dst, add NUL and traverse rest of src */
2184 *d = '\0'; /* NUL-terminate dst */
2189 return(s - src - 1); /* count does not include NUL */
2192 #endif // #ifndef HAVE_STRLCPY
2194 void FindFraction(double val, int *num, int *denom, int denomMax)
2199 bestdiff = fabs(val);
2203 for(i = 1; i <= denomMax; ++i)
2205 int inum = (int) floor(0.5 + val * i);
2206 double diff = fabs(val - inum / (double)i);