From 80e42b0f967a5753156447e0d3129bcbe934d23e Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 21 May 2011 15:15:46 -0300 Subject: [PATCH 1/2] replace YAML with custom made library for reading text files named FML --- CMakeLists.txt | 4 +- src/framework/core/configs.cpp | 29 +- src/framework/core/configs.h | 2 +- src/framework/graphics/font.cpp | 34 +-- src/framework/platform/win32platform.cpp | 3 + src/framework/platform/x11platform.cpp | 3 + src/framework/prerequisites.h | 2 +- src/framework/script/luafunctions.cpp | 2 +- src/framework/ui/uibuttonskin.cpp | 14 +- src/framework/ui/uibuttonskin.h | 2 +- src/framework/ui/uielementskin.cpp | 47 ++- src/framework/ui/uielementskin.h | 8 +- src/framework/ui/uilabelskin.cpp | 2 +- src/framework/ui/uilabelskin.h | 2 +- src/framework/ui/uiloader.cpp | 164 +++++------ src/framework/ui/uiloader.h | 22 +- src/framework/ui/uiskins.cpp | 91 ++---- src/framework/ui/uitexteditskin.cpp | 4 +- src/framework/ui/uitexteditskin.h | 2 +- src/framework/ui/uiwindowskin.cpp | 12 +- src/framework/ui/uiwindowskin.h | 2 +- src/framework/util/color.h | 10 + src/framework/util/fml.cpp | 359 +++++++++++++++++++++++ src/framework/util/fml.h | 217 ++++++++++++++ src/framework/util/point.h | 7 + src/framework/util/rect.h | 11 + src/framework/util/size.h | 9 +- src/framework/util/yaml.h | 125 -------- src/main.cpp | 51 ++-- 29 files changed, 827 insertions(+), 413 deletions(-) create mode 100644 src/framework/util/fml.cpp create mode 100644 src/framework/util/fml.h delete mode 100644 src/framework/util/yaml.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d916483..3a98e782 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,6 @@ SET(Boost_USE_MULTITHREADED OFF) FIND_PACKAGE(Boost COMPONENTS system signals REQUIRED) FIND_PACKAGE(OpenGL REQUIRED) FIND_PACKAGE(Lua REQUIRED) -FIND_PACKAGE(YamlCpp REQUIRED) FIND_PACKAGE(PhysFS REQUIRED) FIND_PACKAGE(GMP REQUIRED) FIND_PACKAGE(ZLIB REQUIRED) @@ -33,7 +32,6 @@ INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR} ${LUA_INCLUDE_DIR} - ${YAMLCPP_INCLUDE_DIR} ${PHYSFS_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} @@ -71,6 +69,7 @@ SET(SOURCES src/framework/util/logger.cpp src/framework/util/rsa.cpp src/framework/util/apngloader.cpp + src/framework/util/fml.cpp # framework graphics src/framework/graphics/image.cpp @@ -129,7 +128,6 @@ TARGET_LINK_LIBRARIES(otclient ${Boost_LIBRARIES} ${OPENGL_LIBRARIES} ${LUA_LIBRARIES} - ${YAMLCPP_LIBRARY} ${PHYSFS_LIBRARY} ${GMP_LIBRARY} ${ZLIB_LIBRARY} diff --git a/src/framework/core/configs.cpp b/src/framework/core/configs.cpp index 4fcc923f..142d1351 100644 --- a/src/framework/core/configs.cpp +++ b/src/framework/core/configs.cpp @@ -36,27 +36,28 @@ bool Configs::load(const std::string& fileName) if(!g_resources.loadFile(fileName, fin)) return false; - try { - YAML::Node doc; - YAML::Parser parser(fin); - parser.GetNextDocument(doc); - - for(auto it = doc.begin(); it != doc.end(); ++it) - m_confsMap[yamlRead(it.first())] = yamlRead(it.second()); - - return true; - } catch (YAML::Exception& e) { - flogError("ERROR: Malformed config file: %s", e.what()); + FML::Parser parser; + if(parser.load(fin)) { + FML::Node* doc = parser.getDocument(); + + foreach(FML::Node* node, *doc) + m_confsMap[node->tag()] = node->value(); + } else { + flogError("ERROR: Malformed config file: %s", parser.getErrorMessage()); return false; } + + return true; } void Configs::save() { if(!m_fileName.empty()) { - YAML::Emitter out; - out << m_confsMap; - g_resources.saveFile(m_fileName, (const uchar*)out.c_str(), out.size()); + std::stringstream out; + foreach(auto pair, m_confsMap) { + out << pair.first << ": " << pair.second << std::endl; + } + g_resources.saveFile(m_fileName, out); } } diff --git a/src/framework/core/configs.h b/src/framework/core/configs.h index 846ee72f..8e677a7f 100644 --- a/src/framework/core/configs.h +++ b/src/framework/core/configs.h @@ -41,7 +41,7 @@ public: bool load(const std::string& fileName); void save(); - template void setValue(const std::string& key, const T& value) { m_confsMap[key] = convert_cast(value); } + template void set(const std::string& key, const T& value) { m_confsMap[key] = convert_cast(value); } ConfigValueProxy get(const std::string& key) { return ConfigValueProxy{m_confsMap[key]}; } private: diff --git a/src/framework/graphics/font.cpp b/src/framework/graphics/font.cpp index 367fdc69..335c1089 100644 --- a/src/framework/graphics/font.cpp +++ b/src/framework/graphics/font.cpp @@ -77,20 +77,17 @@ bool Font::load(const std::string& file) std::string textureName; Size glyphSize; - try { - YAML::Parser parser(fin); - - YAML::Node doc; - parser.GetNextDocument(doc); + FML::Parser parser(fin); + if(!parser.hasError()) { + FML::Node* doc = parser.getDocument(); // required values - doc["glyph height"] >> m_glyphHeight; - doc["image glyph size"] >> glyphSize; - textureName = yamlRead(doc, "image"); - m_glyphHeight = yamlRead(doc, "glyph height"); - m_firstGlyph = yamlRead(doc, "first glyph", 32); - m_topMargin = yamlRead(doc, "top margin", 0); - m_glyphSpacing = yamlRead(doc, "glyph spacing", Size(0,0)); + textureName = doc->valueAt("image"); + glyphSize = doc->readAt("image glyph size", Size(16, 16)); + m_glyphHeight = doc->readAt("glyph height", 11); + m_firstGlyph = doc->readAt("first glyph", 32); + m_topMargin = doc->readAt("top margin", 0); + m_glyphSpacing = doc->readAt("glyph spacing", Size(0,0)); // load texture m_texture = g_textures.get(textureName); @@ -103,9 +100,12 @@ bool Font::load(const std::string& file) calculateGlyphsWidthsAutomatically(glyphSize); // read custom widths - std::map glyphWidths = yamlReadMap(doc, "glyph widths"); - foreach(const auto& pair, glyphWidths) - m_glyphsSize[pair.first].setWidth(pair.second); + if(doc->hasNode("glyph widths")) { + std::map glyphWidths; + (*(doc->at("glyph widths"))) >> glyphWidths; + foreach(const auto& pair, glyphWidths) + m_glyphsSize[pair.first].setWidth(pair.second); + } // calculate glyphs texture coords int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width(); @@ -115,8 +115,8 @@ bool Font::load(const std::string& file) m_glyphsSize[glyph].width(), m_glyphHeight); } - } catch (YAML::Exception& e) { - flogError("ERROR: Malformed font file \"%s\":\n %s", file.c_str() % e.what()); + } else { + flogError("ERROR: Malformed font file \"%s\":\n %s", file.c_str() % parser.getErrorMessage()); return false; } diff --git a/src/framework/platform/win32platform.cpp b/src/framework/platform/win32platform.cpp index 3d51783e..751d0cf5 100644 --- a/src/framework/platform/win32platform.cpp +++ b/src/framework/platform/win32platform.cpp @@ -50,6 +50,9 @@ struct Win32PlatformPrivate { void Platform::init(const char *appName) { + // seend random numbers + std::srand(std::time(NULL)); + win32.appName = appName; win32.instance = GetModuleHandle(NULL); diff --git a/src/framework/platform/x11platform.cpp b/src/framework/platform/x11platform.cpp index 2fc8590a..50a7dfba 100644 --- a/src/framework/platform/x11platform.cpp +++ b/src/framework/platform/x11platform.cpp @@ -69,6 +69,9 @@ struct X11PlatformPrivate { void Platform::init(const char *appName) { + // seend random numbers + std::srand(std::time(NULL)); + x11.appName = appName; x11.display = NULL; x11.visual = NULL; diff --git a/src/framework/prerequisites.h b/src/framework/prerequisites.h index d23fdbc7..ed66062a 100644 --- a/src/framework/prerequisites.h +++ b/src/framework/prerequisites.h @@ -85,11 +85,11 @@ typedef boost::function SimpleCallback; // common utilities #include +#include #include #include #include #include #include -#include #endif // PREREQUISITES_H diff --git a/src/framework/script/luafunctions.cpp b/src/framework/script/luafunctions.cpp index e67b6d7d..f666d25c 100644 --- a/src/framework/script/luafunctions.cpp +++ b/src/framework/script/luafunctions.cpp @@ -98,7 +98,7 @@ int lua_UI_load() UIElementPtr element; if(parent) - element = g_uiLoader.loadFromYAML(uiFile.c_str(), parent); + element = g_uiLoader.loadFromFile(uiFile.c_str(), parent); else g_lua.reportErrorWithTraceback("invalid parent container"); diff --git a/src/framework/ui/uibuttonskin.cpp b/src/framework/ui/uibuttonskin.cpp index 8b6976f5..02927dcd 100644 --- a/src/framework/ui/uibuttonskin.cpp +++ b/src/framework/ui/uibuttonskin.cpp @@ -27,24 +27,22 @@ #include #include -void UIButtonSkin::load(const YAML::Node& node) +void UIButtonSkin::load(FML::Node* node) { UIElementSkin::load(node); m_buttonDownTextColor = getFontColor(); m_buttonHoverTextColor = getFontColor(); - if(yamlHasValue(node, "down state")) { - const YAML::Node& cnode = node["down state"]; + if(FML::Node* cnode = node->at("down state")) { m_buttonDownImage = loadImage(cnode); - m_buttonDownTranslate = yamlRead(cnode, "text translate", Point()); - m_buttonDownTextColor = yamlRead(cnode, "font color", getFontColor()); + m_buttonDownTranslate = cnode->readAt("text translate", Point()); + m_buttonDownTextColor = cnode->readAt("font color", getFontColor()); } - if(yamlHasValue(node, "hover state")) { - const YAML::Node& cnode = node["hover state"]; + if(FML::Node* cnode = node->at("hover state")) { m_buttonHoverImage = loadImage(cnode); - m_buttonHoverTextColor = yamlRead(cnode, "font color", getFontColor()); + m_buttonHoverTextColor = cnode->readAt("font color", getFontColor()); } } diff --git a/src/framework/ui/uibuttonskin.h b/src/framework/ui/uibuttonskin.h index 06933795..e03e0332 100644 --- a/src/framework/ui/uibuttonskin.h +++ b/src/framework/ui/uibuttonskin.h @@ -37,7 +37,7 @@ public: UIButtonSkin(const std::string& name) : UIElementSkin(name, UI::Button) { } - void load(const YAML::Node& node); + void load(FML::Node* node); void draw(UIElement *element); private: diff --git a/src/framework/ui/uielementskin.cpp b/src/framework/ui/uielementskin.cpp index dbca54b2..b3ed552b 100644 --- a/src/framework/ui/uielementskin.cpp +++ b/src/framework/ui/uielementskin.cpp @@ -30,12 +30,12 @@ #include #include -void UIElementSkin::load(const YAML::Node& node) +void UIElementSkin::load(FML::Node* node) { - m_defaultSize = yamlRead(node, "default size", Size()); + m_defaultSize = node->readAt("default size", Size()); m_defaultImage = loadImage(node); m_font = loadFont(node); - m_fontColor = yamlRead(node, "font color", g_uiSkins.getDefaultFontColor()); + m_fontColor = node->readAt("font color", g_uiSkins.getDefaultFontColor()); } void UIElementSkin::apply(UIElement* element) @@ -50,23 +50,22 @@ void UIElementSkin::draw(UIElement *element) m_defaultImage->draw(element->getRect()); } -ImagePtr UIElementSkin::loadImage(const YAML::Node& node) +ImagePtr UIElementSkin::loadImage(FML::Node* node) { ImagePtr image; TexturePtr texture; - if(yamlHasValue(node, "bordered image")) { - const YAML::Node& cnode = node["bordered image"]; - Rect left = yamlRead(cnode, "left border", Rect()); - Rect right = yamlRead(cnode, "right border", Rect()); - Rect top = yamlRead(cnode, "top border", Rect()); - Rect bottom = yamlRead(cnode, "bottom border", Rect()); - Rect topLeft = yamlRead(cnode, "top left corner", Rect()); - Rect topRight = yamlRead(cnode, "top right corner", Rect()); - Rect bottomLeft = yamlRead(cnode, "bottom left corner", Rect()); - Rect bottomRight = yamlRead(cnode, "bottom right corner", Rect()); - Rect center = yamlRead(cnode, "center", Rect()); - std::string textureName = yamlRead(cnode, "source", std::string()); + if(FML::Node* cnode = node->at("bordered image")) { + Rect left = cnode->readAt("left border", Rect()); + Rect right = cnode->readAt("right border", Rect()); + Rect top = cnode->readAt("top border", Rect()); + Rect bottom = cnode->readAt("bottom border", Rect()); + Rect topLeft = cnode->readAt("top left corner", Rect()); + Rect topRight = cnode->readAt("top right corner", Rect()); + Rect bottomLeft = cnode->readAt("bottom left corner", Rect()); + Rect bottomRight = cnode->readAt("bottom right corner", Rect()); + Rect center = cnode->readAt("center", Rect()); + std::string textureName = cnode->readAt("source", std::string()); if(!textureName.empty()) texture = g_textures.get(textureName); @@ -87,20 +86,20 @@ ImagePtr UIElementSkin::loadImage(const YAML::Node& node) } if(!image) - logError(yamlErrorDesc(cnode, "failed to load bordered image")); - } else if(yamlHasValue(node, "image")) { - texture = g_textures.get(yamlRead(node, "image")); + logError(node->generateErrorMessage("failed to load bordered image")); + } else if(FML::Node* cnode = node->at("image")) { + texture = g_textures.get(cnode->value()); if(texture) image = ImagePtr(new Image(texture)); if(!m_defaultSize.isValid()) m_defaultSize = texture->getSize(); if(!image) - logError(yamlErrorDesc(node["image"], "failed to load image")); + logError(cnode->generateErrorMessage("failed to load image")); } if(texture) { - bool antialised = yamlRead(node, "antialised", false); + bool antialised = node->readAt("antialised", false); if(antialised) texture->enableBilinearFilter(); } @@ -108,11 +107,11 @@ ImagePtr UIElementSkin::loadImage(const YAML::Node& node) return image; } -FontPtr UIElementSkin::loadFont(const YAML::Node& node) +FontPtr UIElementSkin::loadFont(FML::Node* node) { FontPtr font; - if(yamlHasValue(node, "font")) - font = g_fonts.get(yamlRead(node, "font")); + if(FML::Node* cnode = node->at("font")) + font = g_fonts.get(cnode->value()); if(!font) font = g_uiSkins.getDefaultFont(); return font; diff --git a/src/framework/ui/uielementskin.h b/src/framework/ui/uielementskin.h index 2b50209c..87b9e1b6 100644 --- a/src/framework/ui/uielementskin.h +++ b/src/framework/ui/uielementskin.h @@ -41,8 +41,8 @@ public: UIElementSkin() : m_elementType(UI::Element) { } virtual ~UIElementSkin() { } - /// Load the skin from a YAML node - virtual void load(const YAML::Node& node); + /// Load the skin from a FML node + virtual void load(FML::Node* node); /// Apply the skin to an element virtual void apply(UIElement *element); /// Draw the skinned element @@ -56,8 +56,8 @@ public: Color getFontColor() const { return m_fontColor; } protected: - ImagePtr loadImage(const YAML::Node& node); - FontPtr loadFont(const YAML::Node &node); + ImagePtr loadImage(FML::Node* node); + FontPtr loadFont(FML::Node* node); private: std::string m_name; diff --git a/src/framework/ui/uilabelskin.cpp b/src/framework/ui/uilabelskin.cpp index 84a713a7..398df3cb 100644 --- a/src/framework/ui/uilabelskin.cpp +++ b/src/framework/ui/uilabelskin.cpp @@ -27,7 +27,7 @@ #include #include -void UILabelSkin::load(const YAML::Node& node) +void UILabelSkin::load(FML::Node* node) { UIElementSkin::load(node); } diff --git a/src/framework/ui/uilabelskin.h b/src/framework/ui/uilabelskin.h index 0cce4a62..8a6400e9 100644 --- a/src/framework/ui/uilabelskin.h +++ b/src/framework/ui/uilabelskin.h @@ -35,7 +35,7 @@ public: UILabelSkin(const std::string& name) : UIElementSkin(name, UI::Label) { } - void load(const YAML::Node& node); + void load(FML::Node* node); void apply(UIElement *element); void draw(UIElement *element); }; diff --git a/src/framework/ui/uiloader.cpp b/src/framework/ui/uiloader.cpp index 8bad19d4..35a67387 100644 --- a/src/framework/ui/uiloader.cpp +++ b/src/framework/ui/uiloader.cpp @@ -67,93 +67,81 @@ UIElementPtr UILoader::createElementFromId(const std::string& id) return element; } -UIElementPtr UILoader::loadFromYAML(std::string filePath, const UIContainerPtr& parent) +UIElementPtr UILoader::loadFromFile(std::string filePath, const UIContainerPtr& parent) { std::stringstream fin; if(!g_resources.loadFile(filePath, fin)) { - flogError("ERROR: Could not load ui file \"%s", filePath.c_str()); + flogError("ERROR: Could not load ui file \"%s", filePath.c_str()); return UIElementPtr(); } m_currentFile = filePath; - try { - YAML::Parser parser(fin); - - YAML::Node doc; - parser.GetNextDocument(doc); + FML::Parser parser(fin); + if(!parser.hasError()) { + FML::Node* doc = parser.getDocument(); // get element id - std::string elementId; - doc.begin().first() >> elementId; + std::string elementId = doc->front()->tag(); // first we should populate all elements // only after that we can load anchors // create element interpreting it's id UIElementPtr element = createElementFromId(elementId); - if(!element) - yamlThrowError(doc.begin().first(), "invalid element type"); + if(!element) { + logError(doc->front()->generateErrorMessage("invalid root element type")); + return element; + } parent->addChild(element); // populete it if(element->asUIContainer()) - populateContainer(element->asUIContainer(), doc.begin().second()); + populateContainer(element->asUIContainer(), doc->front()); // now do the real load - loadElements(element, doc.begin().second()); + loadElements(element, doc->front()); // report onLoad events element->onLoad(); return element; - } catch (YAML::Exception& e) { - flogError("ERROR: Failed to load ui file \"%s\":\n %s", filePath.c_str() % e.what()); + } else { + flogError("ERROR: Failed to load ui file \"%s\":\n %s", filePath.c_str() % parser.getErrorMessage()); } return UIElementPtr(); } -void UILoader::populateContainer(const UIContainerPtr& parent, const YAML::Node& node) +void UILoader::populateContainer(const UIContainerPtr& parent, FML::Node* node) { - // order nodes - std::map orderedNodes; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string id; - it.first() >> id; - - // check if it's an element id - if(id.find("#") != std::string::npos) - orderedNodes[it.first().GetMark().pos] = id; - } - // populate ordered elements - foreach(auto pair, orderedNodes) { - std::string id = pair.second; - const YAML::Node& cnode = node[id]; + foreach(FML::Node* cnode, *node) { + std::string id = cnode->tag(); + if(id.find("#") != std::string::npos) { + UIElementPtr element = createElementFromId(id); + if(!element) { + logError(cnode->generateErrorMessage("invalid element type")); + continue; + } + parent->addChild(element); - UIElementPtr element = createElementFromId(id); - if(!element) { - logError(yamlErrorDesc(cnode, "invalid element type")); - continue; + // also populate this element if it's a parent + if(element->asUIContainer()) + populateContainer(element->asUIContainer(), cnode); } - parent->addChild(element); - - // also populate this element if it's a parent - if(element->asUIContainer()) - populateContainer(element->asUIContainer(), cnode); } } -void UILoader::loadElements(const UIElementPtr& parent, const YAML::Node& node) +void UILoader::loadElements(const UIElementPtr& parent, FML::Node* node) { loadElement(parent, node); if(UIContainerPtr container = parent->asUIContainer()) { foreach(const UIElementPtr& element, container->getChildren()) { - for(auto it = node.begin(); it != node.end(); ++it) { + foreach(FML::Node* cnode, *node) { // node found, load it - if(boost::ends_with(yamlRead(it.first()), "#" + element->getId())) { - loadElements(element, it.second()); + if(boost::ends_with(cnode->tag(), "#" + element->getId())) { + loadElements(element, cnode); break; } } @@ -161,13 +149,12 @@ void UILoader::loadElements(const UIElementPtr& parent, const YAML::Node& node) } } -void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node) +void UILoader::loadElement(const UIElementPtr& element, FML::Node* node) { // set element skin - if(yamlHasValue(node, "skin")) { - const YAML::Node& cnode = node["skin"]; - if(cnode.Type() == YAML::NodeType::Scalar) { - element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), yamlRead(cnode))); + if(FML::Node* cnode = node->at("skin")) { + if(cnode->hasValue()) { + element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), cnode->value())); } else { UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin()); skin->load(cnode); @@ -177,46 +164,45 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node) element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), "default")); // load elements common proprieties - if(yamlHasValue(node, "size")) - element->setSize(yamlRead(node, "size")); - element->setMarginLeft(yamlRead(node, "margin.left", 0)); - element->setMarginRight(yamlRead(node, "margin.right", 0)); - element->setMarginTop(yamlRead(node, "margin.top", 0)); - element->setMarginBottom(yamlRead(node, "margin.bottom", 0)); + if(node->hasNode("size")) + element->setSize(node->readAt("size")); + + element->setMarginLeft(node->readAt("margin.left", 0)); + element->setMarginRight(node->readAt("margin.right", 0)); + element->setMarginTop(node->readAt("margin.top", 0)); + element->setMarginBottom(node->readAt("margin.bottom", 0)); - if(yamlHasValue(node, "anchors.left")) - loadElementAnchor(element, UI::AnchorLeft, node["anchors.left"]); + if(node->hasNode("anchors.left")) + loadElementAnchor(element, UI::AnchorLeft, node->at("anchors.left")); - if(yamlHasValue(node, "anchors.right")) - loadElementAnchor(element, UI::AnchorRight, node["anchors.right"]); + if(node->hasNode("anchors.right")) + loadElementAnchor(element, UI::AnchorRight, node->at("anchors.right")); - if(yamlHasValue(node, "anchors.top")) - loadElementAnchor(element, UI::AnchorTop, node["anchors.top"]); + if(node->hasNode("anchors.top")) + loadElementAnchor(element, UI::AnchorTop, node->at("anchors.top")); - if(yamlHasValue(node, "anchors.bottom")) - loadElementAnchor(element, UI::AnchorBottom, node["anchors.bottom"]); + if(node->hasNode("anchors.bottom")) + loadElementAnchor(element, UI::AnchorBottom, node->at("anchors.bottom")); - if(yamlHasValue(node, "anchors.horizontalCenter")) - loadElementAnchor(element, UI::AnchorHorizontalCenter, node["anchors.horizontalCenter"]); + if(node->hasNode("anchors.horizontalCenter")) + loadElementAnchor(element, UI::AnchorHorizontalCenter, node->at("anchors.horizontalCenter")); - if(yamlHasValue(node, "anchors.verticalCenter")) - loadElementAnchor(element, UI::AnchorVerticalCenter, node["anchors.verticalCenter"]); + if(node->hasNode("anchors.verticalCenter")) + loadElementAnchor(element, UI::AnchorVerticalCenter, node->at("anchors.verticalCenter")); // load events - if(yamlHasValue(node, "onLoad")) { - const YAML::Node& cnode = node["onLoad"]; - if(g_lua.loadBufferAsFunction(yamlRead(cnode), getElementSourceDesc(element, "onLoad"))) + if(FML::Node* cnode = node->at("onLoad")) { + if(g_lua.loadBufferAsFunction(cnode->value(), getElementSourceDesc(element, "onLoad"))) g_lua.setScriptableField(element, "onLoad"); else - logError(yamlErrorDesc(cnode, "failed to parse inline lua script")); + logError(cnode->generateErrorMessage("failed to parse inline lua script")); } - if(yamlHasValue(node, "onDestroy")) { - const YAML::Node& cnode = node["onDestroy"]; - if(g_lua.loadBufferAsFunction(yamlRead(cnode), getElementSourceDesc(element, "onDestroy"))) + if(FML::Node* cnode = node->at("onDestroy")) { + if(g_lua.loadBufferAsFunction(cnode->value(), getElementSourceDesc(element, "onDestroy"))) g_lua.setScriptableField(element, "onDestroy"); else - logError(yamlErrorDesc(cnode, "failed to parse inline lua script")); + logError(cnode->generateErrorMessage("failed to parse inline lua script")); } // load specific element type @@ -224,30 +210,29 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node) loadButton(boost::static_pointer_cast(element), node); else if(element->getElementType() == UI::Window) { UIWindowPtr window = boost::static_pointer_cast(element); - window->setTitle(yamlRead(node, "title", std::string())); + window->setTitle(node->readAt("title", std::string())); } else if(element->getElementType() == UI::Label) { UILabelPtr label = boost::static_pointer_cast(element); - label->setText(yamlRead(node, "text", std::string())); - label->setAlign(parseAlignment(yamlRead(node, "align", std::string("left")))); + label->setText(node->readAt("text", std::string())); + label->setAlign(parseAlignment(node->readAt("align", std::string("left")))); } } -void UILoader::loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const YAML::Node& node) +void UILoader::loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, FML::Node* node) { UIAnchorLayoutPtr layout = boost::dynamic_pointer_cast(anchoredElement->getLayout()); if(!layout) { - logError(yamlErrorDesc(node, "could not add anchor, because this element does not participate of an anchor layout")); + logError(node->generateErrorMessage("could not add anchor, because this element does not participate of an anchor layout")); return; } - std::string anchorDescription; - node >> anchorDescription; + std::string anchorDescription = node->value(); std::vector split; boost::split(split, anchorDescription, boost::is_any_of(std::string("."))); if(split.size() != 2) { - logError(yamlErrorDesc(node, "invalid anchor")); + logError(node->generateErrorMessage("invalid anchor")); return; } @@ -255,25 +240,24 @@ void UILoader::loadElementAnchor(const UIElementPtr& anchoredElement, UI::Anchor UI::AnchorPoint anchorLineEdge = UIAnchorLayout::parseAnchorPoint(split[1]); if(anchorLineEdge == UI::AnchorNone) { - logError(yamlErrorDesc(node, "invalid anchor type")); + logError(node->generateErrorMessage("invalid anchor type")); return; } if(!layout->addAnchor(anchoredElement, anchoredEdge, AnchorLine(anchorLineElementId, anchorLineEdge))) - logError(yamlErrorDesc(node, "anchoring failed")); + logError(node->generateErrorMessage("anchoring failed")); } -void UILoader::loadButton(const UIButtonPtr& button, const YAML::Node& node) +void UILoader::loadButton(const UIButtonPtr& button, FML::Node* node) { - button->setText(yamlRead(node["text"])); + button->setText(node->valueAt("text")); // set on click event - if(yamlHasValue(node, "onClick")) { - const YAML::Node& cnode = node["onClick"]; - if(g_lua.loadBufferAsFunction(yamlRead(cnode), getElementSourceDesc(button, "onClick"))) + if(FML::Node* cnode = node->at("onClick")) { + if(g_lua.loadBufferAsFunction(cnode->value(), getElementSourceDesc(button, "onClick"))) g_lua.setScriptableField(button, "onClick"); else - logError(yamlErrorDesc(cnode, "failed to parse inline lua script")); + logError(cnode->generateErrorMessage("failed to parse inline lua script")); } } diff --git a/src/framework/ui/uiloader.h b/src/framework/ui/uiloader.h index b0013ecc..370d8da0 100644 --- a/src/framework/ui/uiloader.h +++ b/src/framework/ui/uiloader.h @@ -33,27 +33,27 @@ class UILoader { public: - /// Loads an UIElement and it's children from a YAML file - UIElementPtr loadFromYAML(std::string filePath, const UIContainerPtr& parent = UIContainer::getRoot()); + /// Loads an UIElement and it's children from a FML file + UIElementPtr loadFromFile(std::string filePath, const UIContainerPtr& parent = UIContainer::getRoot()); private: /// Detect element type and create it UIElementPtr createElementFromId(const std::string& id); - /// Populate container children from a YAML node - void populateContainer(const UIContainerPtr& parent, const YAML::Node& node); + /// Populate container children from a FML node + void populateContainer(const UIContainerPtr& parent, FML::Node* node); - /// Load element and its children from a YAML node - void loadElements(const UIElementPtr& parent, const YAML::Node& node); + /// Load element and its children from a FML node + void loadElements(const UIElementPtr& parent, FML::Node* node); - /// Load element proprieties from a YAML node - void loadElement(const UIElementPtr& element, const YAML::Node& node); + /// Load element proprieties from a FML node + void loadElement(const UIElementPtr& element, FML::Node* node); - /// Load anchor from a YAML node - void loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const YAML::Node& node); + /// Load anchor from a FML node + void loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, FML::Node* node); // specific elements loading - void loadButton(const UIButtonPtr& button, const YAML::Node& node); + void loadButton(const UIButtonPtr& button, FML::Node* node); std::string getElementSourceDesc(const UIElementPtr& element, const std::string& key = ""); diff --git a/src/framework/ui/uiskins.cpp b/src/framework/ui/uiskins.cpp index 9056d044..4315649e 100644 --- a/src/framework/ui/uiskins.cpp +++ b/src/framework/ui/uiskins.cpp @@ -43,97 +43,62 @@ void UISkins::load(const std::string& skinName) if(!g_resources.loadFile(skinName + ".yml", fin)) flogFatal("FATAL ERROR: Could not load skin \"%s", skinName.c_str()); - try { - YAML::Parser parser(fin); + FML::Parser parser(fin); + if(!parser.hasError()) { + FML::Node* doc = parser.getDocument(); - YAML::Node doc; - parser.GetNextDocument(doc); - - m_defaultFont = g_fonts.get(yamlRead(doc, "default font")); + m_defaultFont = g_fonts.get(doc->readAt("default font")); if(!m_defaultFont) logFatal("FATAL ERROR: Could not load skin default font"); - m_defaultFontColor = yamlRead(doc, "default font color", Color::white); + m_defaultFontColor = doc->readAt("default font color", Color::white); - std::string defaultTextureName = yamlRead(doc, "default texture", std::string()); + std::string defaultTextureName = doc->readAt("default texture", std::string()); if(!defaultTextureName.empty()) m_defaultTexture = g_textures.get(defaultTextureName); - { - const YAML::Node& node = doc["buttons"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UIButtonSkin(name)); - skin->load(it.second()); - m_elementSkins.push_back(skin); - } - } - - { - const YAML::Node& node = doc["panels"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin(name, UI::Panel)); - skin->load(it.second()); + if(FML::Node* node = doc->at("buttons")) { + foreach(FML::Node* cnode, *node) { + UIElementSkinPtr skin = UIElementSkinPtr(new UIButtonSkin(cnode->tag())); + skin->load(cnode); m_elementSkins.push_back(skin); } } - { - const YAML::Node& node = doc["windows"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UIWindowSkin(name)); - skin->load(it.second()); + if(FML::Node* node = doc->at("panels")) { + foreach(FML::Node* cnode, *node) { + UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin(cnode->tag(), UI::Panel)); + skin->load(cnode); m_elementSkins.push_back(skin); } } - { - const YAML::Node& node = doc["labels"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UILabelSkin(name)); - skin->load(it.second()); + if(FML::Node* node = doc->at("windows")) { + foreach(FML::Node* cnode, *node) { + UIElementSkinPtr skin = UIElementSkinPtr(new UIWindowSkin(cnode->tag())); + skin->load(cnode); m_elementSkins.push_back(skin); } } - - { - const YAML::Node& node = doc["text edits"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UITextEditSkin(name)); - skin->load(it.second()); + if(FML::Node* node = doc->at("labels")) { + foreach(FML::Node* cnode, *node) { + UIElementSkinPtr skin = UIElementSkinPtr(new UILabelSkin(cnode->tag())); + skin->load(cnode); m_elementSkins.push_back(skin); } } - { - const YAML::Node& node = doc["line decorations"]; - for(auto it = node.begin(); it != node.end(); ++it) { - std::string name; - it.first() >> name; - - UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin(name, UI::LineDecoration)); - skin->load(it.second()); + if(FML::Node* node = doc->at("text edits")) { + foreach(FML::Node* cnode, *node) { + UIElementSkinPtr skin = UIElementSkinPtr(new UITextEditSkin(cnode->tag())); + skin->load(cnode); m_elementSkins.push_back(skin); } } - } catch (YAML::Exception& e) { - flogFatal("FATAL ERROR: Malformed skin file \"%s\":\n %s", skinName.c_str() % e.what()); + } else { + flogFatal("FATAL ERROR: Malformed skin file \"%s\":\n %s", skinName.c_str() % parser.getErrorMessage()); } g_resources.popCurrentPath(); diff --git a/src/framework/ui/uitexteditskin.cpp b/src/framework/ui/uitexteditskin.cpp index ffc0a090..4306f204 100644 --- a/src/framework/ui/uitexteditskin.cpp +++ b/src/framework/ui/uitexteditskin.cpp @@ -27,10 +27,10 @@ #include #include -void UITextEditSkin::load(const YAML::Node& node) +void UITextEditSkin::load(FML::Node* node) { UIElementSkin::load(node); - m_textMargin = yamlRead(node, "text margin", 2); + m_textMargin = node->readAt("text margin", 2); } void UITextEditSkin::apply(UIElement* element) diff --git a/src/framework/ui/uitexteditskin.h b/src/framework/ui/uitexteditskin.h index 0f31da50..21ea0d19 100644 --- a/src/framework/ui/uitexteditskin.h +++ b/src/framework/ui/uitexteditskin.h @@ -36,7 +36,7 @@ public: UITextEditSkin(const std::string& name) : UIElementSkin(name, UI::TextEdit) { } - void load(const YAML::Node& node); + void load(FML::Node* node); void apply(UIElement *element); void draw(UIElement *element); diff --git a/src/framework/ui/uiwindowskin.cpp b/src/framework/ui/uiwindowskin.cpp index d6854a5d..adf3477e 100644 --- a/src/framework/ui/uiwindowskin.cpp +++ b/src/framework/ui/uiwindowskin.cpp @@ -26,17 +26,17 @@ #include #include -void UIWindowSkin::load(const YAML::Node& node) +void UIWindowSkin::load(FML::Node* node) { UIElementSkin::load(node); - const YAML::Node& headNode = node["head"]; - const YAML::Node& bodyNode = node["body"]; + FML::Node* headNode = node->at("head"); + FML::Node* bodyNode = node->at("body"); m_headImage = boost::dynamic_pointer_cast(loadImage(headNode)); - m_headHeight = yamlRead(headNode, "height", m_headImage->getDefaultSize().height()); - m_headMargin = yamlRead(headNode, "margin", 0); - m_titleAlign = parseAlignment(yamlRead(headNode, "text align", std::string("center"))); + m_headHeight = headNode->readAt("height", m_headImage->getDefaultSize().height()); + m_headMargin = headNode->readAt("margin", 0); + m_titleAlign = parseAlignment(headNode->readAt("text align", std::string("center"))); m_bodyImage = loadImage(bodyNode); } diff --git a/src/framework/ui/uiwindowskin.h b/src/framework/ui/uiwindowskin.h index ca45f368..0e7448e8 100644 --- a/src/framework/ui/uiwindowskin.h +++ b/src/framework/ui/uiwindowskin.h @@ -35,7 +35,7 @@ public: UIWindowSkin(const std::string& name) : UIElementSkin(name, UI::Window) { } - void load(const YAML::Node& node); + void load(FML::Node* node); void draw(UIElement *element); int getHeadHeight() const { return m_headHeight; } diff --git a/src/framework/util/color.h b/src/framework/util/color.h index e9301061..d4bb5dcc 100644 --- a/src/framework/util/color.h +++ b/src/framework/util/color.h @@ -76,4 +76,14 @@ inline std::ostream& operator<<(std::ostream& out, const Color& color) return out; } +inline void operator>>(const FML::Node& node, Color& color) +{ + int r, g, b, a; + *node.at(0) >> r; + *node.at(1) >> g; + *node.at(2) >> b; + *node.at(3) >> a; + color.setRGBA(r,g,b,a); +} + #endif // COLOR_H diff --git a/src/framework/util/fml.cpp b/src/framework/util/fml.cpp new file mode 100644 index 00000000..8e9ba2e4 --- /dev/null +++ b/src/framework/util/fml.cpp @@ -0,0 +1,359 @@ +#include "fml.h" + +#include +#include +#include + +namespace FML { + + +/////////////////////////////////////////////////////////////////////////////// +// Utilities + +bool fml_convert(const std::string& input, bool& b) +{ + std::string names[5][2] = { { "1", "0" }, + { "y", "n" }, + { "yes", "no" }, + { "true", "false" }, + { "on", "off" } }; + std::string processedInput = input; + boost::trim(processedInput); + boost::to_lower(processedInput); + for(int i=0;i<5;i++) { + if(names[i][0] == processedInput) { + b = true; + return true; + } + + if(names[i][1] == processedInput) { + b = false; + return true; + } + } + return false; +} + +bool fml_convert(const std::string& input, std::string& output) { + output = input; + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Node + +Node::~Node() +{ + for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) + delete (*it); +} + +Node* Node::at(const std::string& childTag) const +{ + for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) { + if((*it)->tag() == childTag) + return (*it); + } + return NULL; +} + +Node* Node::at(int pos) const +{ + if(pos < 0 || pos >= size()) + return NULL; + return m_children[pos]; +} + +std::string Node::value(const std::string& def) const +{ + if(!m_value.empty()) + return m_value; + return def; +} + +std::string Node::valueAt(const std::string childTag, const std::string& def) const +{ + for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) { + if((*it)->tag() == childTag) + return (*it)->value(); + } + return def; +} + +void Node::addNode(Node *node) +{ + if(node->hasTag() && node->hasValue()) { + // remove nodes wit the same tag + for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) { + if((*it)->tag() == node->tag()) { + delete (*it); + m_children.erase(it); + break; + } + } + } + m_children.push_back(node); + node->setParent(this); +} + +std::string Node::generateErrorMessage(const std::string& message) const +{ + std::stringstream ss; + ss << "FML error "; + if(m_parser && !m_parser->getWhat().empty()) + ss << "in " << m_parser->getWhat(); + if(m_line > 0) + ss << "at line " << m_line; + ss << ": " << message; + return ss.str(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Parser + +Parser::~Parser() +{ + if(m_rootNode) + delete m_rootNode; +} + +bool Parser::load(std::istream& in) +{ + // initialize root node + if(m_rootNode) + delete m_rootNode; + m_rootNode = new Node(); + m_rootNode->setTag("root"); + + m_currentParent = m_rootNode; + m_currentDepth = 0; + m_currentLine = 0; + m_multilineMode = DONT_MULTILINE; + m_multilineData.clear(); + + while(in.good() && !in.eof()) { + m_currentLine++; + std::string line; + std::getline(in, line); + parseLine(line); + if(hasError()) + return false; + } + + // stop multilining if enabled + if(isMultilining()) + stopMultilining(); + + return !hasError(); +} + +void Parser::parseLine(std::string& line) +{ + // process multiline data first + if(isMultilining() && parseMultiline(line)) + return; + + // calculate depth + std::size_t numSpaces = line.find_first_not_of(' '); + + // trim left whitespaces + boost::trim_left(line); + + // skip comment lines + if(line[0] == '#') + return; + + // skip empty lines + if(line.empty()) + return; + + int depth = 0; + if(numSpaces != std::string::npos) { + depth = numSpaces / 2; + // check for syntax error + if(numSpaces % 2 != 0) { + setErrorMessage("file must be idented every 2 whitespaces", m_currentLine); + return; + } + } + + // a depth above + if(depth == m_currentDepth+1) { + // change current parent to the previous added node + m_currentParent = m_previousNode; + // a depth below, change parent to previus parent and add new node inside previuos parent + } else if(depth < m_currentDepth) { + // change current parent to the the new depth parent + for(int i=0;iparent(); + // else if nots the same depth it's a syntax error + } else if(depth != m_currentDepth) { + setErrorMessage("invalid indentation level", m_currentLine); + return; + } + + // update current depth + m_currentDepth = depth; + + // add node + Node *node = parseNode(line); + m_currentParent->addNode(node); + m_previousNode = node; +} + +Node *Parser::parseNode(std::string& line) +{ + // determine node tag and value + std::string tag; + std::string value; + + // its a node that has tag and possible a value + std::size_t dotsPos = line.find_first_of(':'); + if(dotsPos != std::string::npos) { + tag = line.substr(0, dotsPos); + value = line.substr(dotsPos+1); + } + // its a node that has a value but no tag + else if(line[0] == '-') { + value = line.substr(1); + } + // its a node that has only a tag + else { + tag = line; + } + + // trim the tag and value + boost::trim(tag); + boost::trim(value); + + // create the node + Node *node = new Node(this); + node->setLine(m_currentLine); + node->setTag(tag); + + // process node value + if(!value.empty()) { + // multiline text scalar + if(value[0] == '|') { + startMultilining(value); + } + // sequence + else if(value[0] == '[') { + if(boost::ends_with(value, "]")) { + value.erase(value.length()-1, 1); + value.erase(0, 1); + boost::trim(value); + boost::tokenizer > tokens(value); + for(boost::tokenizer >::iterator it = tokens.begin(); it != tokens.end(); ++it) { + value = *it; + boost::trim(value); + + Node *child = new Node(this); + child->setLine(m_currentLine); + child->setValue(value); + node->addNode(child); + } + } else + setErrorMessage("missing ']' in sequence", m_currentLine); + } + // text scalar + else { + node->setValue(parseTextScalar(value)); + } + } + + return node; +} + +std::string Parser::parseTextScalar(std::string value) +{ + if(value[0] == '"' && value[value.length()-1] == '"') { + value = value.substr(1, value.length()-2); + // escape characters + boost::replace_all(value, "\\\\", "\\"); + boost::replace_all(value, "\\\"", "\""); + boost::replace_all(value, "\\n", "\n"); + } + return value; +} + +void Parser::startMultilining(const std::string& param) +{ + m_multilineMode = MULTILINE_DONT_FOLD; + m_currentDepth++; + if(param.length() == 2) { + switch(param[1]) { + case '-': + m_multilineMode = MULTILINE_FOLD_BLOCK; + break; + case '+': + m_multilineMode = MULTILINE_FOLD_FLOW; + break; + default: + setErrorMessage("invalid multiline identifier", m_currentLine); + break; + } + } +} + +void Parser::stopMultilining() +{ + // remove all new lines at the end + if(m_multilineMode == MULTILINE_DONT_FOLD || m_multilineMode == MULTILINE_FOLD_BLOCK) { + while(true) { + int lastPos = m_multilineData.length()-1; + if(m_multilineData[lastPos] != '\n') + break; + m_multilineData.erase(lastPos, 1); + } + } + + if(m_multilineMode == MULTILINE_FOLD_BLOCK) + m_multilineData.append("\n"); + + m_previousNode->setValue(m_multilineData); + m_multilineMode = DONT_MULTILINE; + m_currentDepth--; + m_multilineData.clear(); +} + +bool Parser::parseMultiline(std::string line) +{ + // calculate depth + std::size_t numSpaces = line.find_first_not_of(' '); + + // depth above or equal current depth, add the text to the multiline + if(numSpaces != std::string::npos && (int)numSpaces >= m_currentDepth*2) { + m_multilineData += parseTextScalar(line.substr(m_currentDepth*2)) + "\n"; + return true; + // depth below the current depth, check if it is a node + } else if(numSpaces == std::string::npos || (int)numSpaces < m_currentDepth*2) { + // if it has contents, its a node, then we must end multiline + if(line.find_first_not_of(' ') != std::string::npos) { + stopMultilining(); + // the line still have a node on it + } + // no contents, just an extra line + else { + m_multilineData += "\n"; + return true; + } + } + return false; +} + +void Parser::setErrorMessage(const std::string& message, int line) +{ + std::stringstream ss; + ss << "FML syntax error in "; + if(!m_what.empty()) + ss << m_what << " "; + if(line > 0) + ss << "at line " << line; + ss << ": " << message; + m_error = ss.str(); +} + +} // namespace FML { diff --git a/src/framework/util/fml.h b/src/framework/util/fml.h new file mode 100644 index 00000000..a1fcf622 --- /dev/null +++ b/src/framework/util/fml.h @@ -0,0 +1,217 @@ +#ifndef FML_H +#define FML_H + +#include +#include +#include +#include +#include + +#include +#include + +namespace FML { + +/////////////////////////////////////////////////////////////////////////////// +// Utilities + +bool fml_convert(const std::string& input, bool& b); +bool fml_convert(const std::string& input, std::string& output); + +template +bool fml_convert(const V& in, T& out) { + std::stringstream ss; + ss << in; + ss >> out; + return !!ss; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Node + +class Parser; + +class Node : boost::noncopyable +{ +public: + typedef std::vector NodeList; + + Node(Parser* parser = 0) : m_parser(parser), m_parent(0), m_line(0) { } + ~Node(); + + bool hasTag() const { return !m_tag.empty(); } + bool hasChildren() const { return size() > 0; } + bool hasValue() const { return !m_value.empty(); } + bool hasNode(const std::string childTag) const { return at(childTag) != 0; } + + void setTag(std::string tag) { m_tag = tag; } + void setLine(int line) { m_line = line; } + void setValue(const std::string& value) { m_value = value; } + void setParent(Node* parent) { m_parent = parent; } + + std::string tag() const { return m_tag; } + int line() const { return m_line; } + int size() const { return m_children.size(); } + Node* parent() { return m_parent; } + + // iterators + typedef NodeList::iterator iterator; + typedef NodeList::const_iterator const_iterator; + + iterator begin() { return m_children.begin(); } + iterator end() { return m_children.end(); } + const_iterator begin() const { return m_children.begin(); } + const_iterator end() const { return m_children.end(); } + + Node* front() const { return at(0); } + Node* back() const { return at(size()-1); } + + // util for generating error message + std::string generateErrorMessage(const std::string& message) const; + + // extracting values operator + template + friend void operator >> (const Node& node, T& value); + + // get nodes + Node* at(const std::string& childTag) const; + Node* at(int pos) const; + + // get values + std::string value(const std::string& def = std::string()) const; + std::string valueAt(const std::string childTag, const std::string& def = std::string()) const; + + // read values + template + T read(T def = T()) const { + T value = def; + *this >> value; + return value; + } + + template + T readAt(const std::string childTag, T def = T()) const { + T v = def; + for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) { + if((*it)->tag() == childTag) { + *(*it) >> v; + break; + } + } + return v; + } + + void addNode(Node* node); + +private: + Parser* m_parser; + Node* m_parent; + int m_line; + NodeList m_children; + std::string m_tag; + std::string m_value; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Node operators + +template +void operator >> (const Node& node, T& v) +{ + fml_convert(node.value(), v); +} + +template +void operator >> (const Node& node, std::vector& v) +{ + v.clear(); + v.resize(node.size()); + for(unsigned i=0;i> v[i]; +} + +template +void operator >> (const Node& node, std::map& m) +{ + m.clear(); + for(Node::const_iterator it = node.begin(); it != node.end(); ++it) { + Node* child = (*it); + K k; + T v; + if(fml_convert(child->tag(), k)) { + *child >> v; + m[k] = v; + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Parser + +class Parser +{ + enum MultilineMode { + DONT_MULTILINE = 0, + MULTILINE_DONT_FOLD, + MULTILINE_FOLD_BLOCK, + MULTILINE_FOLD_FLOW + }; + +public: + Parser(std::string what = std::string()) : m_rootNode(0), m_what(what) { } + Parser(std::istream& in, std::string what = std::string()) : m_rootNode(0), m_what(what) { load(in); } + ~Parser(); + + bool load(std::istream& in); + + Node* getDocument() const { return m_rootNode; } + bool hasError() const { return !m_error.empty(); } + std::string getErrorMessage() { return m_error; } + std::string getWhat() { return m_what; } + +protected: + void parseLine(std::string& line); + Node* parseNode(std::string& line); + std::string parseTextScalar(std::string value); + + void startMultilining(const std::string& param); + void stopMultilining(); + bool parseMultiline(std::string line); + bool isMultilining() { return m_multilineMode != DONT_MULTILINE; } + + void setErrorMessage(const std::string& message, int line = 0); + +private: + int m_currentDepth; + int m_currentLine; + Node* m_currentParent; + Node* m_previousNode; + Node* m_rootNode; + MultilineMode m_multilineMode; + std::string m_multilineData; + std::string m_error; + std::string m_what; +}; + +} // namespace FML { + +// enable usage with foreach +namespace boost +{ + // specialize range_mutable_iterator and range_const_iterator in namespace boost + template<> + struct range_mutable_iterator< FML::Node > + { + typedef FML::Node::iterator type; + }; + + template<> + struct range_const_iterator< FML::Node > + { + typedef FML::Node::const_iterator type; + }; +} +#endif // FML_H diff --git a/src/framework/util/point.h b/src/framework/util/point.h index 5de0d42f..aae4b88c 100644 --- a/src/framework/util/point.h +++ b/src/framework/util/point.h @@ -85,4 +85,11 @@ inline std::ostream& operator<<(std::ostream& out, const TPoint& point) return out; } +template +inline void operator>>(const FML::Node& node, TPoint& point) +{ + *node.at(0) >> point.x; + *node.at(1) >> point.y; +} + #endif diff --git a/src/framework/util/rect.h b/src/framework/util/rect.h index 86841f5f..dfa810d2 100644 --- a/src/framework/util/rect.h +++ b/src/framework/util/rect.h @@ -309,4 +309,15 @@ inline std::ostream& operator<<(std::ostream& out, const TRect& rect) return out; } +template +inline void operator>>(const FML::Node& node, TRect& rect) +{ + T x, y, width, height; + *node.at(0) >> x; + *node.at(1) >> y; + *node.at(2) >> width; + *node.at(3) >> height; + rect.setRect(x, y, width, height); +} + #endif // RECT_H diff --git a/src/framework/util/size.h b/src/framework/util/size.h index b5016f3f..f22449b6 100644 --- a/src/framework/util/size.h +++ b/src/framework/util/size.h @@ -112,11 +112,12 @@ typedef TSize Size; typedef TSize SizeF; template -inline std::ostream& operator<<(std::ostream& out, const TSize& size) +inline void operator>>(const FML::Node& node, TSize& size) { - out << "Size(" << size.width() << "," - << size.height() << ")"; - return out; + T w, h; + *node.at(0) >> w; + *node.at(1) >> h; + size.setSize(w, h); } #endif diff --git a/src/framework/util/yaml.h b/src/framework/util/yaml.h deleted file mode 100644 index 6b74b875..00000000 --- a/src/framework/util/yaml.h +++ /dev/null @@ -1,125 +0,0 @@ -/* The MIT License - * - * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#ifndef YAML_H -#define YAML_H - -#include -#include - -inline void operator>>(const YAML::Node& node, Color& color) -{ - int r, g, b, a; - node[0] >> r; - node[1] >> g; - node[2] >> b; - node[3] >> a; - color.setRGBA(r,g,b,a); -} - -template -inline void operator>>(const YAML::Node& node, TPoint& point) -{ - node[0] >> point.x; - node[1] >> point.y; -} - -template -inline void operator>>(const YAML::Node& node, TRect& rect) -{ - T x, y, width, height; - node[0] >> x; - node[1] >> y; - node[2] >> width; - node[3] >> height; - rect.setRect(x, y, width, height); -} - -template -inline void operator>>(const YAML::Node& node, TSize& size) -{ - T w, h; - node[0] >> w; - node[1] >> h; - size.setSize(w, h); -} - -template -inline T yamlRead(const YAML::Node& node) -{ - return node.to(); -} - -template -inline T yamlRead(const YAML::Node& node, const char *name) -{ - T value; - if(node.Type() == YAML::NodeType::Map) - node[name] >> value; - return value; -} - -template -inline T yamlRead(const YAML::Node& node, const char *name, const T& defaultValue) -{ - T value = defaultValue; - if(node.Type() == YAML::NodeType::Map && node.FindValue(name)) - node[name] >> value; - return value; -} - -inline bool yamlHasValue(const YAML::Node& node, const char *name) -{ - return (node.Type() == YAML::NodeType::Map && node.FindValue(name) != NULL); -} - -inline std::string yamlErrorDesc(const YAML::Node& node, const std::string& error) -{ - return YAML::Exception(node.GetMark(), error.c_str()).what(); -} - -inline void yamlThrowError(const YAML::Node& node, const std::string& error) -{ - throw YAML::Exception(node.GetMark(), error.c_str()); -} - -template -inline std::map yamlReadMap(const YAML::Node& node, const char *name) -{ - std::map map; - if(node.Type() == YAML::NodeType::Map) { - if(const YAML::Node* mapNode = node.FindValue(name)) { - for(auto it = mapNode->begin(); it != mapNode->end(); ++it) { - A a; - B b; - it.first() >> a; - it.second() >> b; - map[a] = b; - } - } - } - return map; -} - -#endif // YAML_H diff --git a/src/main.cpp b/src/main.cpp index 7736ea88..c8ad2028 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,46 +32,40 @@ #include