// the XML parser calls here with all the elements void TMXMapInfo::startElement(void *ctx, const char *name, const char **atts) { CC_UNUSED_PARAM(ctx); TMXMapInfo *tmxMapInfo = this; std::string elementName = name; ValueMap attributeDict; if (atts && atts[0]) { for (int i = 0; atts[i]; i += 2) { std::string key = atts[i]; std::string value = atts[i+1]; attributeDict.insert(std::make_pair(key, Value(value))); } } if (elementName == "map") { std::string version = attributeDict["version"].asString(); if ( version != "1.0") { CCLOG("cocos2d: TMXFormat: Unsupported TMX version: %s", version.c_str()); } std::string orientationStr = attributeDict["orientation"].asString(); if (orientationStr == "orthogonal") { tmxMapInfo->setOrientation(TMXOrientationOrtho); } else if (orientationStr == "isometric") { tmxMapInfo->setOrientation(TMXOrientationIso); } else if (orientationStr == "hexagonal") { tmxMapInfo->setOrientation(TMXOrientationHex); } else if (orientationStr == "staggered") { tmxMapInfo->setOrientation(TMXOrientationStaggered); } else { CCLOG("cocos2d: TMXFomat: Unsupported orientation: %d", tmxMapInfo->getOrientation()); } Size s; s.width = attributeDict["width"].asFloat(); s.height = attributeDict["height"].asFloat(); tmxMapInfo->setMapSize(s); s.width = attributeDict["tilewidth"].asFloat(); s.height = attributeDict["tileheight"].asFloat(); tmxMapInfo->setTileSize(s); // The parent element is now "map" tmxMapInfo->setParentElement(TMXPropertyMap); } else if (elementName == "tileset") { // If this is an external tileset then start parsing that std::string externalTilesetFilename = attributeDict["source"].asString(); if (externalTilesetFilename != "") { _externalTilesetFilename = externalTilesetFilename; // Tileset file will be relative to the map file. So we need to convert it to an absolute path if (_TMXFileName.find_last_of("/") != string::npos) { string dir = _TMXFileName.substr(0, _TMXFileName.find_last_of("/") + 1); externalTilesetFilename = dir + externalTilesetFilename; } else { externalTilesetFilename = _resources + "/" + externalTilesetFilename; } externalTilesetFilename = FileUtils::getInstance()->fullPathForFilename(externalTilesetFilename); _currentFirstGID = attributeDict["firstgid"].asInt(); if (_currentFirstGID < 0) { _currentFirstGID = 0; } _recordFirstGID = false; tmxMapInfo->parseXMLFile(externalTilesetFilename); } else { TMXTilesetInfo *tileset = new (std::nothrow) TMXTilesetInfo(); tileset->_name = attributeDict["name"].asString(); if (_recordFirstGID) { // unset before, so this is tmx file. tileset->_firstGid = attributeDict["firstgid"].asInt(); if (tileset->_firstGid < 0) { tileset->_firstGid = 0; } } else { tileset->_firstGid = _currentFirstGID; _currentFirstGID = 0; } tileset->_spacing = attributeDict["spacing"].asInt(); tileset->_margin = attributeDict["margin"].asInt(); Size s; s.width = attributeDict["tilewidth"].asFloat(); s.height = attributeDict["tileheight"].asFloat(); tileset->_tileSize = s; tmxMapInfo->getTilesets().pushBack(tileset); tileset->release(); } } else if (elementName == "tile") { if (tmxMapInfo->getParentElement() == TMXPropertyLayer) { TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); Size layerSize = layer->_layerSize; uint32_t gid = static_cast<uint32_t>(attributeDict["gid"].asInt()); int tilesAmount = layerSize.width*layerSize.height; if (_xmlTileIndex < tilesAmount) { layer->_tiles[_xmlTileIndex++] = gid; } } else { TMXTilesetInfo* info = tmxMapInfo->getTilesets().back(); tmxMapInfo->setParentGID(info->_firstGid + attributeDict["id"].asInt()); tmxMapInfo->getTileProperties()[tmxMapInfo->getParentGID()] = Value(ValueMap()); tmxMapInfo->setParentElement(TMXPropertyTile); } } else if (elementName == "layer") { TMXLayerInfo *layer = new (std::nothrow) TMXLayerInfo(); layer->_name = attributeDict["name"].asString(); Size s; s.width = attributeDict["width"].asFloat(); s.height = attributeDict["height"].asFloat(); layer->_layerSize = s; Value& visibleValue = attributeDict["visible"]; layer->_visible = visibleValue.isNull() ? true : visibleValue.asBool(); Value& opacityValue = attributeDict["opacity"]; layer->_opacity = opacityValue.isNull() ? 255 : (unsigned char)(255.0f * opacityValue.asFloat()); float x = attributeDict["x"].asFloat(); float y = attributeDict["y"].asFloat(); layer->_offset.set(x, y); tmxMapInfo->getLayers().pushBack(layer); layer->release(); // The parent element is now "layer" tmxMapInfo->setParentElement(TMXPropertyLayer); } else if (elementName == "objectgroup") { TMXObjectGroup *objectGroup = new (std::nothrow) TMXObjectGroup(); objectGroup->setGroupName(attributeDict["name"].asString()); Vec2 positionOffset; positionOffset.x = attributeDict["x"].asFloat() * tmxMapInfo->getTileSize().width; positionOffset.y = attributeDict["y"].asFloat() * tmxMapInfo->getTileSize().height; objectGroup->setPositionOffset(positionOffset); tmxMapInfo->getObjectGroups().pushBack(objectGroup); objectGroup->release(); // The parent element is now "objectgroup" tmxMapInfo->setParentElement(TMXPropertyObjectGroup); } else if (elementName == "image") { TMXTilesetInfo* tileset = tmxMapInfo->getTilesets().back(); // build full path std::string imagename = attributeDict["source"].asString(); tileset->_originSourceImage = imagename; if (_TMXFileName.find_last_of("/") != string::npos) { string dir = _TMXFileName.substr(0, _TMXFileName.find_last_of("/") + 1); tileset->_sourceImage = dir + imagename; } else { tileset->_sourceImage = _resources + (_resources.size() ? "/" : "") + imagename; } } else if (elementName == "data") { std::string encoding = attributeDict["encoding"].asString(); std::string compression = attributeDict["compression"].asString(); if (encoding == "") { tmxMapInfo->setLayerAttribs(tmxMapInfo->getLayerAttribs() | TMXLayerAttribNone); TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); Size layerSize = layer->_layerSize; int tilesAmount = layerSize.width*layerSize.height; uint32_t *tiles = (uint32_t*) malloc(tilesAmount*sizeof(uint32_t)); // set all value to 0 memset(tiles, 0, tilesAmount*sizeof(int)); layer->_tiles = tiles; } else if (encoding == "base64") { int layerAttribs = tmxMapInfo->getLayerAttribs(); tmxMapInfo->setLayerAttribs(layerAttribs | TMXLayerAttribBase64); tmxMapInfo->setStoringCharacters(true); if (compression == "gzip") { layerAttribs = tmxMapInfo->getLayerAttribs(); tmxMapInfo->setLayerAttribs(layerAttribs | TMXLayerAttribGzip); } else if (compression == "zlib") { layerAttribs = tmxMapInfo->getLayerAttribs(); tmxMapInfo->setLayerAttribs(layerAttribs | TMXLayerAttribZlib); } CCASSERT( compression == "" || compression == "gzip" || compression == "zlib", "TMX: unsupported compression method" ); } } else if (elementName == "object") { TMXObjectGroup* objectGroup = tmxMapInfo->getObjectGroups().back(); // The value for "type" was blank or not a valid class name // Create an instance of TMXObjectInfo to store the object and its properties ValueMap dict; // Parse everything automatically const char* keys[] = {"name", "type", "width", "height", "gid"}; for (const auto& key : keys) { Value value = attributeDict[key]; dict[key] = value; } // But X and Y since they need special treatment // X int x = attributeDict["x"].asInt(); // Y int y = attributeDict["y"].asInt(); Vec2 p(x + objectGroup->getPositionOffset().x, _mapSize.height * _tileSize.height - y - objectGroup->getPositionOffset().y - attributeDict["height"].asInt()); p = CC_POINT_PIXELS_TO_POINTS(p); dict["x"] = Value(p.x); dict["y"] = Value(p.y); int width = attributeDict["width"].asInt(); int height = attributeDict["height"].asInt(); Size s(width, height); s = CC_SIZE_PIXELS_TO_POINTS(s); dict["width"] = Value(s.width); dict["height"] = Value(s.height); // Add the object to the objectGroup objectGroup->getObjects().push_back(Value(dict)); // The parent element is now "object" tmxMapInfo->setParentElement(TMXPropertyObject); } else if (elementName == "property") { if ( tmxMapInfo->getParentElement() == TMXPropertyNone ) { CCLOG( "TMX tile map: Parent element is unsupported. Cannot add property named '%s' with value '%s'", attributeDict["name"].asString().c_str(), attributeDict["value"].asString().c_str() ); } else if ( tmxMapInfo->getParentElement() == TMXPropertyMap ) { // The parent element is the map Value value = attributeDict["value"]; std::string key = attributeDict["name"].asString(); tmxMapInfo->getProperties().insert(std::make_pair(key, value)); } else if ( tmxMapInfo->getParentElement() == TMXPropertyLayer ) { // The parent element is the last layer TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); Value value = attributeDict["value"]; std::string key = attributeDict["name"].asString(); // Add the property to the layer layer->getProperties().insert(std::make_pair(key, value)); } else if ( tmxMapInfo->getParentElement() == TMXPropertyObjectGroup ) { // The parent element is the last object group TMXObjectGroup* objectGroup = tmxMapInfo->getObjectGroups().back(); Value value = attributeDict["value"]; std::string key = attributeDict["name"].asString(); objectGroup->getProperties().insert(std::make_pair(key, value)); } else if ( tmxMapInfo->getParentElement() == TMXPropertyObject ) { // The parent element is the last object TMXObjectGroup* objectGroup = tmxMapInfo->getObjectGroups().back(); ValueMap& dict = objectGroup->getObjects().rbegin()->asValueMap(); std::string propertyName = attributeDict["name"].asString(); dict[propertyName] = attributeDict["value"]; } else if ( tmxMapInfo->getParentElement() == TMXPropertyTile ) { ValueMap& dict = tmxMapInfo->getTileProperties().at(tmxMapInfo->getParentGID()).asValueMap(); std::string propertyName = attributeDict["name"].asString(); dict[propertyName] = attributeDict["value"]; } } else if (elementName == "polygon") { // find parent object's dict and add polygon-points to it TMXObjectGroup* objectGroup = _objectGroups.back(); ValueMap& dict = objectGroup->getObjects().rbegin()->asValueMap(); // get points value string std::string value = attributeDict["points"].asString(); if (!value.empty()) { ValueVector pointsArray; pointsArray.reserve(10); // parse points string into a space-separated set of points stringstream pointsStream(value); string pointPair; while (std::getline(pointsStream, pointPair, ' ')) { // parse each point combo into a comma-separated x,y point stringstream pointStream(pointPair); string xStr, yStr; ValueMap pointDict; // set x if (std::getline(pointStream, xStr, ',')) { int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x; pointDict["x"] = Value(x); } // set y if (std::getline(pointStream, yStr, ',')) { int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y; pointDict["y"] = Value(y); } // add to points array pointsArray.push_back(Value(pointDict)); } dict["points"] = Value(pointsArray); } } else if (elementName == "polyline") { // find parent object's dict and add polyline-points to it TMXObjectGroup* objectGroup = _objectGroups.back(); ValueMap& dict = objectGroup->getObjects().rbegin()->asValueMap(); // get points value string std::string value = attributeDict["points"].asString(); if (!value.empty()) { ValueVector pointsArray; pointsArray.reserve(10); // parse points string into a space-separated set of points stringstream pointsStream(value); string pointPair; while (std::getline(pointsStream, pointPair, ' ')) { // parse each point combo into a comma-separated x,y point stringstream pointStream(pointPair); string xStr, yStr; ValueMap pointDict; // set x if (std::getline(pointStream, xStr, ',')) { int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x; pointDict["x"] = Value(x); } // set y if (std::getline(pointStream, yStr, ',')) { int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y; pointDict["y"] = Value(y); } // add to points array pointsArray.push_back(Value(pointDict)); } dict["polylinePoints"] = Value(pointsArray); } } }
void TMXMapInfo::endElement(void *ctx, const char *name) { CC_UNUSED_PARAM(ctx); TMXMapInfo *tmxMapInfo = this; std::string elementName = name; if (elementName == "data") { if (tmxMapInfo->getLayerAttribs() & TMXLayerAttribBase64) { tmxMapInfo->setStoringCharacters(false); TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); std::string currentString = tmxMapInfo->getCurrentString(); unsigned char *buffer; auto len = base64Decode((unsigned char*)currentString.c_str(), (unsigned int)currentString.length(), &buffer); if (!buffer) { CCLOG("cocos2d: TiledMap: decode data error"); return; } if (tmxMapInfo->getLayerAttribs() & (TMXLayerAttribGzip | TMXLayerAttribZlib)) { unsigned char *deflated = nullptr; Size s = layer->_layerSize; // int sizeHint = s.width * s.height * sizeof(uint32_t); ssize_t sizeHint = s.width * s.height * sizeof(unsigned int); ssize_t CC_UNUSED inflatedLen = ZipUtils::inflateMemoryWithHint(buffer, len, &deflated, sizeHint); CCASSERT(inflatedLen == sizeHint, "inflatedLen should be equal to sizeHint!"); free(buffer); buffer = nullptr; if (!deflated) { CCLOG("cocos2d: TiledMap: inflate data error"); return; } layer->_tiles = reinterpret_cast<uint32_t*>(deflated); } else { layer->_tiles = reinterpret_cast<uint32_t*>(buffer); } tmxMapInfo->setCurrentString(""); } else if (tmxMapInfo->getLayerAttribs() & TMXLayerAttribNone) { _xmlTileIndex = 0; } } else if (elementName == "map") { // The map element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "layer") { // The layer element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "objectgroup") { // The objectgroup element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "object") { // The object element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "tileset") { _recordFirstGID = true; } }
void TMXMapInfo::endElement(void *ctx, const char *name) { CC_UNUSED_PARAM(ctx); TMXMapInfo *tmxMapInfo = this; std::string elementName = name; if (elementName == "data") { if (tmxMapInfo->getLayerAttribs() & TMXLayerAttribBase64) { tmxMapInfo->setStoringCharacters(false); TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); std::string currentString = tmxMapInfo->getCurrentString(); unsigned char *buffer; auto len = base64Decode((unsigned char*)currentString.c_str(), (unsigned int)currentString.length(), &buffer); if (!buffer) { CCLOG("cocos2d: TiledMap: decode data error"); return; } if (tmxMapInfo->getLayerAttribs() & (TMXLayerAttribGzip | TMXLayerAttribZlib)) { unsigned char *deflated = nullptr; Size s = layer->_layerSize; // int sizeHint = s.width * s.height * sizeof(uint32_t); ssize_t sizeHint = s.width * s.height * sizeof(unsigned int); ssize_t CC_UNUSED inflatedLen = ZipUtils::inflateMemoryWithHint(buffer, len, &deflated, sizeHint); CCASSERT(inflatedLen == sizeHint, "inflatedLen should be equal to sizeHint!"); free(buffer); buffer = nullptr; if (!deflated) { CCLOG("cocos2d: TiledMap: inflate data error"); return; } layer->_tiles = reinterpret_cast<uint32_t*>(deflated); } else { layer->_tiles = reinterpret_cast<uint32_t*>(buffer); } tmxMapInfo->setCurrentString(""); } else if (tmxMapInfo->getLayerAttribs() & TMXLayerAttribCSV) { unsigned char *buffer; TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); tmxMapInfo->setStoringCharacters(false); std::string currentString = tmxMapInfo->getCurrentString(); vector<string> gidTokens; istringstream filestr(currentString); string sRow; while(getline(filestr, sRow, '\n')) { string sGID; istringstream rowstr(sRow); while (getline(rowstr, sGID, ',')) { gidTokens.push_back(sGID); } } // 32-bits per gid buffer = (unsigned char*)malloc(gidTokens.size() * 4); if (!buffer) { CCLOG("cocos2d: TiledMap: CSV buffer not allocated."); return; } uint32_t* bufferPtr = reinterpret_cast<uint32_t*>(buffer); for(auto gidToken : gidTokens) { auto tileGid = (uint32_t)strtol(gidToken.c_str(), nullptr, 10); *bufferPtr = tileGid; bufferPtr++; } layer->_tiles = reinterpret_cast<uint32_t*>(buffer); tmxMapInfo->setCurrentString(""); } else if (tmxMapInfo->getLayerAttribs() & TMXLayerAttribNone) { _xmlTileIndex = 0; } } else if (elementName == "map") { // The map element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "layer") { // The layer element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "objectgroup") { // The objectgroup element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "object") { // The object element has ended tmxMapInfo->setParentElement(TMXPropertyNone); } else if (elementName == "tileset") { _recordFirstGID = true; } }