1 /* 2 Copyright (C) 2010-2014 Kristian Duske 3 4 This file is part of TrenchBroom. 5 6 TrenchBroom is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 TrenchBroom is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "DefParser.h" 21 22 #include "CollectionUtils.h" 23 #include "Exceptions.h" 24 #include "Assets/EntityDefinition.h" 25 #include "Assets/AttributeDefinition.h" 26 #include "Assets/ModelDefinition.h" 27 #include "IO/ParserStatus.h" 28 29 namespace TrenchBroom { 30 namespace IO { DefTokenizer(const char * begin,const char * end)31 DefTokenizer::DefTokenizer(const char* begin, const char* end) : 32 Tokenizer(begin, end) {} 33 DefTokenizer(const String & str)34 DefTokenizer::DefTokenizer(const String& str) : 35 Tokenizer(str) {} 36 37 const String DefTokenizer::WordDelims = " \t\n\r()[]{};,="; 38 emitToken()39 DefTokenizer::Token DefTokenizer::emitToken() { 40 while (!eof()) { 41 size_t startLine = line(); 42 size_t startColumn = column(); 43 const char* c = curPos(); 44 switch (*c) { 45 case '/': { 46 advance(); 47 if (curChar() == '*') { 48 // eat all chars immediately after the '*' because it's often followed by QUAKE 49 do { advance(); } while (!eof() && !isWhitespace(curChar())); 50 return Token(DefToken::ODefinition, c, curPos(), offset(c), startLine, startColumn); 51 } else if (curChar() == '/') { 52 discardUntil("\n\r"); 53 break; 54 } 55 // fall through and try to read as word 56 retreat(); 57 } 58 case '*': { 59 advance(); 60 if (curChar() == '/') 61 return Token(DefToken::CDefinition, c, curPos(), offset(c), startLine, startColumn); 62 // fall through and try to read as word 63 retreat(); 64 } 65 case '(': 66 advance(); 67 return Token(DefToken::OParenthesis, c, c + 1, offset(c), startLine, startColumn); 68 case ')': 69 advance(); 70 return Token(DefToken::CParenthesis, c, c + 1, offset(c), startLine, startColumn); 71 case '{': 72 advance(); 73 return Token(DefToken::OBrace, c, c + 1, offset(c), startLine, startColumn); 74 case '}': 75 advance(); 76 return Token(DefToken::CBrace, c, c + 1, offset(c), startLine, startColumn); 77 case '=': 78 advance(); 79 return Token(DefToken::Equality, c, c + 1, offset(c), startLine, startColumn); 80 case ';': 81 advance(); 82 return Token(DefToken::Semicolon, c, c + 1, offset(c), startLine, startColumn); 83 case '\r': 84 advance(); 85 case '\n': 86 advance(); 87 return Token(DefToken::Newline, c, c + 1, offset(c), startLine, startColumn); 88 case ',': 89 advance(); 90 return Token(DefToken::Comma, c, c + 1, offset(c), startLine, startColumn); 91 case ' ': 92 case '\t': 93 discardWhile(" \t"); 94 break; 95 case '"': { // quoted string 96 advance(); 97 c = curPos(); 98 const char* e = readQuotedString(); 99 return Token(DefToken::QuotedString, c, e, offset(c), startLine, startColumn); 100 } 101 default: { // integer, decimal or word 102 const char* e = readInteger(WordDelims); 103 if (e != NULL) 104 return Token(DefToken::Integer, c, e, offset(c), startLine, startColumn); 105 e = readDecimal(WordDelims); 106 if (e != NULL) 107 return Token(DefToken::Decimal, c, e, offset(c), startLine, startColumn); 108 e = readString(WordDelims); 109 if (e == NULL) 110 throw ParserException(startLine, startColumn, "Unexpected character: " + String(c, 1)); 111 return Token(DefToken::Word, c, e, offset(c), startLine, startColumn); 112 } 113 } 114 } 115 return Token(DefToken::Eof, NULL, NULL, length(), line(), column()); 116 } 117 DefParser(const char * begin,const char * end,const Color & defaultEntityColor)118 DefParser::DefParser(const char* begin, const char* end, const Color& defaultEntityColor) : 119 m_defaultEntityColor(defaultEntityColor), 120 m_tokenizer(DefTokenizer(begin, end)) {} 121 DefParser(const String & str,const Color & defaultEntityColor)122 DefParser::DefParser(const String& str, const Color& defaultEntityColor) : 123 m_defaultEntityColor(defaultEntityColor), 124 m_tokenizer(DefTokenizer(str)) {} 125 tokenNames() const126 DefParser::TokenNameMap DefParser::tokenNames() const { 127 using namespace DefToken; 128 129 TokenNameMap names; 130 names[Integer] = "integer"; 131 names[Decimal] = "decimal"; 132 names[QuotedString] = "quoted string"; 133 names[OParenthesis] = "'('"; 134 names[CParenthesis] = "')'"; 135 names[OBrace] = "'{'"; 136 names[CBrace] = "'}'"; 137 names[Word] = "word"; 138 names[ODefinition] = "'/*'"; 139 names[CDefinition] = "'*/'"; 140 names[Semicolon] = "';'"; 141 names[Newline] = "newline"; 142 names[Comma] = "','"; 143 names[Equality] = "'='"; 144 names[Eof] = "end of file"; 145 return names; 146 } 147 doParseDefinitions(ParserStatus & status)148 Assets::EntityDefinitionList DefParser::doParseDefinitions(ParserStatus& status) { 149 Assets::EntityDefinitionList definitions; 150 try { 151 Assets::EntityDefinition* definition = parseDefinition(status); 152 status.progress(m_tokenizer.progress()); 153 while (definition != NULL) { 154 definitions.push_back(definition); 155 definition = parseDefinition(status); 156 status.progress(m_tokenizer.progress()); 157 } 158 return definitions; 159 } catch (...) { 160 VectorUtils::clearAndDelete(definitions); 161 throw; 162 } 163 } 164 parseDefinition(ParserStatus & status)165 Assets::EntityDefinition* DefParser::parseDefinition(ParserStatus& status) { 166 Token token = m_tokenizer.nextToken(); 167 while (token.type() != DefToken::Eof && token.type() != DefToken::ODefinition) 168 token = m_tokenizer.nextToken(); 169 if (token.type() == DefToken::Eof) 170 return NULL; 171 172 expect(status, DefToken::ODefinition, token); 173 174 StringList baseClasses; 175 EntityDefinitionClassInfo classInfo; 176 177 token = m_tokenizer.nextToken(); 178 expect(status, DefToken::Word, token); 179 classInfo.setName(token.data()); 180 181 token = m_tokenizer.peekToken(); 182 expect(status, DefToken::OParenthesis | DefToken::Newline, token); 183 if (token.type() == DefToken::OParenthesis) { 184 classInfo.setColor(parseColor(status)); 185 186 token = m_tokenizer.peekToken(); 187 expect(status, DefToken::OParenthesis | DefToken::Word, token); 188 if (token.type() == DefToken::OParenthesis) { 189 classInfo.setSize(parseBounds(status)); 190 } else if (token.data() == "?") { 191 m_tokenizer.nextToken(); 192 } else { 193 expect(status, "question mark: ?", token); 194 } 195 196 token = m_tokenizer.peekToken(); 197 if (token.type() == DefToken::Word) 198 classInfo.addAttributeDefinition(parseSpawnflags(status)); 199 } 200 201 expect(status, DefToken::Newline, token = m_tokenizer.nextToken()); 202 203 Assets::AttributeDefinitionMap attributes; 204 Assets::ModelDefinitionList models; 205 StringList superClasses; 206 parserAttributes(status, attributes, models, superClasses); 207 classInfo.addAttributeDefinitions(attributes); 208 classInfo.addModelDefinitions(models); 209 210 classInfo.setDescription(StringUtils::trim(parseDescription())); 211 expect(status, DefToken::CDefinition, token = m_tokenizer.nextToken()); 212 213 if (classInfo.hasColor()) { 214 classInfo.resolveBaseClasses(m_baseClasses, superClasses); 215 if (classInfo.hasSize()) // point definition 216 return new Assets::PointEntityDefinition(classInfo.name(), classInfo.color(), classInfo.size(), classInfo.description(), classInfo.attributeList(), classInfo.models()); 217 return new Assets::BrushEntityDefinition(classInfo.name(), m_defaultEntityColor, classInfo.description(), classInfo.attributeList()); 218 } 219 220 // base definition 221 m_baseClasses[classInfo.name()] = classInfo; 222 return parseDefinition(status); 223 } 224 parseSpawnflags(ParserStatus & status)225 Assets::AttributeDefinitionPtr DefParser::parseSpawnflags(ParserStatus& status) { 226 Assets::FlagsAttributeDefinition* definition = new Assets::FlagsAttributeDefinition(Model::AttributeNames::Spawnflags); 227 size_t numOptions = 0; 228 229 try { 230 Token token = m_tokenizer.peekToken(); 231 while (token.type() == DefToken::Word) { 232 token = m_tokenizer.nextToken(); 233 String name = token.data(); 234 int value = 1 << numOptions++; 235 definition->addOption(value, name, "", false); 236 token = m_tokenizer.peekToken(); 237 } 238 } catch (...) { 239 delete definition; 240 throw; 241 } 242 243 return Assets::AttributeDefinitionPtr(definition); 244 } 245 parserAttributes(ParserStatus & status,Assets::AttributeDefinitionMap & attributes,Assets::ModelDefinitionList & modelDefinitions,StringList & superClasses)246 void DefParser::parserAttributes(ParserStatus& status, Assets::AttributeDefinitionMap& attributes, Assets::ModelDefinitionList& modelDefinitions, StringList& superClasses) { 247 Token token = m_tokenizer.peekToken(); 248 if (token.type() == DefToken::OBrace) { 249 token = m_tokenizer.nextToken(); 250 while (parseAttribute(status, attributes, modelDefinitions, superClasses)); 251 } 252 } 253 parseAttribute(ParserStatus & status,Assets::AttributeDefinitionMap & attributes,Assets::ModelDefinitionList & modelDefinitions,StringList & superClasses)254 bool DefParser::parseAttribute(ParserStatus& status, Assets::AttributeDefinitionMap& attributes, Assets::ModelDefinitionList& modelDefinitions, StringList& superClasses) { 255 Token token; 256 expect(status, DefToken::Word | DefToken::CBrace, token = nextTokenIgnoringNewlines()); 257 if (token.type() != DefToken::Word) 258 return false; 259 260 String typeName = token.data(); 261 if (typeName == "default") { 262 // ignore these attributes 263 parseDefaultAttribute(status); 264 } else if (typeName == "base") { 265 parseBaseAttribute(status, superClasses); 266 } else if (typeName == "choice") { 267 parseChoiceAttribute(status, attributes); 268 } else if (typeName == "model") { 269 parseModelDefinitions(status, modelDefinitions); 270 } 271 272 expect(status, DefToken::Semicolon, token = nextTokenIgnoringNewlines()); 273 return true; 274 } 275 parseDefaultAttribute(ParserStatus & status)276 void DefParser::parseDefaultAttribute(ParserStatus& status) { 277 Token token; 278 expect(status, DefToken::OParenthesis, token = nextTokenIgnoringNewlines()); 279 expect(status, DefToken::QuotedString, token = nextTokenIgnoringNewlines()); 280 const String attributeName = token.data(); 281 expect(status, DefToken::Comma, token = nextTokenIgnoringNewlines()); 282 expect(status, DefToken::QuotedString, token = nextTokenIgnoringNewlines()); 283 const String attributeValue = token.data(); 284 expect(status, DefToken::CParenthesis, token = nextTokenIgnoringNewlines()); 285 } 286 parseBaseAttribute(ParserStatus & status,StringList & superClasses)287 void DefParser::parseBaseAttribute(ParserStatus& status, StringList& superClasses) { 288 Token token; 289 expect(status, DefToken::OParenthesis, token = nextTokenIgnoringNewlines()); 290 expect(status, DefToken::QuotedString, token = nextTokenIgnoringNewlines()); 291 const String basename = token.data(); 292 expect(status, DefToken::CParenthesis, token = nextTokenIgnoringNewlines()); 293 294 superClasses.push_back(basename); 295 } 296 parseChoiceAttribute(ParserStatus & status,Assets::AttributeDefinitionMap & attributes)297 void DefParser::parseChoiceAttribute(ParserStatus& status, Assets::AttributeDefinitionMap& attributes) { 298 Token token; 299 expect(status, DefToken::QuotedString, token = m_tokenizer.nextToken()); 300 const String attributeName = token.data(); 301 302 Assets::ChoiceAttributeOption::List options; 303 expect(status, DefToken::OParenthesis, token = nextTokenIgnoringNewlines()); 304 token = nextTokenIgnoringNewlines(); 305 while (token.type() == DefToken::OParenthesis) { 306 expect(status, DefToken::Integer, token = nextTokenIgnoringNewlines()); 307 const String name = token.data(); 308 expect(status, DefToken::Comma, token = nextTokenIgnoringNewlines()); 309 expect(status, DefToken::QuotedString, token = nextTokenIgnoringNewlines()); 310 const String value = token.data(); 311 options.push_back(Assets::ChoiceAttributeOption(name, value)); 312 313 expect(status, DefToken::CParenthesis, token = nextTokenIgnoringNewlines()); 314 token = nextTokenIgnoringNewlines(); 315 } 316 317 expect(status, DefToken::CParenthesis, token); 318 319 attributes[attributeName] = Assets::AttributeDefinitionPtr(new Assets::ChoiceAttributeDefinition(attributeName, "", "", options)); 320 } 321 parseModelDefinitions(ParserStatus & status,Assets::ModelDefinitionList & modelDefinitions)322 void DefParser::parseModelDefinitions(ParserStatus& status, Assets::ModelDefinitionList& modelDefinitions) { 323 Assets::ModelDefinitionList result; 324 Token token; 325 expect(status, DefToken::OParenthesis, token = m_tokenizer.nextToken()); 326 expect(status, DefToken::QuotedString | DefToken::Word | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 327 if (token.type() == DefToken::QuotedString || token.type() == DefToken::Word) { 328 m_tokenizer.pushToken(token); 329 do { 330 expect(status, DefToken::QuotedString | DefToken::Word, token = m_tokenizer.peekToken()); 331 if (token.type() == DefToken::QuotedString) 332 parseStaticModelDefinition(status, modelDefinitions); 333 else 334 parseDynamicModelDefinition(status, modelDefinitions); 335 expect(status, DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 336 } while (token.type() == DefToken::Comma); 337 } 338 } 339 parseStaticModelDefinition(ParserStatus & status,Assets::ModelDefinitionList & modelDefinitions)340 void DefParser::parseStaticModelDefinition(ParserStatus& status, Assets::ModelDefinitionList& modelDefinitions) { 341 Token token; 342 expect(status, DefToken::QuotedString, token = m_tokenizer.nextToken()); 343 const String pathStr = token.data(); 344 const IO::Path path(!pathStr.empty() && pathStr[0] == ':' ? pathStr.substr(1) : pathStr); 345 346 std::vector<size_t> indices; 347 348 expect(status, DefToken::Integer | DefToken::Word | DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 349 if (token.type() == DefToken::Integer) { 350 indices.push_back(token.toInteger<size_t>()); 351 expect(status, DefToken::Integer | DefToken::Word | DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 352 if (token.type() == DefToken::Integer) { 353 indices.push_back(token.toInteger<size_t>()); 354 expect(status, DefToken::Word | DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 355 } 356 } 357 358 size_t skinIndex = 0; 359 size_t frameIndex = 0; 360 if (!indices.empty()) { 361 skinIndex = indices[0]; 362 if (indices.size() > 1) 363 frameIndex = indices[1]; 364 } 365 366 if (token.type() == DefToken::Word) { 367 const String attributeKey = token.data(); 368 expect(status, DefToken::Equality, token = m_tokenizer.nextToken()); 369 expect(status, DefToken::QuotedString | DefToken::Integer, token = m_tokenizer.nextToken()); 370 if (token.type() == DefToken::QuotedString) { 371 const String attributeValue = token.data(); 372 modelDefinitions.push_back(Assets::ModelDefinitionPtr(new Assets::StaticModelDefinition(path, skinIndex, frameIndex, attributeKey, attributeValue))); 373 } else { 374 const int flagValue = token.toInteger<int>(); 375 modelDefinitions.push_back(Assets::ModelDefinitionPtr(new Assets::StaticModelDefinition(path, skinIndex, frameIndex, attributeKey, flagValue))); 376 } 377 } else { 378 m_tokenizer.pushToken(token); 379 modelDefinitions.push_back(Assets::ModelDefinitionPtr(new Assets::StaticModelDefinition(path, skinIndex, frameIndex))); 380 } 381 } 382 parseDynamicModelDefinition(ParserStatus & status,Assets::ModelDefinitionList & modelDefinitions)383 void DefParser::parseDynamicModelDefinition(ParserStatus& status, Assets::ModelDefinitionList& modelDefinitions) { 384 Token token; 385 String pathKey, skinKey, frameKey; 386 387 expect(status, DefToken::Word, token = m_tokenizer.nextToken()); 388 if (!StringUtils::caseInsensitiveEqual("pathKey", token.data())) { 389 const String msg = "Expected 'pathKey', but found '" + token.data() + "'"; 390 status.error(token.line(), token.column(), msg); 391 throw ParserException(token.line(), token.column(), msg); 392 } 393 394 expect(status, DefToken::Equality, token = m_tokenizer.nextToken()); 395 expect(status, DefToken::QuotedString, token = m_tokenizer.nextToken()); 396 pathKey = token.data(); 397 398 expect(status, DefToken::Word | DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 399 while (token.type() == DefToken::Word) { 400 if (StringUtils::caseInsensitiveEqual("skinKey", token.data())) { 401 m_tokenizer.pushToken(token); 402 skinKey = parseNamedValue(status, "skinKey"); 403 } else if (StringUtils::caseInsensitiveEqual("frameKey", token.data())) { 404 m_tokenizer.pushToken(token); 405 frameKey = parseNamedValue(status, "frameKey"); 406 } else { 407 const String msg = "Expected 'skinKey' or 'frameKey', but found '" + token.data() + "'"; 408 status.error(token.line(), token.column(), msg); 409 throw ParserException(token.line(), token.column(), msg); 410 } 411 expect(status, DefToken::Word | DefToken::Comma | DefToken::CParenthesis, token = m_tokenizer.nextToken()); 412 } 413 m_tokenizer.pushToken(token); 414 415 modelDefinitions.push_back(Assets::ModelDefinitionPtr(new Assets::DynamicModelDefinition(pathKey, skinKey, frameKey))); 416 } 417 parseNamedValue(ParserStatus & status,const String & name)418 String DefParser::parseNamedValue(ParserStatus& status, const String& name) { 419 Token token; 420 expect(status, DefToken::Word, token = m_tokenizer.nextToken()); 421 assert(StringUtils::caseInsensitiveEqual(name, token.data())); 422 expect(status, DefToken::Equality, token = m_tokenizer.nextToken()); 423 expect(status, DefToken::QuotedString, token = m_tokenizer.nextToken()); 424 return token.data(); 425 } 426 parseDescription()427 String DefParser::parseDescription() { 428 Token token = m_tokenizer.peekToken(); 429 if (token.type() == DefToken::CDefinition) 430 return ""; 431 return m_tokenizer.readRemainder(DefToken::CDefinition); 432 } 433 parseVector(ParserStatus & status)434 Vec3 DefParser::parseVector(ParserStatus& status) { 435 Token token; 436 Vec3 vec; 437 for (size_t i = 0; i < 3; i++) { 438 expect(status, DefToken::Integer | DefToken::Decimal, token = m_tokenizer.nextToken()); 439 vec[i] = token.toFloat<double>(); 440 } 441 return vec; 442 } 443 parseBounds(ParserStatus & status)444 BBox3 DefParser::parseBounds(ParserStatus& status) { 445 BBox3 bounds; 446 Token token; 447 expect(status, DefToken::OParenthesis, token = m_tokenizer.nextToken()); 448 bounds.min = parseVector(status); 449 expect(status, DefToken::CParenthesis, token = m_tokenizer.nextToken()); 450 expect(status, DefToken::OParenthesis, token = m_tokenizer.nextToken()); 451 bounds.max = parseVector(status); 452 expect(status, DefToken::CParenthesis, token = m_tokenizer.nextToken()); 453 return bounds; 454 } 455 parseColor(ParserStatus & status)456 Color DefParser::parseColor(ParserStatus& status) { 457 Color color; 458 Token token; 459 expect(status, DefToken::OParenthesis, token = m_tokenizer.nextToken()); 460 for (size_t i = 0; i < 3; i++) { 461 expect(status, DefToken::Decimal | DefToken::Integer, token = m_tokenizer.nextToken()); 462 color[i] = token.toFloat<float>(); 463 if (color[i] > 1.0f) 464 color[i] /= 255.0f; 465 } 466 expect(status, DefToken::CParenthesis, token = m_tokenizer.nextToken()); 467 color[3] = 1.0f; 468 return color; 469 } 470 nextTokenIgnoringNewlines()471 DefParser::Token DefParser::nextTokenIgnoringNewlines() { 472 Token token = m_tokenizer.nextToken(); 473 while (token.type() == DefToken::Newline) 474 token = m_tokenizer.nextToken(); 475 return token; 476 } 477 } 478 } 479