From 8caa7d3f8b1146fafc6dff6de4f801eb2e8b61ae Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 24 Jun 2008 19:48:01 +0000 Subject: Common: - Added function to get the active host type as a string. XMLParser: - Added support for ignoring keys while parsing (check documentation). Backwards compatible. - parserError() has been revamped. Shows all kinds of detailed information regarding the error ala Python InterfaceManager/ThemeParser: - DrawData keys and their DrawStep subkeys are now successfully parsed and loaded into structs. That's a win. - Bug fixes. svn-id: r32768 --- common/util.cpp | 29 +++++++++++++ common/util.h | 7 +++ common/xmlparser.cpp | 85 +++++++++++++++++++++++++++++------- common/xmlparser.h | 17 +++++--- gui/InterfaceManager.cpp | 51 +++++++++++++++++++++- gui/InterfaceManager.h | 9 +++- gui/ThemeParser.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++----- gui/ThemeParser.h | 18 ++++++++ 8 files changed, 293 insertions(+), 34 deletions(-) diff --git a/common/util.cpp b/common/util.cpp index 6f0fdcb233..51bd8bcad6 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -481,6 +481,34 @@ uint32 getEnabledSpecialDebugLevels() { return gDebugLevelsEnabled; } +const char *getHostPlatformString() { + +#if defined(__SYMBIAN32__) + return "symbian"; +#elif defined(_WIN32_WCE) || defined(_MSC_VER) || defined(__MINGW32__) || defined(UNIX) + return "pc"; +#elif defined(__PALMOS_TRAPS__) || defined (__PALMOS_ARMLET__) + return "palmos"; +#elif defined(__DC__) + return "dc"; +#elif defined(__GP32__) + return "gp32"; +#elif defined(__PLAYSTATION2__) + return "ps2"; +#elif defined(__PSP__) + return "psp"; +#elif defined(__amigaos4__) + return "amigaos"; +#elif defined (__DS__) //NeilM + return "nds"; +#elif defined(__WII__) + return "wii"; +#else + return ""; +#endif + +} + } // End of namespace Common @@ -694,3 +722,4 @@ Common::String tag2string(uint32 tag) { str[4] = '\0'; return Common::String(str); } + diff --git a/common/util.h b/common/util.h index c23513596c..c4cbf35212 100644 --- a/common/util.h +++ b/common/util.h @@ -323,6 +323,13 @@ const DebugLevelContainer &listSpecialDebugLevels(); uint32 getEnabledSpecialDebugLevels(); +/** + * Return a string containing the name of the currently running host. + * E.g. returns "wii" if ScummVM is being run in a Wii, and so on. + */ +const char *getHostPlatformString(); + + } // End of namespace Common diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index 7728d90d48..de10269cdb 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -36,35 +36,86 @@ using namespace Graphics; void XMLParser::debug_testEval() { static const char *debugConfigText = - "\n" - "\n" + "\n" + "\n" "//\n" "/* lol this is just a simple test*/\n"; _text = strdup(debugConfigText); + _fileName = strdup("test_parse.xml"); Common::String test = "12, 125, 125"; - printf("\n\nRegex result: %s.\n\n", test.regexMatch("^[d]*,[d]*,[d]*$", true) ? "Success." : "Fail"); - parse(); } -void XMLParser::parserError(const char *error_string) { +void XMLParser::parserError(const char *error_string, ...) { _state = kParserError; - printf("PARSER ERROR: %s\n", error_string); + + int pos = _pos; + int line_count = 1; + int line_start = -1; + int line_width = 1; + + do { + if (_text[pos] == '\n' || _text[pos] == '\r') { + line_count++; + + if (line_start == -1) + line_start = pos; + } + } while (pos-- > 0); + + line_start = MAX(line_start, _pos - 80); + + do { + if (_text[line_start + line_width] == '\n' || _text[line_start + line_width] == '\r') + break; + } while (_text[line_start + line_width++]); + + line_width = MIN(line_width, 80); + + char linestr[81]; + strncpy(linestr, &_text[line_start] + 1, line_width ); + linestr[line_width - 1] = 0; + + printf(" File <%s>, line %d:\n", _fileName, line_count); + + printf("%s\n", linestr); + for (int i = 1; i < _pos - line_start; ++i) + printf(" "); + + printf("^\n"); + printf("Parser error: "); + + va_list args; + va_start(args, error_string); + vprintf(error_string, args); + va_end(args); + + printf("\n"); } -void XMLParser::parseActiveKey(bool closed) { - if (keyCallback(_activeKey.top()->name) == false) { - parserError("Unhandled value inside key."); - return; +bool XMLParser::parseActiveKey(bool closed) { + bool ignore = false; + + // check if any of the parents must be ignored. + // if a parent is ignored, all children are too. + for (int i = _activeKey.size() - 1; i >= 0; --i) { + if (_activeKey[i]->ignore) + ignore = true; + } + + if (ignore == false && keyCallback(_activeKey.top()->name) == false) { + return false; } if (closed) { delete _activeKey.pop(); } + + return true; } bool XMLParser::parseKeyValue(Common::String keyName) { @@ -115,7 +166,7 @@ bool XMLParser::parse() { switch (_state) { case kParserNeedKey: if (_text[_pos++] != '<') { - parserError("Expecting key start."); + parserError("Parser expecting key start."); break; } @@ -144,6 +195,7 @@ bool XMLParser::parse() { } else { ParserNode *node = new ParserNode; node->name = _token; + node->ignore = false; _activeKey.push(node); } @@ -166,9 +218,10 @@ bool XMLParser::parse() { selfClosure = (_text[_pos] == '/'); if ((selfClosure && _text[_pos + 1] == '>') || _text[_pos] == '>') { - parseActiveKey(selfClosure); - _pos += selfClosure ? 2 : 1; - _state = kParserNeedKey; + if (parseActiveKey(selfClosure)) { + _pos += selfClosure ? 2 : 1; + _state = kParserNeedKey; + } break; } @@ -181,7 +234,7 @@ bool XMLParser::parse() { case kParserNeedPropertyOperator: if (_text[_pos++] != '=') - parserError("Unexpected character after key name."); + parserError("Syntax error after key name."); else _state = kParserNeedPropertyValue; @@ -189,7 +242,7 @@ bool XMLParser::parse() { case kParserNeedPropertyValue: if (!parseKeyValue(_token)) - parserError("Unable to parse key value."); + parserError("Invalid key value."); else _state = kParserNeedPropertyName; diff --git a/common/xmlparser.h b/common/xmlparser.h index 3edc2b31e1..c7f7218857 100644 --- a/common/xmlparser.h +++ b/common/xmlparser.h @@ -74,6 +74,7 @@ public: struct ParserNode { Common::String name; Common::StringMap values; + bool ignore; }; virtual bool parse(); @@ -103,7 +104,13 @@ protected: * Remember to leave the node stack _UNCHANGED_ in your own function. Removal * of closed keys is done automatically. * - * Return true if the key was properly handled. False otherwise. + * When parsing a key, one may chose to skip it, e.g. because it's not needed + * on the current configuration. In order to ignore a key, you must set + * the "ignore" field of its KeyNode struct to "true": The key and all its children + * will then be automatically ignored by the parser. + * + * Return true if the key was properly handled (this includes the case when the + * key is being ignored). False otherwise. * See the sample implementation in GUI::ThemeParser. */ virtual bool keyCallback(Common::String keyName) { @@ -120,13 +127,12 @@ protected: * node stack and calls the keyCallback. * There's no reason to overload this. */ - virtual void parseActiveKey(bool closed); + virtual bool parseActiveKey(bool closed); /** * Prints an error message when parsing fails and stops the parser. - * TODO: More descriptive error messages. */ - virtual void parserError(const char *errorString); + virtual void parserError(const char *errorString, ...); /** * Skips spaces/whitelines etc. Returns true if any spaces were skipped. @@ -163,7 +169,7 @@ protected: if (_text[_pos] == '/' && _text[_pos + 1] == '/') { _pos += 2; - while (_text[_pos] && _text[_pos] != '\n') + while (_text[_pos] && _text[_pos] != '\n' && _text[_pos] != '\r') _pos++; return true; } @@ -194,6 +200,7 @@ protected: int _pos; /** Current position on the XML buffer. */ char *_text; /** Buffer with the text being parsed */ + char *_fileName; ParserState _state; /** Internal state of the parser */ diff --git a/gui/InterfaceManager.cpp b/gui/InterfaceManager.cpp index dc2250a2bd..5ee3a014ed 100644 --- a/gui/InterfaceManager.cpp +++ b/gui/InterfaceManager.cpp @@ -38,11 +38,44 @@ namespace GUI { using namespace Graphics; +const char *InterfaceManager::kDrawDataStrings[] = { + "mainmenu_bg", + "special_bg", + "plain_bg", + "default_bg", + + "button_idle", + "button_hover", + + "surface", + + "slider_full", + "slider_empty", + + "checkbox_enabled", + "checkbox_disabled", + + "tab", + + "scrollbar_base", + "scrollbar_top", + "scrollbar_bottom", + "scrollbar_handle", + + "popup", + "caret", + "separator" +}; + InterfaceManager::InterfaceManager() : _vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled), _screen(0), _bytesPerPixel(0) { _system = g_system; + for (int i = 0; i < kDrawDataMAX; ++i) { + _widgets[i] = 0; + } + setGraphicsMode(kGfxStandard16bit); } @@ -78,9 +111,25 @@ void InterfaceManager::setGraphicsMode(Graphics_Mode mode) { } void InterfaceManager::addDrawStep(Common::String &drawDataId, Graphics::DrawStep *step) { - _widgets[getDrawDataId(drawDataId)]->_steps.push_back(step); + DrawData id = getDrawDataId(drawDataId); + + assert(_widgets[id] != 0); + _widgets[id]->_steps.push_back(step); } +bool InterfaceManager::addDrawData(DrawData data_id, bool cached) { + assert(data_id >= 0 && data_id < kDrawDataMAX); + + if (_widgets[data_id] != 0) + return false; + + _widgets[data_id] = new WidgetDrawData; + _widgets[data_id]->_cached = cached; + _widgets[data_id]->_type = data_id; + _widgets[data_id]->_scaled = false; + + return true; +} bool InterfaceManager::init() { return false; diff --git a/gui/InterfaceManager.h b/gui/InterfaceManager.h index f5efb105ec..00441a9efc 100644 --- a/gui/InterfaceManager.h +++ b/gui/InterfaceManager.h @@ -47,6 +47,8 @@ class InterfaceManager : public Common::Singleton { friend class Common::Singleton; typedef Common::String String; + static const char *kDrawDataStrings[]; + public: enum Graphics_Mode { kGfxDisabled = 0, @@ -168,10 +170,15 @@ public: void drawLineSeparator(const Common::Rect &r, WidgetStateInfo state = kStateEnabled); DrawData getDrawDataId(Common::String &name) { - return (DrawData)0; + for (int i = 0; i < kDrawDataMAX; ++i) + if (name.compareToIgnoreCase(kDrawDataStrings[i]) == 0) + return (DrawData)i; + + return (DrawData)-1; } void addDrawStep(Common::String &drawDataId, Graphics::DrawStep *step); + bool addDrawData(DrawData data_id, bool cached); protected: template void screenInit(); diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 9f029a874e..afc623c3d8 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -42,6 +42,15 @@ using namespace Common; ThemeParser::ThemeParser() : XMLParser() { _callbacks["drawstep"] = &ThemeParser::parserCallback_DRAWSTEP; _callbacks["drawdata"] = &ThemeParser::parserCallback_DRAWDATA; + + _drawFunctions["circle"] = &Graphics::VectorRenderer::drawCallback_CIRCLE; + _drawFunctions["square"] = &Graphics::VectorRenderer::drawCallback_SQUARE; + _drawFunctions["roundedsq"] = &Graphics::VectorRenderer::drawCallback_ROUNDSQ; + _drawFunctions["bevelsq"] = &Graphics::VectorRenderer::drawCallback_BEVELSQ; + _drawFunctions["line"] = &Graphics::VectorRenderer::drawCallback_LINE; + _drawFunctions["triangle"] = &Graphics::VectorRenderer::drawCallback_TRIANGLE; + _drawFunctions["fill"] = &Graphics::VectorRenderer::drawCallback_FILLSURFACE; + _drawFunctions["void"] = &Graphics::VectorRenderer::drawCallback_VOID; } bool ThemeParser::keyCallback(Common::String keyName) { @@ -73,8 +82,12 @@ Graphics::DrawStep *ThemeParser::newDrawStep() { } bool ThemeParser::parserCallback_DRAWSTEP() { - ParserNode *stepNode = _activeKey.pop(); - ParserNode *drawdataNode = _activeKey.pop(); + ParserNode *stepNode = _activeKey.top(); + + // HACK: Any cleaner way to access the second item from + // the top without popping? Let's keep it this way and hope + // the internal representation doesn't change + ParserNode *drawdataNode = _activeKey[_activeKey.size() - 2]; assert(stepNode->name == "drawstep"); assert(drawdataNode->name == "drawdata"); @@ -85,32 +98,108 @@ bool ThemeParser::parserCallback_DRAWSTEP() { Common::String functionName = stepNode->values["func"]; if (_drawFunctions.contains(functionName) == false) { - parserError("Invalid drawing function in draw step."); - _activeKey.push(drawdataNode); - _activeKey.push(stepNode); + parserError("%s is not a valid drawing function name", functionName.c_str()); return false; } drawstep->drawingCall = _drawFunctions[functionName]; - if (stepNode->values.contains("stroke")) { + uint32 red, green, blue; +/** + * Helper macro to sanitize and assign an integer value from a key + * to the draw step. + * + * @param struct_name Name of the field of a DrawStep struct that must be + * assigned. + * @param key_name Name as STRING of the key identifier as it appears in the + * theme description format. + */ +#define __PARSER_ASSIGN_INT(struct_name, key_name) \ + if (stepNode->values.contains(key_name)) { \ + if (!validateKeyInt(stepNode->values[key_name].c_str())) \ + return false; \ + \ + drawstep->struct_name = atoi(stepNode->values[key_name].c_str()); \ + } + +/** + * Helper macro to sanitize and assign a RGB value from a key to the draw + * step. RGB values have the following syntax: "R, G, B". + * + * TODO: Handle also specific name colors such as "red", "green", etc. + * + * @param struct_name Name of the field of a DrawStep struct that must be + * assigned. + * @param key_name Name as STRING of the key identifier as it appears in the + * theme description format. + */ +#define __PARSER_ASSIGN_RGB(struct_name, key_name) \ + if (stepNode->values.contains(key_name)) { \ + if (sscanf(stepNode->values[key_name].c_str(), "%d, %d, %d", &red, &green, &blue) != 3) \ + return false; \ + \ + drawstep->struct_name.r = red; \ + drawstep->struct_name.g = green; \ + drawstep->struct_name.b = blue; \ } - if (functionName == "roundedsq") { + __PARSER_ASSIGN_INT(stroke, "stroke"); + __PARSER_ASSIGN_INT(shadow, "shadow"); + __PARSER_ASSIGN_INT(factor, "gradient_factor"); + __PARSER_ASSIGN_RGB(fgColor, "fg_color"); + __PARSER_ASSIGN_RGB(bgColor, "bg_color"); + __PARSER_ASSIGN_RGB(gradColor1, "gradient_start"); + __PARSER_ASSIGN_RGB(gradColor2, "gradient_end"); + + if (functionName == "roundedsq" || functionName == "circle") { + __PARSER_ASSIGN_INT(radius, "radius"); } - g_InterfaceManager.addDrawStep(drawdataNode->values["id"], drawstep); + if (functionName == "bevelsq") { + __PARSER_ASSIGN_INT(extraData, "bevel"); + } - _activeKey.push(drawdataNode); - _activeKey.push(stepNode); +#undef __PARSER_ASSIGN_INT +#undef __PARSER_ASSIGN_RGB + g_InterfaceManager.addDrawStep(drawdataNode->values["id"], drawstep); return true; } bool ThemeParser::parserCallback_DRAWDATA() { - printf("Drawdata callback!\n"); + ParserNode *drawdataNode = _activeKey.top(); + bool cached = false; + + if (drawdataNode->values.contains("id") == false) { + parserError("DrawData notes must contain an identifier."); + return false; + } + + InterfaceManager::DrawData id = g_InterfaceManager.getDrawDataId(drawdataNode->values["id"]); + + if (id == -1) { + parserError("%d is not a valid DrawData set identifier.", drawdataNode->values["id"].c_str()); + return false; + } + + if (drawdataNode->values.contains("cached") && drawdataNode->values["cached"] == "true") { + cached = true; + } + + if (drawdataNode->values.contains("platform")) { + if (drawdataNode->values["platform"].compareToIgnoreCase(Common::getHostPlatformString()) != 0) { + drawdataNode->ignore = true; + return true; + } + } + + if (g_InterfaceManager.addDrawData(id, cached) == false) { + parserError("Repeated DrawData: Only one set of Drawing Data for a widget may be specified on each platform."); + return false; + } + return true; } diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h index 108a7263ba..4103098a63 100644 --- a/gui/ThemeParser.h +++ b/gui/ThemeParser.h @@ -321,6 +321,24 @@ protected: bool parserCallback_DRAWSTEP(); bool parserCallback_DRAWDATA(); + bool validateKeyIntSigned(const char *key) { + if (!isdigit(*key) && *key != '+' && *key != '-') + return false; + + return validateKeyInt(key + 1); + } + + bool validateKeyInt(const char *key) { + if (*key == 0) + return false; + + while (*key) + if (!isdigit(*key++)) + return false; + + return true; + } + Graphics::DrawStep *newDrawStep(); Common::HashMap _drawFunctions; -- cgit v1.2.3