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