X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=common.c;h=badf4c9ccaca487160a3af26f8d2f3d14fd870df;hp=79325dc7328d572bdba45739d4439b83976e8689;hb=2075ae43356d724bae305ce8fd36ea570718b14a;hpb=545926ff9670b675714b3b2d8acbfe19e08483f3 diff --git a/common.c b/common.c index 79325dc7..badf4c9c 100644 --- a/common.c +++ b/common.c @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #include "quakedef.h" +#include "utf8lib.h" 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"}; @@ -33,9 +34,11 @@ cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launche char com_token[MAX_INPUTLINE]; int com_argc; const char **com_argv; +int com_selffd = -1; gamemode_t gamemode; const char *gamename; +const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the const char *gamedirname1; const char *gamedirname2; const char *gamescreenshotname; @@ -51,68 +54,78 @@ char com_modname[MAX_OSPATH] = ""; ============================================================================ */ -short ShortSwap (short l) -{ - unsigned char b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - return (b1<<8) + b2; +float BuffBigFloat (const unsigned char *buffer) +{ + union + { + float f; + unsigned int i; + } + u; + u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + return u.f; } -int LongSwap (int l) +int BuffBigLong (const unsigned char *buffer) { - unsigned char b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +short BuffBigShort (const unsigned char *buffer) +{ + return (buffer[0] << 8) | buffer[1]; } -float FloatSwap (float f) +float BuffLittleFloat (const unsigned char *buffer) { union { - float f; - unsigned char b[4]; - } dat1, dat2; - - - dat1.f = f; - dat2.b[0] = dat1.b[3]; - dat2.b[1] = dat1.b[2]; - dat2.b[2] = dat1.b[1]; - dat2.b[3] = dat1.b[0]; - return dat2.f; + float f; + unsigned int i; + } + u; + u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; + return u.f; } +int BuffLittleLong (const unsigned char *buffer) +{ + return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; +} -// Extract integers from buffers - -unsigned int BuffBigLong (const unsigned char *buffer) +short BuffLittleShort (const unsigned char *buffer) { - return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + return (buffer[1] << 8) | buffer[0]; } -unsigned short BuffBigShort (const unsigned char *buffer) +void StoreBigLong (unsigned char *buffer, unsigned int i) { - return (buffer[0] << 8) | buffer[1]; + buffer[0] = (i >> 24) & 0xFF; + buffer[1] = (i >> 16) & 0xFF; + buffer[2] = (i >> 8) & 0xFF; + buffer[3] = i & 0xFF; } -unsigned int BuffLittleLong (const unsigned char *buffer) +void StoreBigShort (unsigned char *buffer, unsigned short i) { - return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; + buffer[0] = (i >> 8) & 0xFF; + buffer[1] = i & 0xFF; } -unsigned short BuffLittleShort (const unsigned char *buffer) +void StoreLittleLong (unsigned char *buffer, unsigned int i) { - return (buffer[1] << 8) | buffer[0]; + buffer[0] = i & 0xFF; + buffer[1] = (i >> 8) & 0xFF; + buffer[2] = (i >> 16) & 0xFF; + buffer[3] = (i >> 24) & 0xFF; } +void StoreLittleShort (unsigned char *buffer, unsigned short i) +{ + buffer[0] = i & 0xFF; + buffer[1] = (i >> 8) & 0xFF; +} /* ============================================================================ @@ -173,6 +186,14 @@ unsigned short CRC_Block(const unsigned char *data, size_t 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] = { @@ -344,7 +365,7 @@ void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol) MSG_WriteCoord32f (sb, f); } -void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol) +void MSG_WriteVector (sizebuf_t *sb, const vec3_t v, protocolversion_t protocol) { MSG_WriteCoord (sb, v[0], protocol); MSG_WriteCoord (sb, v[1], protocol); @@ -384,167 +405,175 @@ void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol) // // reading functions // -int msg_readcount; -qboolean msg_badread; -void MSG_BeginReading (void) +void MSG_InitReadBuffer (sizebuf_t *buf, unsigned char *data, int size) +{ + memset(buf, 0, sizeof(*buf)); + buf->data = data; + buf->maxsize = buf->cursize = size; + MSG_BeginReading(buf); +} + +void MSG_BeginReading(sizebuf_t *sb) { - msg_readcount = 0; - msg_badread = false; + sb->readcount = 0; + sb->badread = false; } -int MSG_ReadLittleShort (void) +int MSG_ReadLittleShort(sizebuf_t *sb) { - if (msg_readcount+2 > net_message.cursize) + if (sb->readcount+2 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 2; - return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8)); + sb->readcount += 2; + return (short)(sb->data[sb->readcount-2] | (sb->data[sb->readcount-1]<<8)); } -int MSG_ReadBigShort (void) +int MSG_ReadBigShort (sizebuf_t *sb) { - if (msg_readcount+2 > net_message.cursize) + if (sb->readcount+2 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 2; - return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]); + sb->readcount += 2; + return (short)((sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]); } -int MSG_ReadLittleLong (void) +int MSG_ReadLittleLong (sizebuf_t *sb) { - if (msg_readcount+4 > net_message.cursize) + if (sb->readcount+4 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 4; - 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); + sb->readcount += 4; + return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24); } -int MSG_ReadBigLong (void) +int MSG_ReadBigLong (sizebuf_t *sb) { - if (msg_readcount+4 > net_message.cursize) + if (sb->readcount+4 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 4; - 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]; + sb->readcount += 4; + return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]; } -float MSG_ReadLittleFloat (void) +float MSG_ReadLittleFloat (sizebuf_t *sb) { union { float f; int l; } dat; - if (msg_readcount+4 > net_message.cursize) + if (sb->readcount+4 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 4; - 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); + sb->readcount += 4; + dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24); return dat.f; } -float MSG_ReadBigFloat (void) +float MSG_ReadBigFloat (sizebuf_t *sb) { union { float f; int l; } dat; - if (msg_readcount+4 > net_message.cursize) + if (sb->readcount+4 > sb->cursize) { - msg_badread = true; + sb->badread = true; return -1; } - msg_readcount += 4; - 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]; + sb->readcount += 4; + dat.l = (sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1]; return dat.f; } -char *MSG_ReadString (void) +char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring) { - static char string[MAX_INPUTLINE]; - int l,c; - for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++) - string[l] = c; + int c; + size_t l = 0; + // read string into sbfer, but only store as many characters as will fit + while ((c = MSG_ReadByte(sb)) > 0) + if (l < maxstring - 1) + string[l++] = c; string[l] = 0; return string; } -int MSG_ReadBytes (int numbytes, unsigned char *out) +int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out) { int l, c; - for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++) + for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++) out[l] = c; return l; } -float MSG_ReadCoord13i (void) +float MSG_ReadCoord13i (sizebuf_t *sb) { - return MSG_ReadLittleShort() * (1.0/8.0); + return MSG_ReadLittleShort(sb) * (1.0/8.0); } -float MSG_ReadCoord16i (void) +float MSG_ReadCoord16i (sizebuf_t *sb) { - return (signed short) MSG_ReadLittleShort(); + return (signed short) MSG_ReadLittleShort(sb); } -float MSG_ReadCoord32f (void) +float MSG_ReadCoord32f (sizebuf_t *sb) { - return MSG_ReadLittleFloat(); + return MSG_ReadLittleFloat(sb); } -float MSG_ReadCoord (protocolversion_t protocol) +float MSG_ReadCoord (sizebuf_t *sb, protocolversion_t protocol) { 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(); + return MSG_ReadCoord13i(sb); else if (protocol == PROTOCOL_DARKPLACES1) - return MSG_ReadCoord32f(); + return MSG_ReadCoord32f(sb); else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4) - return MSG_ReadCoord16i(); + return MSG_ReadCoord16i(sb); else - return MSG_ReadCoord32f(); + return MSG_ReadCoord32f(sb); } -void MSG_ReadVector (float *v, protocolversion_t protocol) +void MSG_ReadVector (sizebuf_t *sb, vec3_t v, protocolversion_t protocol) { - v[0] = MSG_ReadCoord(protocol); - v[1] = MSG_ReadCoord(protocol); - v[2] = MSG_ReadCoord(protocol); + v[0] = MSG_ReadCoord(sb, protocol); + v[1] = MSG_ReadCoord(sb, protocol); + v[2] = MSG_ReadCoord(sb, protocol); } // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem -float MSG_ReadAngle8i (void) +float MSG_ReadAngle8i (sizebuf_t *sb) { - return (signed char) MSG_ReadByte () * (360.0/256.0); + return (signed char) MSG_ReadByte (sb) * (360.0/256.0); } -float MSG_ReadAngle16i (void) +float MSG_ReadAngle16i (sizebuf_t *sb) { - return (signed short)MSG_ReadShort () * (360.0/65536.0); + return (signed short)MSG_ReadShort (sb) * (360.0/65536.0); } -float MSG_ReadAngle32f (void) +float MSG_ReadAngle32f (sizebuf_t *sb) { - return MSG_ReadFloat (); + return MSG_ReadFloat (sb); } -float MSG_ReadAngle (protocolversion_t protocol) +float MSG_ReadAngle (sizebuf_t *sb, protocolversion_t protocol) { 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 (); + return MSG_ReadAngle8i (sb); else - return MSG_ReadAngle16i (); + return MSG_ReadAngle16i (sb); } @@ -587,7 +616,7 @@ void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length) // attention, it has been eradicated from here, its only (former) use in // all of darkplaces. -static char *hexchar = "0123456789ABCDEF"; +static const char *hexchar = "0123456789ABCDEF"; void Com_HexDumpToConsole(const unsigned char *data, int size) { int i, j, n; @@ -635,7 +664,7 @@ void Com_HexDumpToConsole(const unsigned char *data, int size) *cur++ = STRING_COLOR_TAG; *cur++ = STRING_COLOR_TAG; } - else if (d[j] >= ' ') + else if (d[j] >= (unsigned char) ' ') *cur++ = d[j]; else *cur++ = '.'; @@ -662,6 +691,304 @@ 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. + +In case no single character fits into the given width, the wordWidth function +must return the width of exactly one character. + +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); + goto out; + 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) // cannot happen according to current spec of wordWidth + { + 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; +*/ +} /* ============== @@ -670,7 +997,7 @@ COM_ParseToken_Simple Parse a token out of a string ============== */ -int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash) +int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments) { int len; int c; @@ -691,7 +1018,7 @@ skipwhite: // UNIX: \n // Mac: \r // Windows: \r\n - for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { if (*data == 0) { @@ -705,14 +1032,14 @@ skipwhite: if (data[0] == '\r' && data[1] == '\n') data++; - if (data[0] == '/' && data[1] == '/') + if (parsecomments && data[0] == '/' && data[1] == '/') { // comment while (*data && *data != '\n' && *data != '\r') data++; goto skipwhite; } - else if (data[0] == '/' && data[1] == '*') + else if (parsecomments && data[0] == '/' && data[1] == '*') { // comment data++; @@ -767,7 +1094,7 @@ skipwhite: else { // regular word - for (;*data > ' ';data++) + for (;!ISWHITESPACE(*data);data++) if (len < (int)sizeof(com_token) - 1) com_token[len++] = *data; com_token[len] = 0; @@ -804,7 +1131,7 @@ skipwhite: // UNIX: \n // Mac: \r // Windows: \r\n - for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { if (*data == 0) { @@ -881,7 +1208,7 @@ skipwhite: else { // regular word - for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';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; @@ -918,7 +1245,7 @@ skipwhite: // UNIX: \n // Mac: \r // Windows: \r\n - for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { if (*data == 0) { @@ -995,7 +1322,7 @@ skipwhite: else { // regular word - for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';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; @@ -1027,7 +1354,7 @@ int COM_ParseToken_Console(const char **datapointer) // skip whitespace skipwhite: - for (;*data <= ' ';data++) + for (;ISWHITESPACE(*data);data++) { if (*data == 0) { @@ -1063,7 +1390,7 @@ skipwhite: else { // regular word - for (;*data > ' ';data++) + for (;!ISWHITESPACE(*data);data++) if (len < (int)sizeof(com_token) - 1) com_token[len++] = *data; com_token[len] = 0; @@ -1101,115 +1428,174 @@ int COM_CheckParm (const char *parm) // Game mods +gamemode_t com_startupgamemode; +gamemode_t com_startupgamegroup; + typedef struct gamemode_info_s { - const char* prog_name; - const char* cmdline; - const char* gamename; - const char* gamedirname1; - const char* gamedirname2; - const char* gamescreenshotname; - const char* gameuserdirname; + gamemode_t mode; // this gamemode + gamemode_t group; // different games with same group can switch automatically when gamedirs change + const char* prog_name; // not null + const char* cmdline; // not null + const char* gamename; // not null + const char* gamenetworkfiltername; // not null + const char* gamedirname1; // not null + const char* gamedirname2; // null + const char* gamescreenshotname; // not nul + const char* gameuserdirname; // not null } gamemode_info_t; -static const gamemode_info_t gamemode_info [] = -{// prog_name cmdline gamename gamedirname gamescreenshotname - -// GAME_NORMAL -// COMMANDLINEOPTION: Game: -quake runs the game Quake (default) -{ "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, -// GAME_HIPNOTIC -// COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon -{ "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, -// GAME_ROGUE -// COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity -{ "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, -// GAME_NEHAHRA -// COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game -{ "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, -// GAME_NEXUIZ -// COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz -{ "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, -// GAME_TRANSFUSION -// COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake) -{ "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, -// GAME_GOODVSBAD2 -// COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2 -{ "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, -// GAME_TEU -// COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces) -{ "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, -// GAME_BATTLEMECH -// COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech -{ "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, -// GAME_ZYMOTIC -// COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic -{ "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, -// GAME_SETHERAL -// COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral -{ "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" }, -// GAME_SOM -// COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man -{ "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" }, -// GAME_TENEBRAE -// COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented) -{ "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, -// GAME_NEOTERIC -// COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric -{ "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, -// GAME_OPENQUARTZ -// COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content -{ "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, -// GAME_PRYDON -// COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate -{ "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, -// GAME_NETHERWORLD -// COMMANDLINEOPTION: Game: -netherworld runs the game Netherworld: Dark Master -{ "netherworld", "-netherworld", "Netherworld: Dark Master", "id1", "netherworld", "nw", "darkplaces" }, -// GAME_THEHUNTED -// COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted -{ "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" }, -// GAME_DEFEATINDETAIL2 -// COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2 -{ "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" }, -// GAME_DARSANA -// COMMANDLINEOPTION: Game: -darsana runs the game Darsana -{ "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, -// GAME_CONTAGIONTHEORY -// COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory -{ "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" }, +static const gamemode_info_t gamemode_info [GAME_COUNT] = +{// game basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option +{ GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default) +{ GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon +{ GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity +{ GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game +{ GAME_QUOTH, GAME_NORMAL, "quoth", "-quoth", "Darkplaces-Quoth", "Darkplaces-Quoth", "id1", "quoth", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it +{ GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz +{ GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic +{ GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake) +{ GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2 +{ GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces) +{ GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech +{ GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic +{ GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral +{ GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented) +{ GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric +{ GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content +{ GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate +{ GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake +{ GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted +{ GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "Defeat_In_Detail_2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2 +{ GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana +{ GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory +{ GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype +{ GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy +{ GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide +{ GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm +{ GAME_STEELSTORM2, GAME_STEELSTORM2, "steelstorm2", "-steelstorm2", "Steel Storm 2", "Steel_Storm_2", "gamedata", NULL, "ss2", "steelstorm2" }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2 +{ GAME_SSAMMO, GAME_SSAMMO, "steelstorm-ammo", "-steelstormammo", "Steel Storm A.M.M.O.", "Steel_Storm_A.M.M.O.", "gamedata", NULL, "ssammo", "steelstorm-ammo" }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O. +{ GAME_STEELSTORMREVENANTS, GAME_STEELSTORMREVENANTS, "steelstorm-revenants", "-steelstormrev", "Steel Storm: Revenants", "Steel_Storm_Revenants", "base", NULL, "ssrev", "steelstorm-revenants" }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants +{ GAME_TOMESOFMEPHISTOPHELES, GAME_TOMESOFMEPHISTOPHELES, "tomesofmephistopheles","-tomesofmephistopheles", "Tomes of Mephistopheles", "Tomes_of_Mephistopheles", "gamedata", NULL, "tom", "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles +{ GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car +{ GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm +{ GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament }; +static void COM_SetGameType(int index); void COM_InitGameType (void) { char name [MAX_OSPATH]; - unsigned int i; + int i; + int index = 0; + +#ifdef FORCEGAME + COM_ToLowerString(FORCEGAME, name, sizeof (name)); +#else + // check executable filename for keywords, but do it SMARTLY - only check the last path element + FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name)); + COM_ToLowerString(name, name, sizeof (name)); +#endif + for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++) + if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name)) + index = i; + + // check commandline options for keywords + for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++) + if (COM_CheckParm (gamemode_info[i].cmdline)) + index = i; - FS_StripExtension (com_argv[0], name, sizeof (name)); - COM_ToLowerString (name, name, sizeof (name)); + com_startupgamemode = gamemode_info[index].mode; + com_startupgamegroup = gamemode_info[index].group; + COM_SetGameType(index); +} - // Check the binary name; default to GAME_NORMAL (0) - gamemode = GAME_NORMAL; - for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++) - if (strstr (name, gamemode_info[i].prog_name)) +void COM_ChangeGameTypeForGameDirs(void) +{ + int i; + int index = -1; + // this will not not change the gamegroup + // first check if a base game (single gamedir) matches + for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++) + { + if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0])) { - gamemode = (gamemode_t)i; + index = i; break; } - - // Look for a command-line option - for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++) - if (COM_CheckParm (gamemode_info[i].cmdline)) + } + // now that we have a base game, see if there is a matching derivative game (two gamedirs) + if (fs_numgamedirs) + { + for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++) { - gamemode = (gamemode_t)i; - break; + if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2)) + { + index = i; + break; + } } + } + // we now have a good guess at which game this is meant to be... + if (index >= 0 && gamemode != gamemode_info[index].mode) + COM_SetGameType(index); +} + +static void COM_SetGameType(int index) +{ + static char gamenetworkfilternamebuffer[64]; + int i, t; + if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]))) + index = 0; + gamemode = gamemode_info[index].mode; + gamename = gamemode_info[index].gamename; + gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername; + gamedirname1 = gamemode_info[index].gamedirname1; + gamedirname2 = gamemode_info[index].gamedirname2; + gamescreenshotname = gamemode_info[index].gamescreenshotname; + gameuserdirname = gamemode_info[index].gameuserdirname; + + if (gamemode == com_startupgamemode) + { + if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc) + gamename = gamenetworkfiltername = com_argv[t+1]; + if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < com_argc) + gamenetworkfiltername = com_argv[t+1]; + if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc) + gamedirname1 = com_argv[t+1]; + if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc) + gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL; + if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc) + gamescreenshotname = com_argv[t+1]; + if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc) + gameuserdirname = com_argv[t+1]; + } + + if (gamedirname2 && gamedirname2[0]) + Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2); + else + Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1); + for (i = 0;i < fs_numgamedirs;i++) + { + if (i == 0) + Con_Printf(", with mod gamedirs"); + Con_Printf(" %s", fs_gamedirs[i]); + } + Con_Printf("\n"); + + if (strchr(gamenetworkfiltername, ' ')) + { + char *s; + // if there are spaces in the game's network filter name it would + // cause parse errors in getservers in dpmaster, so we need to replace + // them with _ characters + strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer)); + while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL) + *s = '_'; + gamenetworkfiltername = gamenetworkfilternamebuffer; + } - gamename = gamemode_info[gamemode].gamename; - gamedirname1 = gamemode_info[gamemode].gamedirname1; - gamedirname2 = gamemode_info[gamemode].gamedirname2; - gamescreenshotname = gamemode_info[gamemode].gamescreenshotname; - gameuserdirname = gamemode_info[gamemode].gameuserdirname; + Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername); } @@ -1234,13 +1620,22 @@ void COM_Init_Commands (void) if (strstr(com_argv[j], " ")) { // arg contains whitespace, store quotes around it + // This condition checks whether we can allow to put + // in two quote characters. + if (n >= ((int)sizeof(com_cmdline) - 2)) + break; com_cmdline[n++] = '\"'; - while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i]) + // This condition checks whether we can allow one + // more character and a quote character. + while ((n < ((int)sizeof(com_cmdline) - 2)) && com_argv[j][i]) + // FIXME: Doesn't quote special characters. com_cmdline[n++] = com_argv[j][i++]; com_cmdline[n++] = '\"'; } else { + // This condition checks whether we can allow one + // more character. while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i]) com_cmdline[n++] = com_argv[j][i++]; } @@ -1257,25 +1652,18 @@ void COM_Init_Commands (void) ============ va -does a varargs printf into a temp buffer, so I don't need to have -varargs versions of all text functions. -FIXME: make this buffer size safe someday +varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf) ============ */ -char *va(const char *format, ...) +char *va(char *buf, size_t buflen, const char *format, ...) { va_list argptr; - // LordHavoc: now cycles through 8 buffers to avoid problems in most cases - static char string[8][1024], *s; - static int stringindex = 0; - s = string[stringindex]; - stringindex = (stringindex + 1) & 7; va_start (argptr, format); - dpvsnprintf (s, sizeof (string[0]), format,argptr); + dpvsnprintf (buf, buflen, format,argptr); va_end (argptr); - return s; + return buf; } @@ -1309,7 +1697,11 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar { int result; +#if _MSC_VER >= 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'; @@ -1327,6 +1719,23 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out) if (size_out == 0) return; + if(utf8_enable.integer) + { + *out = 0; + while(*in && size_out > 1) + { + int n; + Uchar ch = u8_getchar_utf8_enabled(in, &in); + ch = u8_tolower(ch); + n = u8_fromchar(ch, out, size_out); + if(n <= 0) + break; + out += n; + size_out -= n; + } + return; + } + while (*in && size_out > 1) { if (*in >= 'A' && *in <= 'Z') @@ -1343,6 +1752,23 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out) if (size_out == 0) return; + if(utf8_enable.integer) + { + *out = 0; + while(*in && size_out > 1) + { + int n; + Uchar ch = u8_getchar_utf8_enabled(in, &in); + ch = u8_toupper(ch); + n = u8_fromchar(ch, out, size_out); + if(n <= 0) + break; + out += n; + size_out -= n; + } + return; + } + while (*in && size_out > 1) { if (*in >= 'a' && *in <= 'z') @@ -1375,7 +1801,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t commentprefixlength = (int)strlen(commentprefix); while (*l && *l != '\n' && *l != '\r') { - if (*l > ' ') + if (!ISWHITESPACE(*l)) { if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength)) { @@ -1400,7 +1826,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t } else { - while (*l > ' ') + while (!ISWHITESPACE(*l)) { if (tokenbuf >= tokenbufend) return -1; @@ -1459,6 +1885,17 @@ COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid) ++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) @@ -1497,7 +1934,7 @@ 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)+2 +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. @@ -1523,6 +1960,19 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, ++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 @@ -1555,113 +2005,54 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, #undef APPEND } -// written by Elric, thanks Elric! -char *SearchInfostring(const char *infostring, const char *key) -{ - static char value [MAX_INPUTLINE]; - char crt_key [MAX_INPUTLINE]; - size_t value_ind, key_ind; - char c; - - if (*infostring++ != '\\') - return NULL; - - value_ind = 0; - for (;;) - { - key_ind = 0; - - // Get the key name - for (;;) - { - c = *infostring++; - - if (c == '\0') - return NULL; - if (c == '\\' || key_ind == sizeof (crt_key) - 1) - { - crt_key[key_ind] = '\0'; - break; - } - - crt_key[key_ind++] = c; - } - - // If it's the key we are looking for, save it in "value" - if (!strcmp(crt_key, key)) - { - for (;;) - { - c = *infostring++; - - if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1) - { - value[value_ind] = '\0'; - return value; - } - - value[value_ind++] = c; - } - } - - // Else, skip the value - for (;;) - { - c = *infostring++; - - if (c == '\0') - return NULL; - if (c == '\\') - break; - } - } -} - -void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength) +char *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; + return NULL; } value[0] = 0; if (strchr(key, '\\')) { Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key); - return; + return NULL; } if (strchr(key, '\"')) { Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key); - return; + return NULL; } if (!key[0]) { Con_Printf("InfoString_GetValue: can not look up a key with no name\n"); - return; + return NULL; } while (buffer[pos] == '\\') { - if (!memcmp(buffer + pos+1, key, keylength)) + if (!memcmp(buffer + pos+1, key, keylength) && + (buffer[pos+1 + keylength] == 0 || + buffer[pos+1 + keylength] == '\\')) { - for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); - pos++; + pos += 1 + keylength; // Skip \key + if (buffer[pos] == '\\') pos++; // Skip \ before value. for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++) value[j] = buffer[pos+j]; value[j] = 0; - return; + return value; } + if (buffer[pos] == '\\') pos++; // Skip \ before value. for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + if (buffer[pos] == '\\') pos++; // Skip \ before value. for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); } // if we reach this point the key was not found + return NULL; } void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value) @@ -1690,29 +2081,34 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con } while (buffer[pos] == '\\') { - if (!memcmp(buffer + pos+1, key, keylength)) + if (!memcmp(buffer + pos+1, key, keylength) && + (buffer[pos+1 + keylength] == 0 || + buffer[pos+1 + keylength] == '\\')) break; - for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); - for (pos++;buffer[pos] && buffer[pos] != '\\';pos++); + if (buffer[pos] == '\\') pos++; // Skip \ before value. + for (;buffer[pos] && buffer[pos] != '\\';pos++); + if (buffer[pos] == '\\') pos++; // Skip \ before value. + for (;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++); + pos2 += 1 + keylength; // Skip \key + if (buffer[pos2] == '\\') pos2++; // Skip \ before value. + for (;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]) + if (value[0]) { // set the key/value and append the remaining text - char tempbuffer[4096]; + char tempbuffer[MAX_INPUTLINE]; strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer)); - sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer); + dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer); } else { @@ -1724,8 +2120,8 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con void InfoString_Print(char *buffer) { int i; - char key[2048]; - char value[2048]; + char key[MAX_INPUTLINE]; + char value[MAX_INPUTLINE]; while (*buffer) { if (*buffer != '\\') @@ -1833,3 +2229,84 @@ 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; + } + } +} + +// decodes an XPM from C syntax +char **XPM_DecodeString(const char *in) +{ + static char *tokens[257]; + static char lines[257][512]; + size_t line = 0; + + // skip until "{" token + while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{")); + + // now, read in succession: string, comma-or-} + while(COM_ParseToken_QuakeC(&in, false)) + { + tokens[line] = lines[line]; + strlcpy(lines[line++], com_token, sizeof(lines[0])); + if(!COM_ParseToken_QuakeC(&in, false)) + return NULL; + if(!strcmp(com_token, "}")) + break; + if(strcmp(com_token, ",")) + return NULL; + if(line >= sizeof(tokens) / sizeof(tokens[0])) + return NULL; + } + + return tokens; +} + +static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes) +{ + unsigned char i0 = (bytes > 0) ? in[0] : 0; + unsigned char i1 = (bytes > 1) ? in[1] : 0; + unsigned char i2 = (bytes > 2) ? in[2] : 0; + unsigned char o0 = base64[i0 >> 2]; + unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077]; + unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077]; + unsigned char o3 = base64[i2 & 077]; + out[0] = (bytes > 0) ? o0 : '?'; + out[1] = (bytes > 0) ? o1 : '?'; + out[2] = (bytes > 1) ? o2 : '='; + out[3] = (bytes > 2) ? o3 : '='; +} + +size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen) +{ + size_t blocks, i; + // expand the out-buffer + blocks = (buflen + 2) / 3; + if(blocks*4 > outbuflen) + return 0; + for(i = blocks; i > 0; ) + { + --i; + base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i)); + } + return blocks * 4; +}