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;
738 case '\n': // end of line
739 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
740 isContinuation = false;
742 startOfLine = cursor;
746 spaceUsedInLine += spaceWidth;
750 while(cursor + wordLen < end)
752 switch(cursor[wordLen])
764 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
765 if(wordLen < 1) // cannot happen according to current spec of wordWidth
768 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
770 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
772 // we can simply append it
774 spaceUsedInLine += spaceUsedForWord;
778 // output current line
779 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
780 isContinuation = true;
781 startOfLine = cursor;
783 spaceUsedInLine = continuationWidth + spaceUsedForWord;
792 qboolean isContinuation = false;
793 float currentWordSpace = 0;
794 const char *currentWord = 0;
795 float minReserve = 0;
797 float spaceUsedInLine = 0;
798 const char *currentLine = 0;
799 const char *currentLineEnd = 0;
800 float currentLineFinalWhitespace = 0;
804 minReserve = charWidth(passthroughCW, 0);
805 minReserve += charWidth(passthroughCW, ' ');
807 if(maxWidth < continuationWidth + minReserve)
808 maxWidth = continuationWidth + minReserve;
810 charWidth(passthroughCW, 0);
812 for(p = string; p < string + length; ++p)
815 float w = charWidth(passthroughCW, c);
820 currentWordSpace = 0;
826 spaceUsedInLine = isContinuation ? continuationWidth : 0;
832 // 1. I can add the word AND a space - then just append it.
833 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
835 currentLineEnd = p; // note: space not included here
836 currentLineFinalWhitespace = w;
837 spaceUsedInLine += currentWordSpace + w;
839 // 2. I can just add the word - then append it, output current line and go to next one.
840 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
842 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
844 isContinuation = true;
846 // 3. Otherwise, output current line and go to next one, where I can add the word.
847 else if(continuationWidth + currentWordSpace + w <= maxWidth)
850 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
851 currentLine = currentWord;
852 spaceUsedInLine = continuationWidth + currentWordSpace + w;
854 currentLineFinalWhitespace = w;
855 isContinuation = true;
857 // 4. We can't even do that? Then output both current and next word as new lines.
862 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
863 isContinuation = true;
865 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
867 isContinuation = true;
873 // 1. I can add the word - then do it.
874 if(spaceUsedInLine + currentWordSpace <= maxWidth)
876 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
878 // 2. Otherwise, output current line, next one and make tabula rasa.
883 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
884 isContinuation = true;
886 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
890 isContinuation = false;
894 currentWordSpace += w;
896 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
898 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
901 // this word cannot join ANY line...
902 // so output the current line...
905 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
906 isContinuation = true;
909 // then this word's beginning...
912 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
913 float pieceWidth = maxWidth - continuationWidth;
914 const char *pos = currentWord;
915 currentWordSpace = 0;
917 // reset the char width function to a state where no kerning occurs (start of word)
918 charWidth(passthroughCW, ' ');
921 float w = charWidth(passthroughCW, *pos);
922 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
924 // print everything until it
925 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
928 currentWordSpace = 0;
930 currentWordSpace += w;
933 // now we have a currentWord that fits... set up its next line
934 // currentWordSpace has been set
935 // currentWord has been set
936 spaceUsedInLine = continuationWidth;
937 currentLine = currentWord;
939 isContinuation = true;
943 // we have a guarantee that it will fix (see if clause)
944 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
946 // and use the rest of this word as new start of a line
947 currentWordSpace = w;
949 spaceUsedInLine = continuationWidth;
952 isContinuation = true;
961 currentWordSpace = 0;
964 if(currentLine) // Same procedure as \n
966 // Can I append the current word?
967 if(spaceUsedInLine + currentWordSpace <= maxWidth)
968 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
973 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
974 isContinuation = true;
976 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
986 COM_ParseToken_Simple
988 Parse a token out of a string
991 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
995 const char *data = *datapointer;
1002 *datapointer = NULL;
1012 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1017 *datapointer = NULL;
1022 // handle Windows line ending
1023 if (data[0] == '\r' && data[1] == '\n')
1026 if (data[0] == '/' && data[1] == '/')
1029 while (*data && *data != '\n' && *data != '\r')
1033 else if (data[0] == '/' && data[1] == '*')
1037 while (*data && (data[0] != '*' || data[1] != '/'))
1045 else if (*data == '\"')
1048 for (data++;*data && *data != '\"';data++)
1051 if (*data == '\\' && parsebackslash)
1060 if (len < (int)sizeof(com_token) - 1)
1061 com_token[len++] = c;
1066 *datapointer = data;
1069 else if (*data == '\r')
1071 // translate Mac line ending to UNIX
1072 com_token[len++] = '\n';data++;
1074 *datapointer = data;
1077 else if (*data == '\n')
1080 com_token[len++] = *data++;
1082 *datapointer = data;
1088 for (;!ISWHITESPACE(*data);data++)
1089 if (len < (int)sizeof(com_token) - 1)
1090 com_token[len++] = *data;
1092 *datapointer = data;
1099 COM_ParseToken_QuakeC
1101 Parse a token out of a string
1104 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
1108 const char *data = *datapointer;
1115 *datapointer = NULL;
1125 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1130 *datapointer = NULL;
1135 // handle Windows line ending
1136 if (data[0] == '\r' && data[1] == '\n')
1139 if (data[0] == '/' && data[1] == '/')
1142 while (*data && *data != '\n' && *data != '\r')
1146 else if (data[0] == '/' && data[1] == '*')
1150 while (*data && (data[0] != '*' || data[1] != '/'))
1158 else if (*data == '\"' || *data == '\'')
1162 for (data++;*data && *data != quote;data++)
1174 if (len < (int)sizeof(com_token) - 1)
1175 com_token[len++] = c;
1180 *datapointer = data;
1183 else if (*data == '\r')
1185 // translate Mac line ending to UNIX
1186 com_token[len++] = '\n';data++;
1188 *datapointer = data;
1191 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1194 com_token[len++] = *data++;
1196 *datapointer = data;
1202 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1203 if (len < (int)sizeof(com_token) - 1)
1204 com_token[len++] = *data;
1206 *datapointer = data;
1213 COM_ParseToken_VM_Tokenize
1215 Parse a token out of a string
1218 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
1222 const char *data = *datapointer;
1229 *datapointer = NULL;
1239 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
1244 *datapointer = NULL;
1249 // handle Windows line ending
1250 if (data[0] == '\r' && data[1] == '\n')
1253 if (data[0] == '/' && data[1] == '/')
1256 while (*data && *data != '\n' && *data != '\r')
1260 else if (data[0] == '/' && data[1] == '*')
1264 while (*data && (data[0] != '*' || data[1] != '/'))
1272 else if (*data == '\"' || *data == '\'')
1276 for (data++;*data && *data != quote;data++)
1288 if (len < (int)sizeof(com_token) - 1)
1289 com_token[len++] = c;
1294 *datapointer = data;
1297 else if (*data == '\r')
1299 // translate Mac line ending to UNIX
1300 com_token[len++] = '\n';data++;
1302 *datapointer = data;
1305 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
1308 com_token[len++] = *data++;
1310 *datapointer = data;
1316 for (;!ISWHITESPACE(*data) && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
1317 if (len < (int)sizeof(com_token) - 1)
1318 com_token[len++] = *data;
1320 *datapointer = data;
1327 COM_ParseToken_Console
1329 Parse a token out of a string, behaving like the qwcl console
1332 int COM_ParseToken_Console(const char **datapointer)
1335 const char *data = *datapointer;
1342 *datapointer = NULL;
1348 for (;ISWHITESPACE(*data);data++)
1353 *datapointer = NULL;
1358 if (*data == '/' && data[1] == '/')
1361 while (*data && *data != '\n' && *data != '\r')
1365 else if (*data == '\"')
1368 for (data++;*data && *data != '\"';data++)
1370 // allow escaped " and \ case
1371 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
1373 if (len < (int)sizeof(com_token) - 1)
1374 com_token[len++] = *data;
1379 *datapointer = data;
1384 for (;!ISWHITESPACE(*data);data++)
1385 if (len < (int)sizeof(com_token) - 1)
1386 com_token[len++] = *data;
1388 *datapointer = data;
1399 Returns the position (1 to argc-1) in the program's argument list
1400 where the given parameter apears, or 0 if not present
1403 int COM_CheckParm (const char *parm)
1407 for (i=1 ; i<com_argc ; i++)
1410 continue; // NEXTSTEP sometimes clears appkit vars.
1411 if (!strcmp (parm,com_argv[i]))
1418 //===========================================================================
1422 typedef struct gamemode_info_s
1424 const char* prog_name;
1425 const char* cmdline;
1426 const char* gamename;
1427 const char* gamedirname1;
1428 const char* gamedirname2;
1429 const char* gamescreenshotname;
1430 const char* gameuserdirname;
1433 static const gamemode_info_t gamemode_info [GAME_COUNT] =
1434 {// prog_name cmdline gamename basegame modgame screenshotprefix userdir
1437 // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
1438 { "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
1440 // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
1441 { "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
1443 // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
1444 { "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
1446 // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
1447 { "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
1449 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
1450 { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
1452 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
1453 { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
1455 // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
1456 { "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
1458 // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
1459 { "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
1461 // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
1462 { "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
1464 // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
1465 { "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
1467 // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
1468 { "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
1470 // COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
1471 { "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
1473 // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
1474 { "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
1476 // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
1477 { "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
1479 // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
1480 { "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
1482 // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
1483 { "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
1485 // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
1486 { "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
1488 // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
1489 { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
1490 // GAME_DEFEATINDETAIL2
1491 // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
1492 { "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
1494 // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
1495 { "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
1496 // GAME_CONTAGIONTHEORY
1497 // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
1498 { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
1500 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
1501 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
1503 // COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
1504 { "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
1506 // COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
1507 { "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" },
1508 // GAME_BLOODOMNICIDE
1509 // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
1510 { "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
1513 void COM_InitGameType (void)
1515 char name [MAX_OSPATH];
1518 FS_StripExtension (com_argv[0], name, sizeof (name));
1519 COM_ToLowerString (name, name, sizeof (name));
1521 // Check the binary name; default to GAME_NORMAL (0)
1522 gamemode = GAME_NORMAL;
1523 for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1524 if (strstr (name, gamemode_info[i].prog_name))
1526 gamemode = (gamemode_t)i;
1530 // Look for a command-line option
1531 for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
1532 if (COM_CheckParm (gamemode_info[i].cmdline))
1534 gamemode = (gamemode_t)i;
1538 gamename = gamemode_info[gamemode].gamename;
1539 gamedirname1 = gamemode_info[gamemode].gamedirname1;
1540 gamedirname2 = gamemode_info[gamemode].gamedirname2;
1541 gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
1542 gameuserdirname = gamemode_info[gamemode].gameuserdirname;
1551 void COM_Init_Commands (void)
1554 char com_cmdline[MAX_INPUTLINE];
1556 Cvar_RegisterVariable (®istered);
1557 Cvar_RegisterVariable (&cmdline);
1559 // reconstitute the command line for the cmdline externally visible cvar
1561 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
1564 if (strstr(com_argv[j], " "))
1566 // arg contains whitespace, store quotes around it
1567 com_cmdline[n++] = '\"';
1568 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1569 com_cmdline[n++] = com_argv[j][i++];
1570 com_cmdline[n++] = '\"';
1574 while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
1575 com_cmdline[n++] = com_argv[j][i++];
1577 if (n < ((int)sizeof(com_cmdline) - 1))
1578 com_cmdline[n++] = ' ';
1583 Cvar_Set ("cmdline", com_cmdline);
1590 does a varargs printf into a temp buffer, so I don't need to have
1591 varargs versions of all text functions.
1592 FIXME: make this buffer size safe someday
1595 char *va(const char *format, ...)
1598 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1599 static char string[8][1024], *s;
1600 static int stringindex = 0;
1602 s = string[stringindex];
1603 stringindex = (stringindex + 1) & 7;
1604 va_start (argptr, format);
1605 dpvsnprintf (s, sizeof (string[0]), format,argptr);
1612 //======================================
1614 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1620 # define snprintf _snprintf
1621 # define vsnprintf _vsnprintf
1625 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1630 va_start (args, format);
1631 result = dpvsnprintf (buffer, buffersize, format, args);
1638 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1642 #if _MSC_VER >= 1400
1643 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1645 result = vsnprintf (buffer, buffersize, format, args);
1647 if (result < 0 || (size_t)result >= buffersize)
1649 buffer[buffersize - 1] = '\0';
1657 //======================================
1659 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1664 while (*in && size_out > 1)
1666 if (*in >= 'A' && *in <= 'Z')
1667 *out++ = *in++ + 'a' - 'A';
1675 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1680 while (*in && size_out > 1)
1682 if (*in >= 'a' && *in <= 'z')
1683 *out++ = *in++ + 'A' - 'a';
1691 int COM_StringBeginsWith(const char *s, const char *match)
1693 for (;*s && *match;s++, match++)
1699 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1701 int argc, commentprefixlength;
1705 tokenbufend = tokenbuf + tokenbufsize;
1707 commentprefixlength = 0;
1709 commentprefixlength = (int)strlen(commentprefix);
1710 while (*l && *l != '\n' && *l != '\r')
1712 if (!ISWHITESPACE(*l))
1714 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1716 while (*l && *l != '\n' && *l != '\r')
1720 if (argc >= maxargc)
1722 argv[argc++] = tokenbuf;
1726 while (*l && *l != '"')
1728 if (tokenbuf >= tokenbufend)
1737 while (!ISWHITESPACE(*l))
1739 if (tokenbuf >= tokenbufend)
1744 if (tokenbuf >= tokenbufend)
1765 COM_StringLengthNoColors
1767 calculates the visible width of a color coded string.
1769 *valid is filled with TRUE if the string is a valid colored string (that is, if
1770 it does not end with an unfinished color code). If it gets filled with FALSE, a
1771 fix would be adding a STRING_COLOR_TAG at the end of the string.
1773 valid can be set to NULL if the caller doesn't care.
1775 For size_s, specify the maximum number of characters from s to use, or 0 to use
1776 all characters until the zero terminator.
1780 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1782 const char *end = size_s ? (s + size_s) : NULL;
1786 switch((s == end) ? 0 : *s)
1792 case STRING_COLOR_TAG:
1794 switch((s == end) ? 0 : *s)
1796 case STRING_COLOR_RGB_TAG_CHAR:
1797 if (s+1 != end && isxdigit(s[1]) &&
1798 s+2 != end && isxdigit(s[2]) &&
1799 s+3 != end && isxdigit(s[3]) )
1804 ++len; // STRING_COLOR_TAG
1805 ++len; // STRING_COLOR_RGB_TAG_CHAR
1807 case 0: // ends with unfinished color code!
1812 case STRING_COLOR_TAG: // escaped ^
1815 case '0': case '1': case '2': case '3': case '4':
1816 case '5': case '6': case '7': case '8': case '9': // color code
1818 default: // not a color code
1819 ++len; // STRING_COLOR_TAG
1820 ++len; // the character
1835 COM_StringDecolorize
1837 removes color codes from a string.
1839 If escape_carets is true, the resulting string will be safe for printing. If
1840 escape_carets is false, the function will just strip color codes (for logging
1843 If the output buffer size did not suffice for converting, the function returns
1844 FALSE. Generally, if escape_carets is false, the output buffer needs
1845 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1846 bytes. In any case, the function makes sure that the resulting string is
1849 For size_in, specify the maximum number of characters from in to use, or 0 to use
1850 all characters until the zero terminator.
1854 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1856 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0)
1857 const char *end = size_in ? (in + size_in) : NULL;
1862 switch((in == end) ? 0 : *in)
1867 case STRING_COLOR_TAG:
1869 switch((in == end) ? 0 : *in)
1871 case STRING_COLOR_RGB_TAG_CHAR:
1872 if (in+1 != end && isxdigit(in[1]) &&
1873 in+2 != end && isxdigit(in[2]) &&
1874 in+3 != end && isxdigit(in[3]) )
1879 APPEND(STRING_COLOR_TAG);
1881 APPEND(STRING_COLOR_TAG);
1882 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1884 case 0: // ends with unfinished color code!
1885 APPEND(STRING_COLOR_TAG);
1886 // finish the code by appending another caret when escaping
1888 APPEND(STRING_COLOR_TAG);
1891 case STRING_COLOR_TAG: // escaped ^
1892 APPEND(STRING_COLOR_TAG);
1893 // append a ^ twice when escaping
1895 APPEND(STRING_COLOR_TAG);
1897 case '0': case '1': case '2': case '3': case '4':
1898 case '5': case '6': case '7': case '8': case '9': // color code
1900 default: // not a color code
1901 APPEND(STRING_COLOR_TAG);
1916 // written by Elric, thanks Elric!
1917 char *SearchInfostring(const char *infostring, const char *key)
1919 static char value [MAX_INPUTLINE];
1920 char crt_key [MAX_INPUTLINE];
1921 size_t value_ind, key_ind;
1924 if (*infostring++ != '\\')
1939 if (c == '\\' || key_ind == sizeof (crt_key) - 1)
1941 crt_key[key_ind] = '\0';
1945 crt_key[key_ind++] = c;
1948 // If it's the key we are looking for, save it in "value"
1949 if (!strcmp(crt_key, key))
1955 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
1957 value[value_ind] = '\0';
1961 value[value_ind++] = c;
1965 // Else, skip the value
1978 void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1986 keylength = strlen(key);
1987 if (valuelength < 1 || !value)
1989 Con_Printf("InfoString_GetValue: no room in value\n");
1993 if (strchr(key, '\\'))
1995 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1998 if (strchr(key, '\"'))
2000 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
2005 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
2008 while (buffer[pos] == '\\')
2010 if (!memcmp(buffer + pos+1, key, keylength))
2012 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2014 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
2015 value[j] = buffer[pos+j];
2019 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2020 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2022 // if we reach this point the key was not found
2025 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
2033 keylength = strlen(key);
2034 if (strchr(key, '\\') || strchr(value, '\\'))
2036 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
2039 if (strchr(key, '\"') || strchr(value, '\"'))
2041 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
2046 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
2049 while (buffer[pos] == '\\')
2051 if (!memcmp(buffer + pos+1, key, keylength))
2053 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2054 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
2056 // if we found the key, find the end of it because we will be replacing it
2058 if (buffer[pos] == '\\')
2060 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2061 for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
2063 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
2065 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
2068 if (value && value[0])
2070 // set the key/value and append the remaining text
2071 char tempbuffer[4096];
2072 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
2073 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
2077 // just remove the key from the text
2078 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
2082 void InfoString_Print(char *buffer)
2089 if (*buffer != '\\')
2091 Con_Printf("InfoString_Print: corrupt string\n");
2094 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2095 if (i < (int)sizeof(key)-1)
2098 if (*buffer != '\\')
2100 Con_Printf("InfoString_Print: corrupt string\n");
2103 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
2104 if (i < (int)sizeof(value)-1)
2105 value[i++] = *buffer;
2107 // empty value is an error case
2108 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
2112 //========================================================
2113 // strlcat and strlcpy, from OpenBSD
2116 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
2118 * Permission to use, copy, modify, and distribute this software for any
2119 * purpose with or without fee is hereby granted, provided that the above
2120 * copyright notice and this permission notice appear in all copies.
2122 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
2123 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2124 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2125 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2126 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2127 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2128 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2131 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
2132 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
2135 #ifndef HAVE_STRLCAT
2137 strlcat(char *dst, const char *src, size_t siz)
2139 register char *d = dst;
2140 register const char *s = src;
2141 register size_t n = siz;
2144 /* Find the end of dst and adjust bytes left but don't go past end */
2145 while (n-- != 0 && *d != '\0')
2151 return(dlen + strlen(s));
2152 while (*s != '\0') {
2161 return(dlen + (s - src)); /* count does not include NUL */
2163 #endif // #ifndef HAVE_STRLCAT
2166 #ifndef HAVE_STRLCPY
2168 strlcpy(char *dst, const char *src, size_t siz)
2170 register char *d = dst;
2171 register const char *s = src;
2172 register size_t n = siz;
2174 /* Copy as many bytes as will fit */
2175 if (n != 0 && --n != 0) {
2177 if ((*d++ = *s++) == 0)
2182 /* Not enough room in dst, add NUL and traverse rest of src */
2185 *d = '\0'; /* NUL-terminate dst */
2190 return(s - src - 1); /* count does not include NUL */
2193 #endif // #ifndef HAVE_STRLCPY
2195 void FindFraction(double val, int *num, int *denom, int denomMax)
2200 bestdiff = fabs(val);
2204 for(i = 1; i <= denomMax; ++i)
2206 int inum = (int) floor(0.5 + val * i);
2207 double diff = fabs(val - inum / (double)i);