- 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
This commit is contained in:
Vicent Marti 2008-06-24 19:48:01 +00:00
parent a4b4534a66
commit 8caa7d3f8b
8 changed files with 295 additions and 36 deletions

View file

@ -481,6 +481,34 @@ uint32 getEnabledSpecialDebugLevels() {
return gDebugLevelsEnabled; 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 } // End of namespace Common
@ -694,3 +722,4 @@ Common::String tag2string(uint32 tag) {
str[4] = '\0'; str[4] = '\0';
return Common::String(str); return Common::String(str);
} }

View file

@ -323,6 +323,13 @@ const DebugLevelContainer &listSpecialDebugLevels();
uint32 getEnabledSpecialDebugLevels(); 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 } // End of namespace Common

View file

@ -36,35 +36,86 @@ using namespace Graphics;
void XMLParser::debug_testEval() { void XMLParser::debug_testEval() {
static const char *debugConfigText = static const char *debugConfigText =
"</* lol this is just a moronic test */drawdata id = \"background_default\" cache = true>\n" "</* lol this is just a moronic test */drawdata id = \"mainmenu_bg\" cache = true>\n"
"<drawstep func = \"roundedsq\" fill = \"gradient\" gradient_start = \"255, 255, 128\" gradient_end = \"128, 128, 128\" size = \"auto\"/>\n" "<drawstep| func = \"roundedsq\" fill = \"gradient\" gradient_start = \"255, 255, 128\" gradient_end = \"128, 128, 128\" size = \"auto\"/>\n"
"//<drawstep func = \"roundedsq\" fill = \"none\" color = /*\"0, 0, 0\"*/\"0, 1, 2\" size = \"auto\"/>\n" "//<drawstep func = \"roundedsq\" fill = \"none\" color = /*\"0, 0, 0\"*/\"0, 1, 2\" size = \"auto\"/>\n"
"</ drawdata>/* lol this is just a simple test*/\n"; "</ drawdata>/* lol this is just a simple test*/\n";
_text = strdup(debugConfigText); _text = strdup(debugConfigText);
_fileName = strdup("test_parse.xml");
Common::String test = "12, 125, 125"; Common::String test = "12, 125, 125";
printf("\n\nRegex result: %s.\n\n", test.regexMatch("^[d]*,[d]*,[d]*$", true) ? "Success." : "Fail");
parse(); parse();
} }
void XMLParser::parserError(const char *error_string) { void XMLParser::parserError(const char *error_string, ...) {
_state = kParserError; _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) { bool XMLParser::parseActiveKey(bool closed) {
if (keyCallback(_activeKey.top()->name) == false) { bool ignore = false;
parserError("Unhandled value inside key.");
return; // 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) { if (closed) {
delete _activeKey.pop(); delete _activeKey.pop();
} }
return true;
} }
bool XMLParser::parseKeyValue(Common::String keyName) { bool XMLParser::parseKeyValue(Common::String keyName) {
@ -115,7 +166,7 @@ bool XMLParser::parse() {
switch (_state) { switch (_state) {
case kParserNeedKey: case kParserNeedKey:
if (_text[_pos++] != '<') { if (_text[_pos++] != '<') {
parserError("Expecting key start."); parserError("Parser expecting key start.");
break; break;
} }
@ -144,6 +195,7 @@ bool XMLParser::parse() {
} else { } else {
ParserNode *node = new ParserNode; ParserNode *node = new ParserNode;
node->name = _token; node->name = _token;
node->ignore = false;
_activeKey.push(node); _activeKey.push(node);
} }
@ -166,9 +218,10 @@ bool XMLParser::parse() {
selfClosure = (_text[_pos] == '/'); selfClosure = (_text[_pos] == '/');
if ((selfClosure && _text[_pos + 1] == '>') || _text[_pos] == '>') { if ((selfClosure && _text[_pos + 1] == '>') || _text[_pos] == '>') {
parseActiveKey(selfClosure); if (parseActiveKey(selfClosure)) {
_pos += selfClosure ? 2 : 1; _pos += selfClosure ? 2 : 1;
_state = kParserNeedKey; _state = kParserNeedKey;
}
break; break;
} }
@ -181,7 +234,7 @@ bool XMLParser::parse() {
case kParserNeedPropertyOperator: case kParserNeedPropertyOperator:
if (_text[_pos++] != '=') if (_text[_pos++] != '=')
parserError("Unexpected character after key name."); parserError("Syntax error after key name.");
else else
_state = kParserNeedPropertyValue; _state = kParserNeedPropertyValue;
@ -189,7 +242,7 @@ bool XMLParser::parse() {
case kParserNeedPropertyValue: case kParserNeedPropertyValue:
if (!parseKeyValue(_token)) if (!parseKeyValue(_token))
parserError("Unable to parse key value."); parserError("Invalid key value.");
else else
_state = kParserNeedPropertyName; _state = kParserNeedPropertyName;

View file

@ -74,6 +74,7 @@ public:
struct ParserNode { struct ParserNode {
Common::String name; Common::String name;
Common::StringMap values; Common::StringMap values;
bool ignore;
}; };
virtual bool parse(); virtual bool parse();
@ -103,7 +104,13 @@ protected:
* Remember to leave the node stack _UNCHANGED_ in your own function. Removal * Remember to leave the node stack _UNCHANGED_ in your own function. Removal
* of closed keys is done automatically. * 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. * See the sample implementation in GUI::ThemeParser.
*/ */
virtual bool keyCallback(Common::String keyName) { virtual bool keyCallback(Common::String keyName) {
@ -120,13 +127,12 @@ protected:
* node stack and calls the keyCallback. * node stack and calls the keyCallback.
* There's no reason to overload this. * 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. * 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. * Skips spaces/whitelines etc. Returns true if any spaces were skipped.
@ -163,7 +169,7 @@ protected:
if (_text[_pos] == '/' && _text[_pos + 1] == '/') { if (_text[_pos] == '/' && _text[_pos + 1] == '/') {
_pos += 2; _pos += 2;
while (_text[_pos] && _text[_pos] != '\n') while (_text[_pos] && _text[_pos] != '\n' && _text[_pos] != '\r')
_pos++; _pos++;
return true; return true;
} }
@ -194,6 +200,7 @@ protected:
int _pos; /** Current position on the XML buffer. */ int _pos; /** Current position on the XML buffer. */
char *_text; /** Buffer with the text being parsed */ char *_text; /** Buffer with the text being parsed */
char *_fileName;
ParserState _state; /** Internal state of the parser */ ParserState _state; /** Internal state of the parser */

View file

@ -38,11 +38,44 @@ namespace GUI {
using namespace Graphics; 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() : InterfaceManager::InterfaceManager() :
_vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled), _vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled),
_screen(0), _bytesPerPixel(0) { _screen(0), _bytesPerPixel(0) {
_system = g_system; _system = g_system;
for (int i = 0; i < kDrawDataMAX; ++i) {
_widgets[i] = 0;
}
setGraphicsMode(kGfxStandard16bit); setGraphicsMode(kGfxStandard16bit);
} }
@ -78,9 +111,25 @@ void InterfaceManager::setGraphicsMode(Graphics_Mode mode) {
} }
void InterfaceManager::addDrawStep(Common::String &drawDataId, Graphics::DrawStep *step) { 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() { bool InterfaceManager::init() {
return false; return false;

View file

@ -47,6 +47,8 @@ class InterfaceManager : public Common::Singleton<InterfaceManager> {
friend class Common::Singleton<SingletonBaseType>; friend class Common::Singleton<SingletonBaseType>;
typedef Common::String String; typedef Common::String String;
static const char *kDrawDataStrings[];
public: public:
enum Graphics_Mode { enum Graphics_Mode {
kGfxDisabled = 0, kGfxDisabled = 0,
@ -168,10 +170,15 @@ public:
void drawLineSeparator(const Common::Rect &r, WidgetStateInfo state = kStateEnabled); void drawLineSeparator(const Common::Rect &r, WidgetStateInfo state = kStateEnabled);
DrawData getDrawDataId(Common::String &name) { 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); void addDrawStep(Common::String &drawDataId, Graphics::DrawStep *step);
bool addDrawData(DrawData data_id, bool cached);
protected: protected:
template<typename PixelType> void screenInit(); template<typename PixelType> void screenInit();

View file

@ -42,6 +42,15 @@ using namespace Common;
ThemeParser::ThemeParser() : XMLParser() { ThemeParser::ThemeParser() : XMLParser() {
_callbacks["drawstep"] = &ThemeParser::parserCallback_DRAWSTEP; _callbacks["drawstep"] = &ThemeParser::parserCallback_DRAWSTEP;
_callbacks["drawdata"] = &ThemeParser::parserCallback_DRAWDATA; _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) { bool ThemeParser::keyCallback(Common::String keyName) {
@ -73,8 +82,12 @@ Graphics::DrawStep *ThemeParser::newDrawStep() {
} }
bool ThemeParser::parserCallback_DRAWSTEP() { bool ThemeParser::parserCallback_DRAWSTEP() {
ParserNode *stepNode = _activeKey.pop(); ParserNode *stepNode = _activeKey.top();
ParserNode *drawdataNode = _activeKey.pop();
// 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(stepNode->name == "drawstep");
assert(drawdataNode->name == "drawdata"); assert(drawdataNode->name == "drawdata");
@ -85,32 +98,108 @@ bool ThemeParser::parserCallback_DRAWSTEP() {
Common::String functionName = stepNode->values["func"]; Common::String functionName = stepNode->values["func"];
if (_drawFunctions.contains(functionName) == false) { if (_drawFunctions.contains(functionName) == false) {
parserError("Invalid drawing function in draw step."); parserError("%s is not a valid drawing function name", functionName.c_str());
_activeKey.push(drawdataNode);
_activeKey.push(stepNode);
return false; return false;
} }
drawstep->drawingCall = _drawFunctions[functionName]; 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()); \
} }
if (functionName == "roundedsq") { /**
* 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; \
} }
__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");
}
if (functionName == "bevelsq") {
__PARSER_ASSIGN_INT(extraData, "bevel");
}
#undef __PARSER_ASSIGN_INT
#undef __PARSER_ASSIGN_RGB
g_InterfaceManager.addDrawStep(drawdataNode->values["id"], drawstep); g_InterfaceManager.addDrawStep(drawdataNode->values["id"], drawstep);
_activeKey.push(drawdataNode);
_activeKey.push(stepNode);
return true; return true;
} }
bool ThemeParser::parserCallback_DRAWDATA() { 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; return true;
} }

View file

@ -321,6 +321,24 @@ protected:
bool parserCallback_DRAWSTEP(); bool parserCallback_DRAWSTEP();
bool parserCallback_DRAWDATA(); 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(); Graphics::DrawStep *newDrawStep();
Common::HashMap<Common::String, DrawingFunctionCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _drawFunctions; Common::HashMap<Common::String, DrawingFunctionCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _drawFunctions;