// Run through a string copying ident to buffer int CExpression::ConsumeAppendIdentifier(char* buf, const char* str) { int i; bool alpha = false; bool floatNum = false; // Read chars up to first '.': if only digits, interpret as being a number and continue. Else, use as identifier. for (i = 0; ; i++) { if (str[i] == '.') break; if (IsChAlpha(str[i])) alpha = true; else if (!IsChDigit(str[i]) && str[i] != '[' && str[i] != ']') break; buf[i] = str[i]; } // Identifier found if (alpha) { buf[i] = NULL; AppendToken(T_IDENTIFIER, buf); CheckForKeywords(toks[toks.size() - 1]); return i - 1; } else if (str[i] == '.') { // Continue with dot buf[i++] = '.'; floatNum = true; } // Only digits preceding . // Carry on reading digits for ( ; (str[i] <= '9' && str[i] >= '0'); i++) buf[i] = str[i]; buf[i] = NULL; if (floatNum) AppendToken(T_FLOAT, buf); else AppendToken(T_INTEGER, buf); return i-1; }
/* =================== R_ParseImageProgram_r If pic is NULL, the timestamps will be filled in, but no image will be generated If both pic and timestamps are NULL, it will just advance past it, which can be used to parse an image program from a text stream. =================== */ static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth ) { idToken token; float scale; ID_TIME_T timestamp; src.ReadToken( &token ); AppendToken( token ); if ( !token.Icmp( "heightmap" ) ) { MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale = token.GetFloatValue(); // process it if ( pic ) { R_HeightmapToNormalMap( *pic, *width, *height, scale ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "addnormals" ) ) { byte *pic2; int width2, height2; MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) { if ( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if ( pic ) { R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "smoothnormals" ) ) { MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } if ( pic ) { R_SmoothNormalMap( *pic, *width, *height ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "add" ) ) { byte *pic2; int width2, height2; MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) { if ( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if ( pic ) { R_ImageAdd( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "scale" ) ) { float scale[4]; int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); for ( i = 0 ; i < 4 ; i++ ) { MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale[i] = token.GetFloatValue(); } // process it if ( pic ) { R_ImageScale( *pic, *width, *height, scale ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "invertAlpha" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // process it if ( pic ) { R_InvertAlpha( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "invertColor" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // process it if ( pic ) { R_InvertColor( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "makeIntensity" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // copy red to green, blue, and alpha if ( pic ) { int c; c = *width * *height * 4; for ( i = 0 ; i < c ; i+=4 ) { (*pic)[i+1] = (*pic)[i+2] = (*pic)[i+3] = (*pic)[i]; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "makeAlpha" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // average RGB into alpha, then set RGB to white if ( pic ) { int c; c = *width * *height * 4; for ( i = 0 ; i < c ; i+=4 ) { (*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3; (*pic)[i+0] = (*pic)[i+1] = (*pic)[i+2] = 255; } } MatchAndAppendToken( src, ")" ); return true; } // if we are just parsing instead of loading or checking, // don't do the R_LoadImage if ( !timestamps && !pic ) { return true; } // load it as an image R_LoadImage( token.c_str(), pic, width, height, ×tamp, true ); if ( timestamp == -1 ) { return false; } // add this to the timestamp if ( timestamps ) { if ( timestamp > *timestamps ) { *timestamps = timestamp; } } return true; }
/* =================== R_ParseImageProgram_r If pic is NULL, the timestamps will be filled in, but no image will be generated If both pic and timestamps are NULL, it will just advance past it, which can be used to parse an image program from a text stream. =================== */ static bool R_ParseImageProgram_r( idLexer& src, byte** pic, int* width, int* height, ID_TIME_T* timestamps, textureUsage_t* usage ) { idToken token; float scale; ID_TIME_T timestamp; src.ReadToken( &token ); // Since all interaction shaders now assume YCoCG diffuse textures. We replace all entries for the intrinsic // _black texture to the black texture on disk. Doing this will cause a YCoCG compliant texture to be generated. // Without a YCoCG compliant black texture we will get color artifacts for any interaction // material that specifies the _black texture. if( token == "_black" ) { token = "textures/black"; } // also check for _white if( token == "_white" ) { token = "guis/assets/white"; } AppendToken( token ); if( !token.Icmp( "heightmap" ) ) { MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale = token.GetFloatValue(); // process it if( pic ) { R_HeightmapToNormalMap( *pic, *width, *height, scale ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "addnormals" ) ) { byte* pic2 = NULL; int width2, height2; MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); if( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) { if( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if( pic ) { R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "smoothnormals" ) ) { MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } if( pic ) { R_SmoothNormalMap( *pic, *width, *height ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "add" ) ) { byte* pic2 = NULL; int width2, height2; MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); if( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) { if( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if( pic ) { R_ImageAdd( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "scale" ) ) { float scale[4]; int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); for( i = 0 ; i < 4 ; i++ ) { MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale[i] = token.GetFloatValue(); } // process it if( pic ) { R_ImageScale( *pic, *width, *height, scale ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "invertAlpha" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // process it if( pic ) { R_InvertAlpha( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "invertColor" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // process it if( pic ) { R_InvertColor( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "makeIntensity" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // copy red to green, blue, and alpha if( pic ) { int c; c = *width * *height * 4; for( i = 0 ; i < c ; i += 4 ) { ( *pic )[i + 1] = ( *pic )[i + 2] = ( *pic )[i + 3] = ( *pic )[i]; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "makeAlpha" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // average RGB into alpha, then set RGB to white if( pic ) { int c; c = *width * *height * 4; for( i = 0 ; i < c ; i += 4 ) { ( *pic )[i + 3] = ( ( *pic )[i + 0] + ( *pic )[i + 1] + ( *pic )[i + 2] ) / 3; ( *pic )[i + 0] = ( *pic )[i + 1] = ( *pic )[i + 2] = 255; } } MatchAndAppendToken( src, ")" ); return true; } // if we are just parsing instead of loading or checking, // don't do the R_LoadImage if( !timestamps && !pic ) { return true; } // load it as an image R_LoadImage( token.c_str(), pic, width, height, ×tamp, true ); if( timestamp == -1 ) { return false; } // add this to the timestamp if( timestamps ) { if( timestamp > *timestamps ) { *timestamps = timestamp; } } return true; }
// Tokenise a user's typed expression string void CExpression::ParseUserString(const char* exp, int* pchpos, bool editorMode) { toks.clear(); whitespaceCount = 0; // Loop every char for (int i = 0; exp[i] != NULL; i++) { if (pchpos) *pchpos = i; char NextCh = exp[i+1]; char CurCh = exp[i]; char PrevCh = exp[(i == 0 ? 0 : i-1)]; char strbuf[128]; // Check this char switch (exp[i]) { case '+': AppendToken(T_ADD, "+"); break; case '-': // If previous token is not operand, use as negative sign // Fix 25/4/07: random(4) - random(5) interprets '-' as integer: explicit check for right bracket if (toks.size() == 0 || ((!TokenFitsRule(toks.back().t, T_ANY_VALUE) || toks.back().t == T_LEFTBRACKET) && toks.back().t != T_RIGHTBRACKET)) { i += ConsumeDigits(strbuf, &(exp[i])); // Contains a dot: float if (StrContains(strbuf, '.')) AppendToken(T_FLOAT, strbuf); else // Else integer AppendToken(T_INTEGER, strbuf); } else AppendToken(T_SUBTRACT, "-"); break; case '*': AppendToken(T_MULTIPLY, "*"); break; case '/': AppendToken(T_DIVIDE, "/"); break; case '^': AppendToken(T_POWER, "^"); break; case '%': AppendToken(T_MOD, "%"); break; case '(': AppendToken(T_LEFTBRACKET, "("); break; case ')': AppendToken(T_RIGHTBRACKET, ")"); break; case '{': AppendToken(T_LEFTCURLY, "{"); break; case '}': AppendToken(T_RIGHTCURLY, "}"); break; case '@': AppendToken(T_AT, "@"); break; case ',': AppendToken(T_COMMA, ","); break; case '.': AppendToken(T_DOT, "."); break; case '"': i += AppendString(&(exp[i]), editorMode); break; case '\'': i += AppendString(&(exp[i]), editorMode, true); break; case '=': AppendToken(T_EQUAL, "="); break; case '<': if (NextCh == '=') { AppendToken(T_LESSEQUAL, "<="); i++; } else if (NextCh == '>') { AppendToken(T_NOTEQUAL, "<>"); i++; } else AppendToken(T_LESS, "<"); break; // Alternative not equal operator != <> case '!': if (NextCh == '=') { AppendToken(T_NOTEQUAL, "!="); i++; } else NotifyError("Syntax error: '!'"); break; case '&': AppendToken(T_AND, "&"); break; case '|': AppendToken(T_OR, "|"); break; case '>': if (NextCh == '=') { AppendToken(T_GREATEREQUAL, ">="); i++; } else AppendToken(T_GREATER, ">"); break; case '?': AppendToken(T_CONDITIONAL, "?"); break; case ':': AppendToken(T_COLON, ":"); break; default: // Parse numbers and idents if (IsChAlphaNumeric(CurCh)) i += ConsumeAppendIdentifier(strbuf, &(exp[i])); // Skip over whitespace else if (IsWhitespace(CurCh)) { // In editor mode add a T_WHITESPACE token if (editorMode) { Token t; t.length = 0; t.t = T_WHITESPACE; while (IsWhitespace(exp[i])) { t.str += exp[i]; i++; } // We want the next for loop iteration to see the next char i--; toks.push_back(t); } // Add to last token length if (toks.size() > 0) toks[toks.size() - 1].length++; } // Else unknown character else { string error = "Unknown character '"; error += CurCh; error += "'"; throw runtime_error(error.c_str()); } }//switch }//for // Only perform preprocessing when not in edittime mode if (!editorMode) PreProcessTokStream(); /////////////////////////// // Final step: parse to tree // Determine if the overall expression is constant, collapsing constant parts along // the way. // Runtime only #ifdef RUNTIME // Make the tree int step = 0; ExpTree = DoTree(step); isConstant = ExpTree->Constant(); // If constant, get the constant value. if (isConstant) { CollapseConstantTree(ExpTree); ExpTree->Evaluate(&constantValue); } #endif }