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