/* ================ CPathTreeCtrl::AddPathToTree Adds a new item to the tree. Assumes new paths after the current stack path do not yet exist. ================ */ HTREEITEM CPathTreeCtrl::AddPathToTree( const idStr &pathName, const int id, idPathTreeStack &stack ) { int lastSlash; idStr itemName, tmpPath; HTREEITEM item; lastSlash = pathName.Last( '/' ); while( stack.Num() > 1 ) { if ( pathName.Icmpn( stack.TopName(), stack.TopNameLength() ) == 0 ) { break; } stack.Pop(); } while( lastSlash > stack.TopNameLength() ) { pathName.Mid( stack.TopNameLength(), pathName.Length(), tmpPath ); tmpPath.Left( tmpPath.Find( '/' ), itemName ); item = InsertItem( itemName, stack.TopItem() ); stack.Push( item, itemName ); } pathName.Mid( stack.TopNameLength(), pathName.Length(), itemName ); item = InsertItem( itemName, stack.TopItem() ); SetItemData( item, id ); return item; }
/* ================ CPathTreeCtrl::InsertPathIntoTree Inserts a new item going from the root down the tree only creating paths where necessary. This is slow and should only be used to insert single items. ================ */ HTREEITEM CPathTreeCtrl::InsertPathIntoTree( const idStr &pathName, const int id ) { int lastSlash; idStr path, tmpPath, itemName; HTREEITEM item, parentItem; parentItem = NULL; item = GetRootItem(); lastSlash = pathName.Last( '/' ); while( item && lastSlash > path.Length() ) { itemName = GetItemText( item ); tmpPath = path + itemName; if ( pathName.Icmpn( tmpPath, tmpPath.Length() ) == 0 ) { parentItem = item; item = GetChildItem( item ); path = tmpPath + "/"; } else { item = GetNextSiblingItem( item ); } } while( lastSlash > path.Length() ) { pathName.Mid( path.Length(), pathName.Length(), tmpPath ); tmpPath.Left( tmpPath.Find( '/' ), itemName ); parentItem = InsertItem( itemName, parentItem ); path += itemName + "/"; } pathName.Mid( path.Length(), pathName.Length(), itemName ); item = InsertItem( itemName, parentItem, TVI_SORT ); SetItemData( item, id ); return item; }
/* ================ idLexer::GetLastWhiteSpace ================ */ int idLexer::GetLastWhiteSpace( idStr &whiteSpace ) const { whiteSpace.Clear(); for ( const char *p = whiteSpaceStart_p; p < whiteSpaceEnd_p; p++ ) { whiteSpace.Append( *p ); } return whiteSpace.Length(); }
void CModInfo::GetMissionTitles(idStr missionTitles) { if (modName.IsEmpty()) { return; } int startIndex = 0; idStr start = "Mission 1 Title: "; idStr end = "Mission 2 Title: "; int endIndex = missionTitles.Find(end.c_str(), true); // grayman #3733 bool finished = false; for ( int i = 1 ; ; i++ ) { idStr title = idStr(missionTitles, startIndex, endIndex); // grayman #3733 Strip(start.c_str(), title); _missionTitles.Append(title); start = end; startIndex = endIndex; if (finished) { break; } end = va("Mission %d Title: ",i+2); endIndex = missionTitles.Find(end.c_str(), true, startIndex); // grayman #3733 if (endIndex < 0) { endIndex = missionTitles.Length(); finished = true; } } }
/* ================ idTypeInfoTools::ParseTemplateArguments ================ */ bool idTypeInfoTools::ParseTemplateArguments( idLexer &src, idStr &arguments ) { int indent; idToken token; arguments = ""; if ( !src.ExpectTokenString( "<" ) ) { return false; } indent = 1; while( indent ) { if ( !src.ReadToken( &token ) ) { break; } if ( token == "<" ) { indent++; } else if ( token == ">" ) { indent--; } else { if ( arguments.Length() ) { arguments += " "; } arguments += token; } } return true; }
/* ================ DialogDeclEditor::TestDecl ================ */ bool DialogDeclEditor::TestDecl(const idStr &declText) { idLexer src(LEXFL_NOSTRINGCONCAT); idToken token; int indent; src.LoadMemory(declText, declText.Length(), "decl text"); indent = 0; while (src.ReadToken(&token)) { if (token == "{") { indent++; } else if (token == "}") { indent--; } } if (indent < 0) { MessageBox("Missing opening brace!", va("Error saving %s", decl->GetFileName()), MB_OK | MB_ICONERROR); return false; } if (indent > 0) { MessageBox("Missing closing brace!", va("Error saving %s", decl->GetFileName()), MB_OK | MB_ICONERROR); return false; } return true; }
/* ================ Sys_DefaultBasePath Get the default base path - binary image path - current directory - hardcoded Try to be intelligent: if there is no BASE_GAMEDIR, try the next path ================ */ const char *Sys_DefaultBasePath( void ) { struct stat st; idStr testbase; basepath = Sys_EXEPath(); if( basepath.Length() ) { basepath.StripFilename(); testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR; if( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) { return basepath.c_str(); } else { common->Printf( "no '%s' directory in exe path %s, skipping\n", BASE_GAMEDIR, basepath.c_str() ); } } if( basepath != Posix_Cwd() ) { basepath = Posix_Cwd(); testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR; if( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) { return basepath.c_str(); } else { common->Printf( "no '%s' directory in cwd path %s, skipping\n", BASE_GAMEDIR, basepath.c_str() ); } } common->Printf( "WARNING: using hardcoded default base path\n" ); return LINUX_DEFAULT_PATH; }
void idGLDrawableView::setCustomModel( const idStr modelName ) { if ( modelName.Length() ) { objectId = -1; } else { objectId = 0; } customModelName = modelName; UpdateModel(); }
/* ============ idInternalCVar::Update ============ */ void idInternalCVar::Update( const idCVar *cvar ) { // if this is a statically declared variable if ( cvar->GetFlags() & CVAR_STATIC ) { if ( flags & CVAR_STATIC ) { // the code has more than one static declaration of the same variable, make sure they have the same properties if ( resetString.Icmp( cvar->GetString() ) != 0 ) { common->Warning( "CVar '%s' declared multiple times with different initial value", nameString.c_str() ); } if ( ( flags & (CVAR_BOOL|CVAR_INTEGER|CVAR_FLOAT) ) != ( cvar->GetFlags() & (CVAR_BOOL|CVAR_INTEGER|CVAR_FLOAT) ) ) { common->Warning( "CVar '%s' declared multiple times with different type", nameString.c_str() ); } if ( valueMin != cvar->GetMinValue() || valueMax != cvar->GetMaxValue() ) { common->Warning( "CVar '%s' declared multiple times with different minimum/maximum", nameString.c_str() ); } } // the code is now specifying a variable that the user already set a value for, take the new value as the reset value resetString = cvar->GetString(); descriptionString = cvar->GetDescription(); description = descriptionString.c_str(); valueMin = cvar->GetMinValue(); valueMax = cvar->GetMaxValue(); Mem_Free( valueStrings ); valueStrings = CopyValueStrings( cvar->GetValueStrings() ); valueCompletion = cvar->GetValueCompletion(); UpdateValue(); cvarSystem->SetModifiedFlags( cvar->GetFlags() ); } flags |= cvar->GetFlags(); UpdateCheat(); // only allow one non-empty reset string without a warning if ( resetString.Length() == 0 ) { resetString = cvar->GetString(); } else if ( cvar->GetString()[0] && resetString.Cmp( cvar->GetString() ) != 0 ) { common->Warning( "cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", nameString.c_str(), resetString.c_str(), cvar->GetString() ); } }
/* ================== Sym_GetFuncInfo ================== */ void Sym_GetFuncInfo( long addr, idStr &module, idStr &funcName ) { MEMORY_BASIC_INFORMATION mbi; module_t *m; symbol_t *s; VirtualQuery( (void*)addr, &mbi, sizeof(mbi) ); for ( m = modules; m != NULL; m = m->next ) { if ( m->address == (int) mbi.AllocationBase ) { break; } } if ( !m ) { Sym_Init( addr ); m = modules; } for ( s = m->symbols; s != NULL; s = s->next ) { if ( s->address == addr ) { char undName[MAX_STRING_CHARS]; if ( UnDecorateSymbolName( s->name, undName, sizeof(undName), UNDECORATE_FLAGS ) ) { funcName = undName; } else { funcName = s->name; } for ( int i = 0; i < funcName.Length(); i++ ) { if ( funcName[i] == '(' ) { funcName.CapLength( i ); break; } } module = m->name; return; } } sprintf( funcName, "0x%08x", addr ); module = ""; }
void CConsoleDlg::ExecuteCommand ( const idStr& cmd ) { CString str; if ( cmd.Length() > 0 ) { str = cmd; } else { editInput.GetWindowText(str); } if ( str != "" ) { editInput.SetWindowText(""); common->Printf("%s\n", str.GetBuffer(0)); //avoid adding multiple identical commands in a row int index = consoleHistory.Num (); if ( index == 0 || str.GetBuffer(0) != consoleHistory[index-1]) { //keep the history to 16 commands, removing the oldest command if ( consoleHistory.Num () > 16 ) { consoleHistory.RemoveIndex ( 0 ); } currentHistoryPosition = consoleHistory.Append ( str.GetBuffer (0) ); } else { currentHistoryPosition = consoleHistory.Num () - 1; } currentCommand.Clear (); bool propogateCommand = true; //process some of our own special commands if ( str.CompareNoCase ( "clear" ) == 0) { editConsole.SetSel ( 0 , -1 ); editConsole.Clear (); } else if ( str.CompareNoCase ( "edit" ) == 0) { propogateCommand = false; } if ( propogateCommand ) { cmdSystem->BufferCommandText( CMD_EXEC_NOW, str ); } Sys_UpdateWindows(W_ALL); } }
TimerValue CStimResponseTimer::ParseTimeString( idStr &str ) { TimerValue v; int h, m, s, ms; idStr source = str; v.Time.Flags = TIMER_UNDEFINED; if( str.Length() == 0 ) { goto Quit; } h = m = s = ms = 0; // Get the first few characters that define the hours h = atoi( source.Left( source.Find( ":" ) ).c_str() ); // Strip the first few numbers plus the colon from the source string source = source.Right( source.Length() - source.Find( ":" ) - 1 ); // Parse the minutes m = atoi( source.Left( source.Find( ":" ) ).c_str() ); if( !( m >= 0 && m <= 59 ) ) { DM_LOG( LC_STIM_RESPONSE, LT_ERROR )LOGSTRING( "Invalid minute string [%s]\r", str.c_str() ); goto Quit; } // Strip the first few numbers plus the colon from the source string source = source.Right( source.Length() - source.Find( ":" ) - 1 ); // Parse the seconds s = atoi( source.Left( source.Find( ":" ) ).c_str() ); if( !( s >= 0 && s <= 59 ) ) { DM_LOG( LC_STIM_RESPONSE, LT_ERROR )LOGSTRING( "Invalid second string [%s]\r", str.c_str() ); goto Quit; } // Parse the milliseconds, this is the remaining part of the string ms = atoi( source.Right( source.Length() - source.Find( ":" ) - 1 ).c_str() ); if( !( ms >= 0 && ms <= 999 ) ) { DM_LOG( LC_STIM_RESPONSE, LT_ERROR )LOGSTRING( "Invalid millisecond string [%s]\r", str.c_str() ); goto Quit; } DM_LOG( LC_STIM_RESPONSE, LT_DEBUG )LOGSTRING( "Parsed timer string: [%s] to %d:%d:%d:%d\r", str.c_str(), h, m, s, ms ); v.Time.Hour = h; v.Time.Minute = m; v.Time.Second = s; v.Time.Millisecond = ms; Quit: return v; }
/** * Applies any source changes to the edit representation of the material. */ void MaterialDoc::ApplySourceModify(idStr& text) { if(sourceModify) { //Changes in the source need to clear any undo redo buffer because we have no idea what has changed manager->ClearUndo(); manager->ClearRedo(); ClearEditMaterial(); idLexer src; src.LoadMemory(text, text.Length(), "Material"); src.SetFlags( LEXFL_NOSTRINGCONCAT | // multiple strings seperated by whitespaces are not concatenated LEXFL_NOSTRINGESCAPECHARS | // no escape characters inside strings LEXFL_ALLOWPATHNAMES | // allow path seperators in names LEXFL_ALLOWMULTICHARLITERALS | // allow multi character literals LEXFL_ALLOWBACKSLASHSTRINGCONCAT | // allow multiple strings seperated by '\' to be concatenated LEXFL_NOFATALERRORS // just set a flag instead of fatal erroring ); idToken token; if(!src.ReadToken(&token)) { src.Warning( "Missing decl name" ); return; } ParseMaterial(&src); sourceModify = false; //Check to see if the name has changed if(token.Icmp(name)) { SetMaterialName(token, false); } } }
/* ============ idInternalCVar::UpdateValue ============ */ void idInternalCVar::UpdateValue( void ) { bool clamped = false; if ( flags & CVAR_BOOL ) { integerValue = ( atoi( value ) != 0 ); floatValue = integerValue; if ( idStr::Icmp( value, "0" ) != 0 && idStr::Icmp( value, "1" ) != 0 ) { valueString = idStr( (bool)( integerValue != 0 ) ); value = valueString.c_str(); } } else if ( flags & CVAR_INTEGER ) { integerValue = (int)atoi( value ); if ( valueMin < valueMax ) { if ( integerValue < valueMin ) { integerValue = (int)valueMin; clamped = true; } else if ( integerValue > valueMax ) { integerValue = (int)valueMax; clamped = true; } } if ( clamped || !idStr::IsNumeric( value ) || idStr::FindChar( value, '.' ) ) { valueString = idStr( integerValue ); value = valueString.c_str(); } floatValue = (float)integerValue; } else if ( flags & CVAR_FLOAT ) { floatValue = (float)atof( value ); if ( valueMin < valueMax ) { if ( floatValue < valueMin ) { floatValue = valueMin; clamped = true; } else if ( floatValue > valueMax ) { floatValue = valueMax; clamped = true; } } if ( clamped || !idStr::IsNumeric( value ) ) { valueString = idStr( floatValue ); value = valueString.c_str(); } integerValue = (int)floatValue; } else { if ( valueStrings && valueStrings[0] ) { integerValue = 0; for ( int i = 0; valueStrings[i]; i++ ) { if ( valueString.Icmp( valueStrings[i] ) == 0 ) { integerValue = i; break; } } valueString = valueStrings[integerValue]; value = valueString.c_str(); floatValue = (float)integerValue; } else if ( valueString.Length() < 32 ) { floatValue = (float)atof( value ); integerValue = (int)floatValue; } else { floatValue = 0.0f; integerValue = 0; } } }
/* ======================== ConvertCG2GLSL ======================== */ idStr ConvertCG2GLSL( const idStr & in, const char * name, bool isVertexProgram, idStr & uniforms ) { idStr program; program.ReAllocate( in.Length() * 2, false ); idList< inOutVariable_t, TAG_RENDERPROG > varsIn; idList< inOutVariable_t, TAG_RENDERPROG > varsOut; idList< idStr > uniformList; idLexer src( LEXFL_NOFATALERRORS ); src.LoadMemory( in.c_str(), in.Length(), name ); bool inMain = false; const char * uniformArrayName = isVertexProgram ? VERTEX_UNIFORM_ARRAY_NAME : FRAGMENT_UNIFORM_ARRAY_NAME; char newline[128] = { "\n" }; idToken token; while ( src.ReadToken( &token ) ) { // check for uniforms while ( token == "uniform" && src.CheckTokenString( "float4" ) ) { src.ReadToken( &token ); uniformList.Append( token ); // strip ': register()' from uniforms if ( src.CheckTokenString( ":" ) ) { if ( src.CheckTokenString( "register" ) ) { src.SkipUntilString( ";" ); } } src.ReadToken( & token ); } // convert the in/out structs if ( token == "struct" ) { if ( src.CheckTokenString( "VS_IN" ) ) { ParseInOutStruct( src, AT_VS_IN, varsIn ); program += "\n\n"; for ( int i = 0; i < varsIn.Num(); i++ ) { if ( varsIn[i].declareInOut ) { program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n"; } } continue; } else if ( src.CheckTokenString( "VS_OUT" ) ) { ParseInOutStruct( src, AT_VS_OUT, varsOut ); program += "\n"; for ( int i = 0; i < varsOut.Num(); i++ ) { if ( varsOut[i].declareInOut ) { program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n"; } } continue; } else if ( src.CheckTokenString( "PS_IN" ) ) { ParseInOutStruct( src, AT_PS_IN, varsIn ); program += "\n\n"; for ( int i = 0; i < varsIn.Num(); i++ ) { if ( varsIn[i].declareInOut ) { program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n"; } } inOutVariable_t var; var.type = "vec4"; var.nameCg = "position"; var.nameGLSL = "gl_FragCoord"; varsIn.Append( var ); continue; } else if ( src.CheckTokenString( "PS_OUT" ) ) { ParseInOutStruct( src, AT_PS_OUT, varsOut ); program += "\n"; for ( int i = 0; i < varsOut.Num(); i++ ) { if ( varsOut[i].declareInOut ) { program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n"; } } continue; } } // strip 'static' if ( token == "static" ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); src.SkipWhiteSpace( true ); // remove white space between 'static' and the next token continue; } // strip ': register()' from uniforms if ( token == ":" ) { if ( src.CheckTokenString( "register" ) ) { src.SkipUntilString( ";" ); program += ";"; continue; } } // strip in/program parameters from main if ( token == "void" && src.CheckTokenString( "main" ) ) { src.ExpectTokenString( "(" ); while( src.ReadToken( &token ) ) { if ( token == ")" ) { break; } } program += "\nvoid main()"; inMain = true; continue; } // strip 'const' from local variables in main() if ( token == "const" && inMain ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); src.SkipWhiteSpace( true ); // remove white space between 'const' and the next token continue; } // maintain indentation if ( token == "{" ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += "{"; int len = Min( idStr::Length( newline ) + 1, (int)sizeof( newline ) - 1 ); newline[len - 1] = '\t'; newline[len - 0] = '\0'; continue; } if ( token == "}" ) { int len = Max( idStr::Length( newline ) - 1, 0 ); newline[len] = '\0'; program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += "}"; continue; } // check for a type conversion bool foundType = false; for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) { if ( token.Cmp( typeConversion[i].typeCG ) == 0 ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += typeConversion[i].typeGLSL; foundType = true; break; } } if ( foundType ) { continue; } if ( r_useUniformArrays.GetBool() ) { // check for uniforms that need to be converted to the array bool isUniform = false; for ( int i = 0; i < uniformList.Num(); i++ ) { if ( token == uniformList[i] ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += va( "%s[%d /* %s */]", uniformArrayName, i, uniformList[i].c_str() ); isUniform = true; break; } } if ( isUniform ) { continue; } } // check for input/output parameters if ( src.CheckTokenString( "." ) ) { if ( token == "vertex" || token == "fragment" ) { idToken member; src.ReadToken( &member ); bool foundInOut = false; for ( int i = 0; i < varsIn.Num(); i++ ) { if ( member.Cmp( varsIn[i].nameCg ) == 0 ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += varsIn[i].nameGLSL; foundInOut = true; break; } } if ( !foundInOut ) { src.Error( "invalid input parameter %s.%s", token.c_str(), member.c_str() ); program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += token; program += "."; program += member; } continue; } if ( token == "result" ) { idToken member; src.ReadToken( &member ); bool foundInOut = false; for ( int i = 0; i < varsOut.Num(); i++ ) { if ( member.Cmp( varsOut[i].nameCg ) == 0 ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += varsOut[i].nameGLSL; foundInOut = true; break; } } if ( !foundInOut ) { src.Error( "invalid output parameter %s.%s", token.c_str(), member.c_str() ); program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += token; program += "."; program += member; } continue; } program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += token; program += "."; continue; } // check for a function conversion bool foundFunction = false; for ( int i = 0; builtinConversion[i].nameCG != NULL; i++ ) { if ( token.Cmp( builtinConversion[i].nameCG ) == 0 ) { program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += builtinConversion[i].nameGLSL; foundFunction = true; break; } } if ( foundFunction ) { continue; } program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" ); program += token; } idStr out; if ( isVertexProgram ) { out.ReAllocate( idStr::Length( vertexInsert ) + in.Length() * 2, false ); out += vertexInsert; } else { out.ReAllocate( idStr::Length( fragmentInsert ) + in.Length() * 2, false ); out += fragmentInsert; } if ( uniformList.Num() > 0 ) { if ( r_useUniformArrays.GetBool() ) { out += va( "\nuniform vec4 %s[%d];\n", uniformArrayName, uniformList.Num() ); } else { out += "\n"; for ( int i = 0; i < uniformList.Num(); i++ ) { out += "uniform vec4 "; out += uniformList[i]; out += ";\n"; } } } out += program; for ( int i = 0; i < uniformList.Num(); i++ ) { uniforms += uniformList[i]; uniforms += "\n"; } uniforms += "\n"; return out; }
/* ======================== StripDeadCode ======================== */ idStr StripDeadCode( const idStr & in, const char * name ) { if ( r_skipStripDeadCode.GetBool() ) { return in; } //idLexer src( LEXFL_NOFATALERRORS ); idParser src( LEXFL_NOFATALERRORS ); src.LoadMemory( in.c_str(), in.Length(), name ); src.AddDefine("PC"); idList< idCGBlock, TAG_RENDERPROG > blocks; blocks.SetNum( 100 ); idToken token; while ( !src.EndOfFile() ) { idCGBlock & block = blocks.Alloc(); // read prefix while ( src.ReadToken( &token ) ) { bool found = false; for ( int i = 0; i < numPrefixes; i++ ) { if ( token == prefixes[i] ) { found = true; break; } } if ( !found ) { for ( int i = 0; i < numTypes; i++ ) { if ( token == types[i] ) { found = true; break; } int typeLen = idStr::Length( types[i] ); if ( token.Cmpn( types[i], typeLen ) == 0 ) { for ( int j = 0; j < numTypePosts; j++ ) { if ( idStr::Cmp( token.c_str() + typeLen, typePosts[j] ) == 0 ) { found = true; break; } } if ( found ) { break; } } } } if ( found ) { if ( block.prefix.Length() > 0 && token.WhiteSpaceBeforeToken() ) { block.prefix += ' '; } block.prefix += token; } else { src.UnreadToken( &token ); break; } } if ( !src.ReadToken( &token ) ) { blocks.SetNum( blocks.Num() - 1 ); break; } block.name = token; if ( src.PeekTokenString( "=" ) || src.PeekTokenString( ":" ) || src.PeekTokenString( "[" ) ) { src.ReadToken( &token ); block.postfix = token; while ( src.ReadToken( &token ) ) { if ( token == ";" ) { block.postfix += ';'; break; } else { if ( token.WhiteSpaceBeforeToken() ){ block.postfix += ' '; } block.postfix += token; } } } else if ( src.PeekTokenString( "(" ) ) { idStr parms, body; src.ParseBracedSection( parms, -1, true, '(', ')' ); if ( src.CheckTokenString( ";" ) ) { block.postfix = parms + ";"; } else { src.ParseBracedSection( body, -1, true, '{', '}' ); block.postfix = parms + " " + body; } } else if ( src.PeekTokenString( "{" ) ) { src.ParseBracedSection( block.postfix, -1, true, '{', '}' ); if ( src.CheckTokenString( ";" ) ) { block.postfix += ';'; } } else if ( src.CheckTokenString( ";" ) ) { block.postfix = idStr( ';' ); } else { src.Warning( "Could not strip dead code -- unknown token %s\n", token.c_str() ); return in; } } idList<int, TAG_RENDERPROG> stack; for ( int i = 0; i < blocks.Num(); i++ ) { blocks[i].used = ( ( blocks[i].name == "main" ) || blocks[i].name.Right( 4 ) == "_ubo" ); if ( blocks[i].name == "include" ) { blocks[i].used = true; blocks[i].name = ""; // clear out the include tag } if ( blocks[i].used ) { stack.Append( i ); } } while ( stack.Num() > 0 ) { int i = stack[stack.Num() - 1]; stack.SetNum( stack.Num() - 1 ); idLexer src( LEXFL_NOFATALERRORS ); src.LoadMemory( blocks[i].postfix.c_str(), blocks[i].postfix.Length(), name ); while ( src.ReadToken( &token ) ) { for ( int j = 0; j < blocks.Num(); j++ ) { if ( !blocks[j].used ) { if ( token == blocks[j].name ) { blocks[j].used = true; stack.Append( j ); } } } } } idStr out; for ( int i = 0; i < blocks.Num(); i++ ) { if ( blocks[i].used ) { out += blocks[i].prefix; out += ' '; out += blocks[i].name; out += ' '; out += blocks[i].postfix; out += '\n'; } } return out; }