1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/xmlparser.h"
24 #include "common/archive.h"
25 #include "common/fs.h"
26 #include "common/memstream.h"
27 #include "common/system.h"
28
29 namespace Common {
30
~XMLParser()31 XMLParser::~XMLParser() {
32 while (!_activeKey.empty())
33 freeNode(_activeKey.pop());
34
35 delete _XMLkeys;
36 delete _stream;
37
38 for (List<XMLKeyLayout *>::iterator i = _layoutList.begin();
39 i != _layoutList.end(); ++i)
40 delete *i;
41
42 _layoutList.clear();
43 }
44
loadFile(const String & filename)45 bool XMLParser::loadFile(const String &filename) {
46 _stream = SearchMan.createReadStreamForMember(filename);
47 if (!_stream)
48 return false;
49
50 _fileName = filename;
51 return true;
52 }
53
loadFile(const FSNode & node)54 bool XMLParser::loadFile(const FSNode &node) {
55 _stream = node.createReadStream();
56 if (!_stream)
57 return false;
58
59 _fileName = node.getName();
60 return true;
61 }
62
loadBuffer(const byte * buffer,uint32 size,DisposeAfterUse::Flag disposable)63 bool XMLParser::loadBuffer(const byte *buffer, uint32 size, DisposeAfterUse::Flag disposable) {
64 _stream = new MemoryReadStream(buffer, size, disposable);
65 _fileName = "Memory Stream";
66 return true;
67 }
68
loadStream(SeekableReadStream * stream)69 bool XMLParser::loadStream(SeekableReadStream *stream) {
70 _stream = stream;
71 _fileName = "File Stream";
72 return _stream != nullptr;
73 }
74
close()75 void XMLParser::close() {
76 delete _stream;
77 _stream = nullptr;
78 }
79
parserError(const String & errStr)80 bool XMLParser::parserError(const String &errStr) {
81 _state = kParserError;
82
83 const int startPosition = _stream->pos();
84 int currentPosition = startPosition;
85 int lineCount = 1;
86 char c = 0;
87
88 _stream->seek(0, SEEK_SET);
89
90 while (currentPosition--) {
91 c = _stream->readByte();
92
93 if (c == '\n' || c == '\r')
94 lineCount++;
95 }
96
97 assert(_stream->pos() == startPosition);
98 currentPosition = startPosition;
99
100 Common::String errorMessage = Common::String::format("\n File <%s>, line %d:\n", _fileName.c_str(), lineCount);
101
102 if (startPosition > 1) {
103 int keyOpening = 0;
104 int keyClosing = 0;
105
106 while (currentPosition-- && keyOpening == 0) {
107 _stream->seek(-2, SEEK_CUR);
108 c = _stream->readByte();
109
110 if (c == '<')
111 keyOpening = currentPosition - 1;
112 else if (c == '>')
113 keyClosing = currentPosition;
114 }
115
116 _stream->seek(startPosition, SEEK_SET);
117 currentPosition = startPosition;
118 while (keyClosing == 0 && c && currentPosition++) {
119 c = _stream->readByte();
120
121 if (c == '>')
122 keyClosing = currentPosition;
123 }
124
125 currentPosition = (keyClosing - keyOpening);
126 _stream->seek(keyOpening, SEEK_SET);
127
128 while (currentPosition--)
129 errorMessage += (char)_stream->readByte();
130 }
131
132 errorMessage += "\n\nParser error: ";
133 errorMessage += errStr;
134 errorMessage += "\n\n";
135
136 g_system->logMessage(LogMessageType::kError, errorMessage.c_str());
137
138 return false;
139 }
140
parseXMLHeader(ParserNode * node)141 bool XMLParser::parseXMLHeader(ParserNode *node) {
142 assert(node->header);
143
144 if (_activeKey.size() != 1)
145 return parserError("XML Header is expected in the global scope.");
146
147 if (!node->values.contains("version"))
148 return parserError("Missing XML version in XML header.");
149
150 if (node->values["version"] != "1.0")
151 return parserError("Unsupported XML version.");
152
153 return true;
154 }
155
parseActiveKey(bool closed)156 bool XMLParser::parseActiveKey(bool closed) {
157 bool ignore = false;
158 assert(_activeKey.empty() == false);
159
160 ParserNode *key = _activeKey.top();
161
162 if (key->name == "xml" && key->header == true) {
163 assert(closed);
164 return parseXMLHeader(key) && closeKey();
165 }
166
167 XMLKeyLayout *layout = (_activeKey.size() == 1) ? _XMLkeys : getParentNode(key)->layout;
168
169 if (layout->children.contains(key->name)) {
170 key->layout = layout->children[key->name];
171
172 const StringMap &localMap = key->values;
173 int keyCount = localMap.size();
174
175 for (List<XMLKeyLayout::XMLKeyProperty>::const_iterator i = key->layout->properties.begin(); i != key->layout->properties.end(); ++i) {
176 if (i->required && !localMap.contains(i->name))
177 return parserError("Missing required property '" + i->name + "' inside key '" + key->name + "'");
178 else if (localMap.contains(i->name))
179 keyCount--;
180 }
181
182 if (keyCount > 0)
183 return parserError("Unhandled property inside key '" + key->name + "'.");
184
185 } else {
186 return parserError("Unexpected key in the active scope ('" + key->name + "').");
187 }
188
189 // check if any of the parents must be ignored.
190 // if a parent is ignored, all children are too.
191 for (int i = _activeKey.size() - 1; i >= 0; --i) {
192 if (_activeKey[i]->ignore)
193 ignore = true;
194 }
195
196 if (ignore == false && keyCallback(key) == false) {
197 // HACK: People may be stupid and overlook the fact that
198 // when keyCallback() fails, a parserError() must be set.
199 // We set it manually in that case.
200 if (_state != kParserError)
201 parserError("Unhandled exception when parsing '" + key->name + "' key.");
202
203 return false;
204 }
205
206 if (closed)
207 return closeKey();
208
209 return true;
210 }
211
parseKeyValue(String keyName)212 bool XMLParser::parseKeyValue(String keyName) {
213 assert(_activeKey.empty() == false);
214
215 if (_activeKey.top()->values.contains(keyName))
216 return false;
217
218 _token.clear();
219 char stringStart;
220
221 if (_char == '"' || _char == '\'') {
222 stringStart = _char;
223 _char = _stream->readByte();
224
225 while (_char && _char != stringStart) {
226 _token += _char;
227 _char = _stream->readByte();
228 }
229
230 if (_char == 0)
231 return false;
232
233 _char = _stream->readByte();
234
235 } else if (!parseToken()) {
236 return false;
237 }
238
239 _activeKey.top()->values[keyName] = _token;
240 return true;
241 }
242
parseIntegerKey(const char * key,int count,...)243 bool XMLParser::parseIntegerKey(const char *key, int count, ...) {
244 bool result;
245 va_list args;
246 va_start(args, count);
247 result = vparseIntegerKey(key, count, args);
248 va_end(args);
249 return result;
250 }
251
parseIntegerKey(const String & key,int count,...)252 bool XMLParser::parseIntegerKey(const String &key, int count, ...) {
253 bool result;
254 va_list args;
255 va_start(args, count);
256 result = vparseIntegerKey(key.c_str(), count, args);
257 va_end(args);
258 return result;
259 }
260
vparseIntegerKey(const char * key,int count,va_list args)261 bool XMLParser::vparseIntegerKey(const char *key, int count, va_list args) {
262 char *parseEnd;
263 int *num_ptr;
264
265 while (count--) {
266 while (isSpace(*key))
267 key++;
268
269 num_ptr = va_arg(args, int*);
270 *num_ptr = strtol(key, &parseEnd, 10);
271
272 key = parseEnd;
273
274 while (isSpace(*key))
275 key++;
276
277 if (count && *key++ != ',')
278 return false;
279 }
280
281 return (*key == 0);
282 }
283
closeKey()284 bool XMLParser::closeKey() {
285 bool ignore = false;
286 bool result = true;
287
288 for (int i = _activeKey.size() - 1; i >= 0; --i) {
289 if (_activeKey[i]->ignore)
290 ignore = true;
291 }
292
293 if (ignore == false)
294 result = closedKeyCallback(_activeKey.top());
295
296 freeNode(_activeKey.pop());
297
298 return result;
299 }
300
parse()301 bool XMLParser::parse() {
302 if (_stream == nullptr)
303 return false;
304
305 // Make sure we are at the start of the stream.
306 _stream->seek(0, SEEK_SET);
307
308 if (_XMLkeys == nullptr)
309 buildLayout();
310
311 while (!_activeKey.empty())
312 freeNode(_activeKey.pop());
313
314 cleanup();
315
316 bool activeClosure = false;
317 bool activeHeader = false;
318 bool selfClosure;
319
320 _state = kParserNeedHeader;
321 _activeKey.clear();
322
323 _char = _stream->readByte();
324
325 while (_char && _state != kParserError) {
326 if (skipSpaces())
327 continue;
328
329 if (skipComments())
330 continue;
331
332 switch (_state) {
333 case kParserNeedHeader:
334 case kParserNeedKey:
335 if (_char != '<') {
336 parserError("Parser expecting key start.");
337 break;
338 }
339
340 if ((_char = _stream->readByte()) == 0) {
341 parserError("Unexpected end of file.");
342 break;
343 }
344
345 if (_state == kParserNeedHeader) {
346 if (_char != '?') {
347 parserError("Expecting XML header.");
348 break;
349 }
350
351 _char = _stream->readByte();
352 activeHeader = true;
353 } else if (_char == '/') {
354 _char = _stream->readByte();
355 activeClosure = true;
356 } else if (_char == '?') {
357 parserError("Unexpected header. There may only be one XML header per file.");
358 break;
359 }
360
361 _state = kParserNeedKeyName;
362 break;
363
364 case kParserNeedKeyName:
365 if (!parseToken()) {
366 parserError("Invalid key name.");
367 break;
368 }
369
370 if (activeClosure) {
371 if (_activeKey.empty() || _token != _activeKey.top()->name) {
372 parserError("Unexpected closure.");
373 break;
374 }
375 } else {
376 ParserNode *node = allocNode(); // new ParserNode;
377 node->name = _token;
378 node->ignore = false;
379 node->header = activeHeader;
380 node->depth = _activeKey.size();
381 node->layout = nullptr;
382 _activeKey.push(node);
383 }
384
385 _state = kParserNeedPropertyName;
386 break;
387
388 case kParserNeedPropertyName:
389 if (activeClosure) {
390 if (!closeKey()) {
391 parserError("Missing data when closing key '" + _activeKey.top()->name + "'.");
392 break;
393 }
394
395 activeClosure = false;
396
397 if (_char != '>')
398 parserError("Invalid syntax in key closure.");
399 else
400 _state = kParserNeedKey;
401
402 _char = _stream->readByte();
403 break;
404 }
405
406 selfClosure = false;
407
408 if (_char == '/' || (_char == '?' && activeHeader)) {
409 selfClosure = true;
410 _char = _stream->readByte();
411 }
412
413 if (_char == '>') {
414 if (activeHeader && !selfClosure) {
415 parserError("XML Header must be self-closed.");
416 } else if (parseActiveKey(selfClosure)) {
417 _char = _stream->readByte();
418 _state = kParserNeedKey;
419 }
420
421 activeHeader = false;
422 break;
423 }
424
425 if (selfClosure)
426 parserError("Expecting key closure after '/' symbol.");
427 else if (!parseToken())
428 parserError("Error when parsing key value.");
429 else
430 _state = kParserNeedPropertyOperator;
431
432 break;
433
434 case kParserNeedPropertyOperator:
435 if (_char != '=')
436 parserError("Syntax error after key name.");
437 else
438 _state = kParserNeedPropertyValue;
439
440 _char = _stream->readByte();
441 break;
442
443 case kParserNeedPropertyValue:
444 if (!parseKeyValue(_token))
445 parserError("Invalid key value.");
446 else
447 _state = kParserNeedPropertyName;
448
449 break;
450
451 default:
452 break;
453 }
454 }
455
456 if (_state == kParserError)
457 return false;
458
459 if (_state != kParserNeedKey || !_activeKey.empty())
460 return parserError("Unexpected end of file.");
461
462 return true;
463 }
464
skipSpaces()465 bool XMLParser::skipSpaces() {
466 if (!isSpace(_char))
467 return false;
468
469 while (_char && isSpace(_char))
470 _char = _stream->readByte();
471
472 return true;
473 }
474
skipComments()475 bool XMLParser::skipComments() {
476 if (_char == '<') {
477 _char = _stream->readByte();
478
479 if (_char != '!') {
480 _stream->seek(-1, SEEK_CUR);
481 _char = '<';
482 return false;
483 }
484
485 if (_stream->readByte() != '-' || _stream->readByte() != '-')
486 return parserError("Malformed comment syntax.");
487
488 _char = _stream->readByte();
489
490 while (_char) {
491 if (_char == '-') {
492 if (_stream->readByte() == '-') {
493
494 if (_stream->readByte() != '>')
495 return parserError("Malformed comment (double-hyphen inside comment body).");
496
497 _char = _stream->readByte();
498 return true;
499 }
500 }
501
502 _char = _stream->readByte();
503 }
504
505 return parserError("Comment has no closure.");
506 }
507
508 return false;
509 }
510
parseToken()511 bool XMLParser::parseToken() {
512 _token.clear();
513
514 while (isValidNameChar(_char)) {
515 _token += _char;
516 _char = _stream->readByte();
517 }
518
519 return isSpace(_char) != 0 || _char == '>' || _char == '=' || _char == '/';
520 }
521
522 } // End of namespace Common
523