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 #ifndef XML_PARSER_H 24 #define XML_PARSER_H 25 26 #include "common/scummsys.h" 27 #include "common/types.h" 28 29 #include "common/fs.h" 30 #include "common/list.h" 31 #include "common/hashmap.h" 32 #include "common/hash-str.h" 33 #include "common/stack.h" 34 #include "common/memorypool.h" 35 36 37 namespace Common { 38 39 /** 40 * @defgroup common_xmlparser XML parser 41 * @ingroup common 42 * 43 * @brief The XML parser allows for parsing XML-like files. 44 * 45 * @{ 46 */ 47 48 class SeekableReadStream; 49 50 #define MAX_XML_DEPTH 8 51 52 #define XML_KEY(keyName) {\ 53 lay = new CustomXMLKeyLayout;\ 54 lay->callback = (&kLocalParserName::parserCallback_##keyName);\ 55 layout.top()->children[#keyName] = lay;\ 56 layout.push(lay); \ 57 _layoutList.push_back(lay); 58 59 #define XML_KEY_RECURSIVE(keyName) {\ 60 layout.top()->children[#keyName] = layout.top();\ 61 layout.push(layout.top());\ 62 } 63 64 #define KEY_END() layout.pop(); } 65 66 #define XML_PROP(propName, req) {\ 67 prop.name = #propName; \ 68 prop.required = req; \ 69 layout.top()->properties.push_back(prop); } 70 71 72 73 #define CUSTOM_XML_PARSER(parserName) \ 74 protected: \ 75 typedef parserName kLocalParserName; \ 76 bool keyCallback(ParserNode *node) override {return node->layout->doCallback(this, node); }\ 77 struct CustomXMLKeyLayout : public XMLKeyLayout {\ 78 typedef bool (parserName::*ParserCallback)(ParserNode *node);\ 79 ParserCallback callback;\ 80 bool doCallback(XMLParser *parent, ParserNode *node) {return ((kLocalParserName *)parent->*callback)(node);} };\ 81 virtual void buildLayout() override { \ 82 Common::Stack<XMLKeyLayout *> layout; \ 83 CustomXMLKeyLayout *lay = 0; \ 84 XMLKeyLayout::XMLKeyProperty prop; \ 85 _XMLkeys = new CustomXMLKeyLayout; \ 86 layout.push(_XMLkeys); 87 88 #define PARSER_END() layout.clear(); } 89 90 /** 91 * The base XMLParser class implements generic functionality for parsing 92 * XML-like files. 93 * 94 * In order to use it, it must be inherited with a child class that implements 95 * the XMLParser::keyCallback() function. 96 * 97 * @see XMLParser::keyCallback() 98 */ 99 class XMLParser { 100 public: 101 /** 102 * Parser constructor. 103 */ XMLParser()104 XMLParser() : _XMLkeys(nullptr), _stream(nullptr) {} 105 106 virtual ~XMLParser(); 107 108 /** Active state for the parser */ 109 enum ParserState { 110 kParserNeedHeader, 111 kParserNeedKey, 112 kParserNeedKeyName, 113 114 kParserNeedPropertyName, 115 kParserNeedPropertyOperator, 116 kParserNeedPropertyValue, 117 118 kParserError 119 }; 120 121 struct XMLKeyLayout; 122 struct ParserNode; 123 124 typedef HashMap<String, XMLParser::XMLKeyLayout*, IgnoreCase_Hash, IgnoreCase_EqualTo> ChildMap; 125 126 /** nested struct representing the layout of the XML file */ 127 struct XMLKeyLayout { 128 struct XMLKeyProperty { 129 String name; 130 bool required; 131 }; 132 133 List<XMLKeyProperty> properties; 134 ChildMap children; 135 136 virtual bool doCallback(XMLParser *parent, ParserNode *node) = 0; 137 ~XMLKeyLayoutXMLKeyLayout138 virtual ~XMLKeyLayout() { 139 properties.clear(); 140 } 141 }; 142 143 XMLKeyLayout *_XMLkeys; 144 145 /** Struct representing a parsed node */ 146 struct ParserNode { 147 String name; 148 StringMap values; 149 bool ignore; 150 bool header; 151 int depth; 152 XMLKeyLayout *layout; 153 }; 154 155 ObjectPool<ParserNode, MAX_XML_DEPTH> _nodePool; 156 allocNode()157 ParserNode *allocNode() { 158 return new (_nodePool) ParserNode; 159 } 160 freeNode(ParserNode * node)161 void freeNode(ParserNode *node) { 162 _nodePool.deleteChunk(node); 163 } 164 165 /** 166 * Loads a file into the parser. 167 * Used for the loading of Theme Description files 168 * straight from the filesystem. 169 * 170 * @param filename Name of the file to load. 171 */ 172 bool loadFile(const String &filename); 173 174 bool loadFile(const FSNode &node); 175 176 /** 177 * Loads a memory buffer into the parser. 178 * Used for loading the default theme fallback directly 179 * from memory if no themes can be found. 180 * 181 * @param buffer Pointer to the buffer. 182 * @param size Size of the buffer 183 * @param disposable Sets if the XMLParser owns the buffer, 184 * i.e. if it can be freed safely after it's 185 * no longer needed by the parser. 186 */ 187 bool loadBuffer(const byte *buffer, uint32 size, DisposeAfterUse::Flag disposable = DisposeAfterUse::NO); 188 189 bool loadStream(SeekableReadStream *stream); 190 191 void close(); 192 193 /** 194 * The actual parsing function. 195 * Parses the loaded data stream, returns true if successful. 196 */ 197 bool parse(); 198 199 /** 200 * Returns the active node being parsed (the one on top of 201 * the node stack). 202 */ getActiveNode()203 ParserNode *getActiveNode() { 204 if (!_activeKey.empty()) 205 return _activeKey.top(); 206 207 return nullptr; 208 } 209 210 /** 211 * Returns the parent of a given node in the stack. 212 */ getParentNode(ParserNode * child)213 ParserNode *getParentNode(ParserNode *child) { 214 return child->depth > 0 ? _activeKey[child->depth - 1] : 0; 215 } 216 217 protected: 218 219 /** 220 * The buildLayout function builds the layout for the parser to use 221 * based on a series of helper macros. This function is automatically 222 * generated by the CUSTOM_XML_PARSER() macro on custom parsers. 223 * 224 * See the documentation regarding XML layouts. 225 */ 226 virtual void buildLayout() = 0; 227 228 /** 229 * The keycallback function is automatically overloaded on custom parsers 230 * when using the CUSTOM_XML_PARSER() macro. 231 * 232 * Its job is to call the corresponding Callback function for the given node. 233 * A function for each key type must be declared separately. See the custom 234 * parser creation instructions. 235 * 236 * When parsing a key in such function, one may chose to skip it, e.g. because it's not needed 237 * on the current configuration. In order to ignore a key, you must set 238 * the "ignore" field of its KeyNode struct to "true": The key and all its children 239 * will then be automatically ignored by the parser. 240 * 241 * The callback function must return true if the key was properly handled (this includes the case when the 242 * key is being ignored). False otherwise. The return of keyCallback() is the same as 243 * the callback function's. 244 * See the sample implementation in GUI::ThemeParser. 245 */ 246 virtual bool keyCallback(ParserNode *node) = 0; 247 248 /** 249 * The closed key callback function MAY be overloaded by inheriting classes to 250 * implement parser-specific functions. 251 * 252 * The closedKeyCallback is issued once a key has been finished parsing, to let 253 * the parser verify that all the required subkeys, etc, were included. 254 * 255 * Unlike the keyCallbacks(), there's just a closedKeyCallback() for all keys. 256 * Use "node->name" to distinguish between each key type. 257 * 258 * Returns true if the key was properly closed, false otherwise. 259 * By default, all keys are properly closed. 260 */ closedKeyCallback(ParserNode * node)261 virtual bool closedKeyCallback(ParserNode *node) { 262 return true; 263 } 264 265 /** 266 * Called when a node is closed. Manages its cleanup and calls the 267 * closing callback function if needed. 268 */ 269 bool closeKey(); 270 271 /** 272 * Parses the value of a given key. There's no reason to overload this. 273 */ 274 bool parseKeyValue(String keyName); 275 276 /** 277 * Called once a key has been parsed. It handles the closing/cleanup of the 278 * node stack and calls the keyCallback. 279 */ 280 bool parseActiveKey(bool closed); 281 282 /** 283 * Prints an error message when parsing fails and stops the parser. 284 * Parser error always returns "false" so we can pass the return value 285 * directly and break down the parsing. 286 */ 287 bool parserError(const String &errStr); 288 289 /** 290 * Skips spaces/whitelines etc. 291 * @return true if any spaces were skipped. 292 */ 293 bool skipSpaces(); 294 295 /** 296 * Skips comment blocks and comment lines. 297 * @return true if any comments were skipped. 298 */ 299 bool skipComments(); 300 301 /** 302 * Check if a given character can be part of a KEY or VALUE name. 303 * Overload this if you want to support keys with strange characters 304 * in their name. 305 */ isValidNameChar(char c)306 virtual inline bool isValidNameChar(char c) { 307 return isAlnum(c) || c == '_'; 308 } 309 310 /** 311 * Parses a the first textual token found. 312 */ 313 bool parseToken(); 314 315 /** 316 * Parses the values inside an integer key. 317 * The count parameter specifies the number of values inside 318 * the key, which are expected to be separated with commas. 319 * 320 * Sample usage: 321 * parseIntegerKey("255, 255, 255", 3, &red, &green, &blue); 322 * [will parse each field into its own integer] 323 * 324 * parseIntegerKey("1234", 1, &number); 325 * [will parse the single number into the variable] 326 * 327 * @param key String containing the integers to be parsed. 328 * @param count Number of comma-separated ints in the string. 329 * @param ... Integer variables to store the parsed ints, passed 330 * by reference. 331 * @returns True if the parsing succeeded. 332 */ 333 bool parseIntegerKey(const char *key, int count, ...); 334 bool parseIntegerKey(const String &keyStr, int count, ...); 335 bool vparseIntegerKey(const char *key, int count, va_list args); 336 337 bool parseXMLHeader(ParserNode *node); 338 339 /** 340 * Overload if your parser needs to support parsing the same file 341 * several times, so you can clean up the internal state of the 342 * parser before each parse. 343 */ cleanup()344 virtual void cleanup() {} 345 346 List<XMLKeyLayout *> _layoutList; 347 348 private: 349 char _char; 350 SeekableReadStream *_stream; 351 String _fileName; 352 353 ParserState _state; /** Internal state of the parser */ 354 355 String _error; /** Current error message */ 356 String _token; /** Current text token */ 357 358 Stack<ParserNode *> _activeKey; /** Node stack of the parsed keys */ 359 }; 360 361 /** @} */ 362 363 } // End of namespace Common 364 365 #endif 366