X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=common.c;h=e29a00ab4b44136e15d931d76be0f60ad412b1d6;hp=75c7ae9bb276527be9e027152cd58a23aa700564;hb=2cd6ce23bdc219b0dc059e27f8e803ffeda698f3;hpb=71c88e4a876a4cfcccf312e3cd4ddbb1ced6d11f diff --git a/common.c b/common.c index 75c7ae9b..e29a00ab 100644 --- a/common.c +++ b/common.c @@ -19,31 +19,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // common.c -- misc functions used in client and server +#include "quakedef.h" + #include #include #ifndef WIN32 #include #endif -#include "quakedef.h" - -cvar_t registered = {0, "registered","0"}; -cvar_t cmdline = {0, "cmdline","0"}; +cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"}; +cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"}; -extern qboolean fs_modified; // set true if using non-id files - -char com_token[1024]; +char com_token[MAX_INPUTLINE]; int com_argc; const char **com_argv; -// LordHavoc: made commandline 1024 characters instead of 256 -#define CMDLINE_LENGTH 1024 -char com_cmdline[CMDLINE_LENGTH]; - -int gamemode; +gamemode_t gamemode; const char *gamename; -const char *gamedirname; +const char *gamedirname1; +const char *gamedirname2; const char *gamescreenshotname; +const char *gameuserdirname; char com_modname[MAX_OSPATH] = ""; @@ -57,7 +53,7 @@ char com_modname[MAX_OSPATH] = ""; short ShortSwap (short l) { - qbyte b1,b2; + unsigned char b1,b2; b1 = l&255; b2 = (l>>8)&255; @@ -67,7 +63,7 @@ short ShortSwap (short l) int LongSwap (int l) { - qbyte b1,b2,b3,b4; + unsigned char b1,b2,b3,b4; b1 = l&255; b2 = (l>>8)&255; @@ -82,7 +78,7 @@ float FloatSwap (float f) union { float f; - qbyte b[4]; + unsigned char b[4]; } dat1, dat2; @@ -97,22 +93,22 @@ float FloatSwap (float f) // Extract integers from buffers -unsigned int BuffBigLong (const qbyte *buffer) +unsigned int BuffBigLong (const unsigned char *buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } -unsigned short BuffBigShort (const qbyte *buffer) +unsigned short BuffBigShort (const unsigned char *buffer) { return (buffer[0] << 8) | buffer[1]; } -unsigned int BuffLittleLong (const qbyte *buffer) +unsigned int BuffLittleLong (const unsigned char *buffer) { return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; } -unsigned short BuffLittleShort (const qbyte *buffer) +unsigned short BuffLittleShort (const unsigned char *buffer) { return (buffer[1] << 8) | buffer[0]; } @@ -169,7 +165,7 @@ static unsigned short crctable[256] = 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; -unsigned short CRC_Block(qbyte *data, int size) +unsigned short CRC_Block(const unsigned char *data, size_t size) { unsigned short crc = CRC_INIT_VALUE; while (size--) @@ -177,6 +173,73 @@ unsigned short CRC_Block(qbyte *data, int size) return crc ^ CRC_XOR_VALUE; } +unsigned short CRC_Block_CaseInsensitive(const unsigned char *data, size_t size) +{ + unsigned short crc = CRC_INIT_VALUE; + while (size--) + crc = (crc << 8) ^ crctable[(crc >> 8) ^ (tolower(*data++))]; + return crc ^ CRC_XOR_VALUE; +} + +// QuakeWorld +static unsigned char chktbl[1024 + 4] = +{ + 0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01, + 0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a, + 0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58, + 0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3, + 0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b, + 0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36, + 0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8, + 0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27, + 0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd, + 0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63, + 0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2, + 0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d, + 0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65, + 0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f, + 0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f, + 0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42, + 0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59, + 0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9, + 0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69, + 0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba, + 0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85, + 0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4, + 0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8, + 0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa, + 0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05, + 0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7, + 0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a, + 0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb, + 0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b, + 0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d, + 0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce, + 0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3, + + // map checksum goes here + 0x00,0x00,0x00,0x00 +}; + +// QuakeWorld +unsigned char COM_BlockSequenceCRCByteQW(unsigned char *base, int length, int sequence) +{ + unsigned char *p; + unsigned char chkb[60 + 4]; + + p = chktbl + (sequence % (sizeof(chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy(chkb, base, length); + + chkb[length] = (sequence & 0xff) ^ p[0]; + chkb[length+1] = p[1]; + chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkb[length+3] = p[3]; + + return CRC_Block(chkb, length + 4) & 0xff; +} /* ============================================================================== @@ -193,7 +256,7 @@ Handles byte ordering and avoids alignment errors void MSG_WriteChar (sizebuf_t *sb, int c) { - qbyte *buf; + unsigned char *buf; buf = SZ_GetSpace (sb, 1); buf[0] = c; @@ -201,7 +264,7 @@ void MSG_WriteChar (sizebuf_t *sb, int c) void MSG_WriteByte (sizebuf_t *sb, int c) { - qbyte *buf; + unsigned char *buf; buf = SZ_GetSpace (sb, 1); buf[0] = c; @@ -209,7 +272,7 @@ void MSG_WriteByte (sizebuf_t *sb, int c) void MSG_WriteShort (sizebuf_t *sb, int c) { - qbyte *buf; + unsigned char *buf; buf = SZ_GetSpace (sb, 2); buf[0] = c&0xff; @@ -218,7 +281,7 @@ void MSG_WriteShort (sizebuf_t *sb, int c) void MSG_WriteLong (sizebuf_t *sb, int c) { - qbyte *buf; + unsigned char *buf; buf = SZ_GetSpace (sb, 4); buf[0] = c&0xff; @@ -239,15 +302,21 @@ void MSG_WriteFloat (sizebuf_t *sb, float f) dat.f = f; dat.l = LittleLong (dat.l); - SZ_Write (sb, &dat.l, 4); + SZ_Write (sb, (unsigned char *)&dat.l, 4); } void MSG_WriteString (sizebuf_t *sb, const char *s) { - if (!s) - SZ_Write (sb, "", 1); + if (!s || !*s) + MSG_WriteChar (sb, 0); else - SZ_Write (sb, s, strlen(s)+1); + SZ_Write (sb, (unsigned char *)s, (int)strlen(s)+1); +} + +void MSG_WriteUnterminatedString (sizebuf_t *sb, const char *s) +{ + if (s && *s) + SZ_Write (sb, (unsigned char *)s, (int)strlen(s)); } void MSG_WriteCoord13i (sizebuf_t *sb, float f) @@ -271,19 +340,19 @@ void MSG_WriteCoord32f (sizebuf_t *sb, float f) MSG_WriteFloat (sb, f); } -void MSG_WriteCoord (sizebuf_t *sb, float f, int protocol) +void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_NEHAHRAMOVIE) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD) MSG_WriteCoord13i (sb, f); - else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6) + else if (protocol == PROTOCOL_DARKPLACES1) MSG_WriteCoord32f (sb, f); else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4) MSG_WriteCoord16i (sb, f); else - Host_Error("MSG_WriteCoord: unknown protocol\n"); + MSG_WriteCoord32f (sb, f); } -void MSG_WriteVector (sizebuf_t *sb, float *v, int protocol) +void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol) { MSG_WriteCoord (sb, v[0], protocol); MSG_WriteCoord (sb, v[1], protocol); @@ -312,12 +381,12 @@ void MSG_WriteAngle32f (sizebuf_t *sb, float f) MSG_WriteFloat (sb, f); } -void MSG_WriteAngle (sizebuf_t *sb, float f, int protocol) +void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6) - MSG_WriteAngle16i (sb, f); - else + 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) MSG_WriteAngle8i (sb, f); + else + MSG_WriteAngle16i (sb, f); } // @@ -412,9 +481,9 @@ float MSG_ReadBigFloat (void) char *MSG_ReadString (void) { - static char string[2048]; + static char string[MAX_INPUTLINE]; int l,c; - for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++) + for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++) string[l] = c; string[l] = 0; return string; @@ -423,7 +492,7 @@ char *MSG_ReadString (void) int MSG_ReadBytes (int numbytes, unsigned char *out) { int l, c; - for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++) + for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++) out[l] = c; return l; } @@ -443,19 +512,19 @@ float MSG_ReadCoord32f (void) return MSG_ReadLittleFloat(); } -float MSG_ReadCoord (int protocol) +float MSG_ReadCoord (protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_NEHAHRAMOVIE) + if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD) return MSG_ReadCoord13i(); - else if (protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6) + else if (protocol == PROTOCOL_DARKPLACES1) return MSG_ReadCoord32f(); else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4) return MSG_ReadCoord16i(); - Host_Error("MSG_ReadCoord: unknown protocol\n"); - return 0; + else + return MSG_ReadCoord32f(); } -void MSG_ReadVector (float *v, int protocol) +void MSG_ReadVector (float *v, protocolversion_t protocol) { v[0] = MSG_ReadCoord(protocol); v[1] = MSG_ReadCoord(protocol); @@ -478,52 +547,33 @@ float MSG_ReadAngle32f (void) return MSG_ReadFloat (); } -float MSG_ReadAngle (int protocol) +float MSG_ReadAngle (protocolversion_t protocol) { - if (protocol == PROTOCOL_DARKPLACES5 || protocol == PROTOCOL_DARKPLACES6) - return MSG_ReadAngle16i (); - else + 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) return MSG_ReadAngle8i (); + else + return MSG_ReadAngle16i (); } //=========================================================================== -void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name) -{ - if (startsize < 256) - startsize = 256; - buf->mempool = Mem_AllocPool(name, 0, NULL); - buf->data = Mem_Alloc(buf->mempool, startsize); - buf->maxsize = startsize; - buf->cursize = 0; -} - - -void SZ_Free (sizebuf_t *buf) -{ - Mem_FreePool(&buf->mempool); - buf->data = NULL; - buf->maxsize = 0; - buf->cursize = 0; -} - void SZ_Clear (sizebuf_t *buf) { buf->cursize = 0; } -void *SZ_GetSpace (sizebuf_t *buf, int length) +unsigned char *SZ_GetSpace (sizebuf_t *buf, int length) { - void *data; + unsigned char *data; if (buf->cursize + length > buf->maxsize) { if (!buf->allowoverflow) - Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n"); + Host_Error ("SZ_GetSpace: overflow without allowoverflow set"); if (length > buf->maxsize) - Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length); + Host_Error ("SZ_GetSpace: %i is > full buffer size", length); buf->overflowed = true; Con_Print("SZ_GetSpace: overflow\n"); @@ -536,7 +586,7 @@ void *SZ_GetSpace (sizebuf_t *buf, int length) return data; } -void SZ_Write (sizebuf_t *buf, const void *data, int length) +void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length) { memcpy (SZ_GetSpace(buf,length),data,length); } @@ -546,12 +596,12 @@ void SZ_Write (sizebuf_t *buf, const void *data, int length) // all of darkplaces. static char *hexchar = "0123456789ABCDEF"; -void Com_HexDumpToConsole(const qbyte *data, int size) +void Com_HexDumpToConsole(const unsigned char *data, int size) { int i, j, n; char text[1024]; char *cur, *flushpointer; - const qbyte *d; + const unsigned char *d; cur = text; flushpointer = text + 512; for (i = 0;i < size;) @@ -587,7 +637,13 @@ void Com_HexDumpToConsole(const qbyte *data, int size) { if (j < n) { - if (d[j] >= ' ' && d[j] <= 127) + // color change prefix character has to be treated specially + if (d[j] == STRING_COLOR_TAG) + { + *cur++ = STRING_COLOR_TAG; + *cur++ = STRING_COLOR_TAG; + } + else if (d[j] >= (unsigned char) ' ') *cur++ = d[j]; else *cur++ = '.'; @@ -614,17 +670,315 @@ void SZ_HexDumpToConsole(const sizebuf_t *buf) //============================================================================ +/* +============== +COM_Wordwrap + +Word wraps a string. The wordWidth function is guaranteed to be called exactly +once for each word in the string, so it may be stateful, no idea what that +would be good for any more. At the beginning of the string, it will be called +for the char 0 to initialize a clean state, and then once with the string " " +(a space) so the routine knows how long a space is. + +Wrapped lines get the isContinuation flag set and are continuationWidth less wide. + +The sum of the return values of the processLine function will be returned. +============== +*/ +int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL) +{ + // Logic is as follows: + // + // For each word or whitespace: + // Newline found? Output current line, advance to next line. This is not a continuation. Continue. + // Space found? Always add it to the current line, no matter if it fits. + // Word found? Check if current line + current word fits. + // If it fits, append it. Continue. + // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue. + + qboolean isContinuation = false; + float spaceWidth; + const char *startOfLine = string; + const char *cursor = string; + const char *end = string + length; + float spaceUsedInLine = 0; + float spaceUsedForWord; + int result = 0; + size_t wordLen; + size_t dummy; + + dummy = 0; + wordWidth(passthroughCW, NULL, &dummy, -1); + dummy = 1; + spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1); + + for(;;) + { + char ch = (cursor < end) ? *cursor : 0; + switch(ch) + { + case 0: // end of string + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + isContinuation = false; + goto out; + break; + case '\n': // end of line + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + isContinuation = false; + ++cursor; + startOfLine = cursor; + break; + case ' ': // space + ++cursor; + spaceUsedInLine += spaceWidth; + break; + default: // word + wordLen = 1; + while(cursor + wordLen < end) + { + switch(cursor[wordLen]) + { + case 0: + case '\n': + case ' ': + goto out_inner; + default: + ++wordLen; + break; + } + } + out_inner: + 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 + if(wordLen < 1) + { + wordLen = 1; + spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself + } + if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine) + { + // we can simply append it + cursor += wordLen; + spaceUsedInLine += spaceUsedForWord; + } + else + { + // output current line + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + isContinuation = true; + startOfLine = cursor; + cursor += wordLen; + spaceUsedInLine = continuationWidth + spaceUsedForWord; + } + } + } + out: + + return result; + +/* + qboolean isContinuation = false; + float currentWordSpace = 0; + const char *currentWord = 0; + float minReserve = 0; + + float spaceUsedInLine = 0; + const char *currentLine = 0; + const char *currentLineEnd = 0; + float currentLineFinalWhitespace = 0; + const char *p; + + int result = 0; + minReserve = charWidth(passthroughCW, 0); + minReserve += charWidth(passthroughCW, ' '); + + if(maxWidth < continuationWidth + minReserve) + maxWidth = continuationWidth + minReserve; + + charWidth(passthroughCW, 0); + + for(p = string; p < string + length; ++p) + { + char c = *p; + float w = charWidth(passthroughCW, c); + + if(!currentWord) + { + currentWord = p; + currentWordSpace = 0; + } + + if(!currentLine) + { + currentLine = p; + spaceUsedInLine = isContinuation ? continuationWidth : 0; + currentLineEnd = 0; + } + + if(c == ' ') + { + // 1. I can add the word AND a space - then just append it. + if(spaceUsedInLine + currentWordSpace + w <= maxWidth) + { + currentLineEnd = p; // note: space not included here + currentLineFinalWhitespace = w; + spaceUsedInLine += currentWordSpace + w; + } + // 2. I can just add the word - then append it, output current line and go to next one. + else if(spaceUsedInLine + currentWordSpace <= maxWidth) + { + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + currentLine = 0; + isContinuation = true; + } + // 3. Otherwise, output current line and go to next one, where I can add the word. + else if(continuationWidth + currentWordSpace + w <= maxWidth) + { + if(currentLineEnd) + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + currentLine = currentWord; + spaceUsedInLine = continuationWidth + currentWordSpace + w; + currentLineEnd = p; + currentLineFinalWhitespace = w; + isContinuation = true; + } + // 4. We can't even do that? Then output both current and next word as new lines. + else + { + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + currentLine = 0; + isContinuation = true; + } + currentWord = 0; + } + else if(c == '\n') + { + // 1. I can add the word - then do it. + if(spaceUsedInLine + currentWordSpace <= maxWidth) + { + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + } + // 2. Otherwise, output current line, next one and make tabula rasa. + else + { + if(currentLineEnd) + { + processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + } + currentWord = 0; + currentLine = 0; + isContinuation = false; + } + else + { + currentWordSpace += w; + if( + spaceUsedInLine + currentWordSpace > maxWidth // can't join this line... + && + continuationWidth + currentWordSpace > maxWidth // can't join any other line... + ) + { + // this word cannot join ANY line... + // so output the current line... + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + + // then this word's beginning... + if(isContinuation) + { + // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces + float pieceWidth = maxWidth - continuationWidth; + const char *pos = currentWord; + currentWordSpace = 0; + + // reset the char width function to a state where no kerning occurs (start of word) + charWidth(passthroughCW, ' '); + while(pos <= p) + { + float w = charWidth(passthroughCW, *pos); + if(currentWordSpace + w > pieceWidth) // this piece won't fit any more + { + // print everything until it + result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true); + // go to here + currentWord = pos; + currentWordSpace = 0; + } + currentWordSpace += w; + ++pos; + } + // now we have a currentWord that fits... set up its next line + // currentWordSpace has been set + // currentWord has been set + spaceUsedInLine = continuationWidth; + currentLine = currentWord; + currentLineEnd = 0; + isContinuation = true; + } + else + { + // we have a guarantee that it will fix (see if clause) + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation); + + // and use the rest of this word as new start of a line + currentWordSpace = w; + currentWord = p; + spaceUsedInLine = continuationWidth; + currentLine = p; + currentLineEnd = 0; + isContinuation = true; + } + } + } + } + + if(!currentWord) + { + currentWord = p; + currentWordSpace = 0; + } + + if(currentLine) // Same procedure as \n + { + // Can I append the current word? + if(spaceUsedInLine + currentWordSpace <= maxWidth) + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + else + { + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + } + } + + return result; +*/ +} /* ============== -COM_ParseToken +COM_ParseToken_Simple Parse a token out of a string ============== */ -int COM_ParseToken(const char **datapointer, int returnnewline) +int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash) { int len; + int c; const char *data = *datapointer; len = 0; @@ -638,7 +992,11 @@ int COM_ParseToken(const char **datapointer, int returnnewline) // skip whitespace skipwhite: - for (;*data <= ' ' && (*data != '\n' || !returnnewline);data++) + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { if (*data == 0) { @@ -648,10 +1006,14 @@ skipwhite: } } + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + if (data[0] == '/' && data[1] == '/') { // comment - while (*data && *data != '\n') + while (*data && *data != '\n' && *data != '\r') data++; goto skipwhite; } @@ -661,29 +1023,45 @@ skipwhite: data++; while (*data && (data[0] != '*' || data[1] != '/')) data++; - data += 2; + if (*data) + data++; + if (*data) + data++; goto skipwhite; } else if (*data == '\"') { // quoted string - for (data++;*data != '\"';data++) + for (data++;*data && *data != '\"';data++) { - if (*data == '\\' && data[1] == '"' ) - data++; - if (!*data || len >= (int)sizeof(com_token) - 1) + c = *data; + if (*data == '\\' && parsebackslash) { - com_token[0] = 0; - *datapointer = NULL; - return false; + data++; + c = *data; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; } - com_token[len++] = *data; + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = c; } com_token[len] = 0; - *datapointer = data+1; + if (*data == '\"') + data++; + *datapointer = data; + return true; + } + else if (*data == '\r') + { + // translate Mac line ending to UNIX + com_token[len++] = '\n';data++; + com_token[len] = 0; + *datapointer = data; return true; } - else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == '\'' || *data == ':' || *data == ',' || *data == ';') + else if (*data == '\n') { // single character com_token[len++] = *data++; @@ -694,16 +1072,9 @@ skipwhite: else { // regular word - for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != '\'' && *data != ':' && *data != ',' && *data != ';';data++) - { - if (len >= (int)sizeof(com_token) - 1) - { - com_token[0] = 0; - *datapointer = NULL; - return false; - } - com_token[len++] = *data; - } + for (;!ISWHITESPACE(*data);data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; com_token[len] = 0; *datapointer = data; return true; @@ -712,14 +1083,15 @@ skipwhite: /* ============== -COM_ParseTokenConsole +COM_ParseToken_QuakeC -Parse a token out of a string, behaving like the qwcl console +Parse a token out of a string ============== */ -int COM_ParseTokenConsole(const char **datapointer) +int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline) { int len; + int c; const char *data = *datapointer; len = 0; @@ -733,7 +1105,11 @@ int COM_ParseTokenConsole(const char **datapointer) // skip whitespace skipwhite: - for (;*data <= ' ';data++) + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { if (*data == 0) { @@ -743,206 +1119,379 @@ skipwhite: } } - if (*data == '/' && data[1] == '/') + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + + if (data[0] == '/' && data[1] == '/') { // comment - while (*data && *data != '\n') + while (*data && *data != '\n' && *data != '\r') data++; goto skipwhite; } - else if (*data == '\"') + else if (data[0] == '/' && data[1] == '*') + { + // comment + data++; + while (*data && (data[0] != '*' || data[1] != '/')) + data++; + if (*data) + data++; + if (*data) + data++; + goto skipwhite; + } + else if (*data == '\"' || *data == '\'') { // quoted string - for (data++;*data != '\"';data++) + char quote = *data; + for (data++;*data && *data != quote;data++) { - if (!*data || len >= (int)sizeof(com_token) - 1) + c = *data; + if (*data == '\\') { - com_token[0] = 0; - *datapointer = NULL; - return false; + data++; + c = *data; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; } - com_token[len++] = *data; + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = c; } com_token[len] = 0; - *datapointer = data+1; + if (*data == quote) + data++; + *datapointer = data; + return true; + } + else if (*data == '\r') + { + // translate Mac line ending to UNIX + com_token[len++] = '\n';data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';') + { + // single character + com_token[len++] = *data++; + com_token[len] = 0; + *datapointer = data; return true; } else { // regular word - for (;*data > ' ';data++) - { - if (len >= (int)sizeof(com_token) - 1) - { - com_token[0] = 0; - *datapointer = NULL; - return false; - } - com_token[len++] = *data; - } + for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; com_token[len] = 0; *datapointer = data; return true; } } - /* -================ -COM_CheckParm +============== +COM_ParseToken_VM_Tokenize -Returns the position (1 to argc-1) in the program's argument list -where the given parameter apears, or 0 if not present -================ +Parse a token out of a string +============== */ -int COM_CheckParm (const char *parm) +int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline) { - int i; + int len; + int c; + const char *data = *datapointer; - for (i=1 ; i= 1400 + result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args); +#else + result = vsnprintf (buffer, buffersize, format, args); +#endif + if (result < 0 || (size_t)result >= buffersize) + { + buffer[buffersize - 1] = '\0'; + return -1; + } + + return result; +} + + //====================================== void COM_ToLowerString (const char *in, char *out, size_t size_out) @@ -1075,14 +1690,14 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t l = *text; commentprefixlength = 0; if (commentprefix) - commentprefixlength = strlen(commentprefix); - while (*l && *l != '\n') + commentprefixlength = (int)strlen(commentprefix); + while (*l && *l != '\n' && *l != '\r') { - if (*l > ' ') + if (!ISWHITESPACE(*l)) { if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength)) { - while (*l && *l != '\n') + while (*l && *l != '\n' && *l != '\r') l++; break; } @@ -1103,7 +1718,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t } else { - while (*l > ' ') + while (!ISWHITESPACE(*l)) { if (tokenbuf >= tokenbufend) return -1; @@ -1117,17 +1732,176 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t else l++; } + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + if (*l == '\r') + l++; if (*l == '\n') l++; *text = l; return argc; } +/* +============ +COM_StringLengthNoColors + +calculates the visible width of a color coded string. + +*valid is filled with TRUE if the string is a valid colored string (that is, if +it does not end with an unfinished color code). If it gets filled with FALSE, a +fix would be adding a STRING_COLOR_TAG at the end of the string. + +valid can be set to NULL if the caller doesn't care. + +For size_s, specify the maximum number of characters from s to use, or 0 to use +all characters until the zero terminator. +============ +*/ +size_t +COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid) +{ + const char *end = size_s ? (s + size_s) : NULL; + size_t len = 0; + for(;;) + { + switch((s == end) ? 0 : *s) + { + case 0: + if(valid) + *valid = TRUE; + return len; + case STRING_COLOR_TAG: + ++s; + switch((s == end) ? 0 : *s) + { + case STRING_COLOR_RGB_TAG_CHAR: + if (s+1 != end && isxdigit(s[1]) && + s+2 != end && isxdigit(s[2]) && + s+3 != end && isxdigit(s[3]) ) + { + s+=3; + break; + } + ++len; // STRING_COLOR_TAG + ++len; // STRING_COLOR_RGB_TAG_CHAR + break; + case 0: // ends with unfinished color code! + ++len; + if(valid) + *valid = FALSE; + return len; + case STRING_COLOR_TAG: // escaped ^ + ++len; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': // color code + break; + default: // not a color code + ++len; // STRING_COLOR_TAG + ++len; // the character + break; + } + break; + default: + ++len; + break; + } + ++s; + } + // never get here +} + +/* +============ +COM_StringDecolorize + +removes color codes from a string. + +If escape_carets is true, the resulting string will be safe for printing. If +escape_carets is false, the function will just strip color codes (for logging +for example). + +If the output buffer size did not suffice for converting, the function returns +FALSE. Generally, if escape_carets is false, the output buffer needs +strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2 +bytes. In any case, the function makes sure that the resulting string is +zero terminated. + +For size_in, specify the maximum number of characters from in to use, or 0 to use +all characters until the zero terminator. +============ +*/ +qboolean +COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets) +{ +#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return FALSE; } } while(0) + const char *end = size_in ? (in + size_in) : NULL; + if(size_out < 1) + return FALSE; + for(;;) + { + switch((in == end) ? 0 : *in) + { + case 0: + *out++ = 0; + return TRUE; + case STRING_COLOR_TAG: + ++in; + switch((in == end) ? 0 : *in) + { + case STRING_COLOR_RGB_TAG_CHAR: + if (in+1 != end && isxdigit(in[1]) && + in+2 != end && isxdigit(in[2]) && + in+3 != end && isxdigit(in[3]) ) + { + in+=3; + break; + } + APPEND(STRING_COLOR_TAG); + if(escape_carets) + APPEND(STRING_COLOR_TAG); + APPEND(STRING_COLOR_RGB_TAG_CHAR); + break; + case 0: // ends with unfinished color code! + APPEND(STRING_COLOR_TAG); + // finish the code by appending another caret when escaping + if(escape_carets) + APPEND(STRING_COLOR_TAG); + *out++ = 0; + return TRUE; + case STRING_COLOR_TAG: // escaped ^ + APPEND(STRING_COLOR_TAG); + // append a ^ twice when escaping + if(escape_carets) + APPEND(STRING_COLOR_TAG); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': // color code + break; + default: // not a color code + APPEND(STRING_COLOR_TAG); + APPEND(*in); + break; + } + break; + default: + APPEND(*in); + break; + } + ++in; + } + // never get here +#undef APPEND +} + // written by Elric, thanks Elric! char *SearchInfostring(const char *infostring, const char *key) { - static char value [256]; - char crt_key [256]; + static char value [MAX_INPUTLINE]; + char crt_key [MAX_INPUTLINE]; size_t value_ind, key_ind; char c; @@ -1185,6 +1959,139 @@ char *SearchInfostring(const char *infostring, const char *key) } } +void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength) +{ + int pos = 0, j; + size_t keylength; + if (!key) + key = ""; + if (!value) + value = ""; + keylength = strlen(key); + if (valuelength < 1 || !value) + { + Con_Printf("InfoString_GetValue: no room in value\n"); + return; + } + value[0] = 0; + if (strchr(key, '\\')) + { + Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key); + return; + } + if (strchr(key, '\"')) + { + Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key); + return; + } + if (!key[0]) + { + Con_Printf("InfoString_GetValue: can not look up a key with no name\n"); + return; + } + while (buffer[pos] == '\\') + { + if (!memcmp(buffer + pos+1, key, keylength)) + { + for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + pos++; + for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++) + value[j] = buffer[pos+j]; + value[j] = 0; + return; + } + for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + } + // if we reach this point the key was not found +} + +void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value) +{ + int pos = 0, pos2; + size_t keylength; + if (!key) + key = ""; + if (!value) + value = ""; + keylength = strlen(key); + if (strchr(key, '\\') || strchr(value, '\\')) + { + Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value); + return; + } + if (strchr(key, '\"') || strchr(value, '\"')) + { + Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value); + return; + } + if (!key[0]) + { + Con_Printf("InfoString_SetValue: can not set a key with no name\n"); + return; + } + while (buffer[pos] == '\\') + { + if (!memcmp(buffer + pos+1, key, keylength)) + break; + for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + } + // if we found the key, find the end of it because we will be replacing it + pos2 = pos; + if (buffer[pos] == '\\') + { + for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++); + for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++); + } + if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2)) + { + Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value); + return; + } + if (value && value[0]) + { + // set the key/value and append the remaining text + char tempbuffer[4096]; + strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer)); + dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer); + } + else + { + // just remove the key from the text + strlcpy(buffer + pos, buffer + pos2, bufferlength - pos); + } +} + +void InfoString_Print(char *buffer) +{ + int i; + char key[2048]; + char value[2048]; + while (*buffer) + { + if (*buffer != '\\') + { + Con_Printf("InfoString_Print: corrupt string\n"); + return; + } + for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++) + if (i < (int)sizeof(key)-1) + key[i++] = *buffer; + key[i] = 0; + if (*buffer != '\\') + { + Con_Printf("InfoString_Print: corrupt string\n"); + return; + } + for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++) + if (i < (int)sizeof(value)-1) + value[i++] = *buffer; + value[i] = 0; + // empty value is an error case + Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE"); + } +} //======================================================== // strlcat and strlcpy, from OpenBSD @@ -1268,3 +2175,25 @@ strlcpy(char *dst, const char *src, size_t siz) } #endif // #ifndef HAVE_STRLCPY + +void FindFraction(double val, int *num, int *denom, int denomMax) +{ + int i; + double bestdiff; + // initialize + bestdiff = fabs(val); + *num = 0; + *denom = 1; + + for(i = 1; i <= denomMax; ++i) + { + int inum = (int) floor(0.5 + val * i); + double diff = fabs(val - inum / (double)i); + if(diff < bestdiff) + { + bestdiff = diff; + *num = inum; + *denom = i; + } + } +}