X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=common.c;h=11a350bfc2dba30daa9d4614657f0e9d873e7fb4;hp=035c1a6f29fa9a1049650aeace48826284899918;hb=f575e5ad7f9eab64e89b714e75ff5dd92e49f373;hpb=0855bac905297f6d915b7ffece2cc34b60f4a230 diff --git a/common.c b/common.c index 035c1a6f..11a350bf 100644 --- a/common.c +++ b/common.c @@ -27,12 +27,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" -cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/qpop.lmp was found)"}; +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"}; char com_token[MAX_INPUTLINE]; int com_argc; const char **com_argv; +int com_selffd = -1; gamemode_t gamemode; const char *gamename; @@ -51,68 +52,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 +184,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] = { @@ -334,7 +353,7 @@ void MSG_WriteCoord32f (sizebuf_t *sb, float f) void MSG_WriteCoord (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD) + 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) MSG_WriteCoord32f (sb, f); @@ -375,7 +394,7 @@ void MSG_WriteAngle32f (sizebuf_t *sb, float f) void MSG_WriteAngle (sizebuf_t *sb, float f, protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD) + 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); @@ -506,7 +525,7 @@ float MSG_ReadCoord32f (void) float MSG_ReadCoord (protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_QUAKEWORLD) + 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) return MSG_ReadCoord32f(); @@ -541,7 +560,7 @@ float MSG_ReadAngle32f (void) float MSG_ReadAngle (protocolversion_t protocol) { - if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD) + 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 (); @@ -587,7 +606,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 +654,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,17 +681,317 @@ 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); + isContinuation = false; + 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; +*/ +} /* ============== -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; @@ -690,7 +1009,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) { @@ -717,56 +1036,159 @@ 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') + { + // single character + com_token[len++] = *data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else + { + // regular word + for (;!ISWHITESPACE(*data);data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; + com_token[len] = 0; + *datapointer = data; return true; } - else if (*data == '\'') +} + +/* +============== +COM_ParseToken_QuakeC + +Parse a token out of a string +============== +*/ +int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline) +{ + int len; + int c; + const char *data = *datapointer; + + len = 0; + com_token[0] = 0; + + if (!data) + { + *datapointer = NULL; + return false; + } + +// skip whitespace +skipwhite: + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + { + if (*data == 0) + { + // end of file + *datapointer = NULL; + return false; + } + } + + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + + if (data[0] == '/' && data[1] == '/') + { + // comment + while (*data && *data != '\n' && *data != '\r') + data++; + goto skipwhite; + } + 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 == '\\' && data[1] == '\'' ) - 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'; + 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' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';') { // single character com_token[len++] = *data++; @@ -777,17 +1199,124 @@ skipwhite: else { // regular word - for (;*data > ' ' && *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; + *datapointer = data; + return true; + } +} + +/* +============== +COM_ParseToken_VM_Tokenize + +Parse a token out of a string +============== +*/ +int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline) +{ + int len; + int c; + const char *data = *datapointer; + + len = 0; + com_token[0] = 0; + + if (!data) + { + *datapointer = NULL; + return false; + } + +// skip whitespace +skipwhite: + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + { + if (*data == 0) { - if (len >= (int)sizeof(com_token) - 1) + // end of file + *datapointer = NULL; + return false; + } + } + + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + + if (data[0] == '/' && data[1] == '/') + { + // comment + while (*data && *data != '\n' && *data != '\r') + data++; + goto skipwhite; + } + 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 == '\'') + { + char quote = *data; + // quoted string + for (data++;*data && *data != quote;data++) + { + 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; + 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 (;!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; } @@ -795,12 +1324,12 @@ skipwhite: /* ============== -COM_ParseTokenConsole +COM_ParseToken_Console Parse a token out of a string, behaving like the qwcl console ============== */ -int COM_ParseTokenConsole(const char **datapointer) +int COM_ParseToken_Console(const char **datapointer) { int len; const char *data = *datapointer; @@ -816,7 +1345,7 @@ int COM_ParseTokenConsole(const char **datapointer) // skip whitespace skipwhite: - for (;*data <= ' ';data++) + for (;ISWHITESPACE(*data);data++) { if (*data == 0) { @@ -836,32 +1365,25 @@ skipwhite: else if (*data == '\"') { // quoted string - for (data++;*data != '\"';data++) + for (data++;*data && *data != '\"';data++) { - if (!*data || len >= (int)sizeof(com_token) - 1) - { - com_token[0] = 0; - *datapointer = NULL; - return false; - } - com_token[len++] = *data; + // allow escaped " and \ case + if (*data == '\\' && (data[1] == '\"' || data[1] == '\\')) + data++; + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; } com_token[len] = 0; - *datapointer = data+1; + if (*data == '\"') + data++; + *datapointer = data; } 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++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; com_token[len] = 0; *datapointer = data; } @@ -899,17 +1421,17 @@ int COM_CheckParm (const char *parm) 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; + const char* prog_name; // not null + const char* cmdline; // not null + const char* gamename; // 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 +static const gamemode_info_t gamemode_info [GAME_COUNT] = +{// prog_name cmdline gamename basegame modgame screenshotprefix userdir // GAME_NORMAL // COMMANDLINEOPTION: Game: -quake runs the game Quake (default) @@ -926,6 +1448,9 @@ static const gamemode_info_t gamemode_info [] = // GAME_NEXUIZ // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz { "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, +// GAME_XONOTIC +// COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic +{ "xonotic", "-xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // GAME_TRANSFUSION // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake) { "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, @@ -959,9 +1484,9 @@ static const gamemode_info_t gamemode_info [] = // 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_DELUXEQUAKE +// COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake +{ "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" }, // GAME_THEHUNTED // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted { "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" }, @@ -974,12 +1499,31 @@ static const gamemode_info_t gamemode_info [] = // GAME_CONTAGIONTHEORY // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory { "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" }, +// GAME_EDU2P +// COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype +{ "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, +// GAME_PROPHECY +// COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy +{ "prophecy", "-prophecy", "Prophecy", "data", NULL, "prophecy", "prophecy" }, +// GAME_BLOODOMNICIDE +// COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide +{ "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" }, +// GAME_STEELSTORM +// COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm +{ "steelstorm", "-steelstorm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, +// GAME_STRAPBOMB +// COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car +{ "strapbomb", "-strapbomb", "Strap-on-bomb Car", "id1", NULL, "strap", "strapbomb" }, +// GAME_MOONHELM +// COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm +{ "moonhelm", "-moonhelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, }; void COM_InitGameType (void) { char name [MAX_OSPATH]; unsigned int i; + int t; FS_StripExtension (com_argv[0], name, sizeof (name)); COM_ToLowerString (name, name, sizeof (name)); @@ -1006,6 +1550,17 @@ void COM_InitGameType (void) gamedirname2 = gamemode_info[gamemode].gamedirname2; gamescreenshotname = gamemode_info[gamemode].gamescreenshotname; gameuserdirname = gamemode_info[gamemode].gameuserdirname; + + if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc) + gamename = 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]; } @@ -1105,7 +1660,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'; @@ -1171,7 +1730,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)) { @@ -1196,7 +1755,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t } else { - while (*l > ' ') + while (!ISWHITESPACE(*l)) { if (tokenbuf >= tokenbufend) return -1; @@ -1222,6 +1781,159 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t 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) { @@ -1290,8 +2002,6 @@ void InfoString_GetValue(const char *buffer, const char *key, char *value, size_ size_t keylength; if (!key) key = ""; - if (!value) - value = ""; keylength = strlen(key); if (valuelength < 1 || !value) { @@ -1379,7 +2089,7 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con // set the key/value and append the remaining text char tempbuffer[4096]; 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 { @@ -1500,3 +2210,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, buflen - 3*i); + } + return blocks * 4; +}