def __init__(self, mapReader): self.p = mapReader self.mMap = None self.mError = QString('') self.mReadingExternalTileset = False self.xml = QXmlStreamReader() self.mGidMapper = GidMapper()
def __init__(self): self.mLayerDataFormat = Map.LayerDataFormat.Base64Zlib self.mDtdEnabled = False self.mUseAbsolutePaths = False self.mMapDir = QDir() self.mGidMapper = GidMapper() self.mError = ''
def __init__(self): self.mError = '' self.mGidMapper = GidMapper() self.mMap = None self.mMapDir = QDir() self.mReadingExternalTileset = False
class VariantToMapConverter(): def __init__(self): self.mError = '' self.mGidMapper = GidMapper() self.mMap = None self.mMapDir = QDir() self.mReadingExternalTileset = False # Using the MapReader context since the messages are the same def tr(self, sourceText, disambiguation='', n=-1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def trUtf8(self, sourceText, disambiguation='', n=-1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) ## # Tries to convert the given \a variant to a Map instance. The \a mapDir # is necessary to resolve any relative references to external images. # # Returns 0 in case of an error. The error can be obstained using # self.errorString(). ## def toMap(self, variant, mapDir): self.mGidMapper.clear() self.mMapDir = mapDir variantMap = variant orientationString = variantMap.get("orientation", '') orientation = orientationFromString(orientationString) if (orientation == Map.Orientation.Unknown): self.mError = self.tr("Unsupported map orientation: \"%s\"" % orientationString) return None staggerAxisString = variantMap.get("staggeraxis", '') staggerAxis = staggerAxisFromString(staggerAxisString) staggerIndexString = variantMap.get("staggerindex", '') staggerIndex = staggerIndexFromString(staggerIndexString) renderOrderString = variantMap.get("renderorder", '') renderOrder = renderOrderFromString(renderOrderString) nextObjectId = variantMap.get("nextobjectid", 0) map = Map(orientation, variantMap.get("width", 0), variantMap.get("height", 0), variantMap.get("tilewidth", 0), variantMap.get("tileheight", 0)) map.setHexSideLength(variantMap.get("hexsidelength", 0)) map.setStaggerAxis(staggerAxis) map.setStaggerIndex(staggerIndex) map.setRenderOrder(renderOrder) if (nextObjectId): map.setNextObjectId(nextObjectId) self.mMap = map map.setProperties(self.toProperties(variantMap.get("properties", {}))) bgColor = variantMap.get("backgroundcolor", '') if (bgColor != '' and QColor.isValidColor(bgColor)): map.setBackgroundColor(QColor(bgColor)) for tilesetVariant in variantMap.get("tilesets", []): tileset = self.__toTileset(tilesetVariant) if not tileset: return None map.addTileset(tileset) for layerVariant in variantMap.get("layers", []): layer = self.toLayer(layerVariant) if not layer: return None map.addLayer(layer) return map ## # Returns the last error, if any. ## def errorString(self): return self.mError def toProperties(self, variant): variantMap = variant properties = Properties() for it in variantMap.items(): properties[it[0]] = it[1] return properties def toTileset(self, variant, directory): self.mMapDir = directory self.mReadingExternalTileset = True tileset = self.__toTileset(variant) self.mReadingExternalTileset = False return tileset def __toTileset(self, variant): variantMap = variant firstGid = variantMap.get("firstgid", 0) # Handle external tilesets sourceVariant = variantMap.get("source", '') if sourceVariant != '': source = resolvePath(self.mMapDir, sourceVariant) tileset, error = readTileset(source) if not tileset: self.mError = self.tr("Error while loading tileset '%s': %s" % (source, error)) else: self.mGidMapper.insert(firstGid, tileset) return tileset name = variantMap.get("name", '') tileWidth = variantMap.get("tilewidth", 0) tileHeight = variantMap.get("tileheight", 0) spacing = variantMap.get("spacing", 0) margin = variantMap.get("margin", 0) tileOffset = variantMap.get("tileoffset", {}) tileOffsetX = tileOffset.get("x", 0) tileOffsetY = tileOffset.get("y", 0) if (tileWidth <= 0 or tileHeight <= 0 or (firstGid == 0 and not self.mReadingExternalTileset)): self.mError = self.tr( "Invalid tileset parameters for tileset '%s'" % name) return None tileset = Tileset.create(name, tileWidth, tileHeight, spacing, margin) tileset.setTileOffset(QPoint(tileOffsetX, tileOffsetY)) trans = variantMap.get("transparentcolor", '') if (trans != '' and QColor.isValidColor(trans)): tileset.setTransparentColor(QColor(trans)) imageVariant = variantMap.get("image", '') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) if (not tileset.loadFromImage(imagePath)): self.mError = self.tr("Error loading tileset image:\n'%s'" % imagePath) return None tileset.setProperties( self.toProperties(variantMap.get("properties", {}))) # Read terrains terrainsVariantList = variantMap.get("terrains", []) for terrainMap in terrainsVariantList: tileset.addTerrain(terrainMap.get("name", ''), terrainMap.get("tile", 0)) # Read tile terrain and external image information tilesVariantMap = variantMap.get("tiles", {}) for it in tilesVariantMap.items(): ok = False tileIndex = Int(it[0]) if (tileIndex < 0): self.mError = self.tr("Tileset tile index negative:\n'%d'" % tileIndex) if (tileIndex >= tileset.tileCount()): # Extend the tileset to fit the tile if (tileIndex >= len(tilesVariantMap)): # If tiles are defined this way, there should be an entry # for each tile. # Limit the index to number of entries to prevent running out # of memory on malicious input.f self.mError = self.tr( "Tileset tile index too high:\n'%d'" % tileIndex) return None for i in range(tileset.tileCount(), tileIndex + 1): tileset.addTile(QPixmap()) tile = tileset.tileAt(tileIndex) if (tile): tileVar = it[1] terrains = tileVar.get("terrain", []) if len(terrains) == 4: for i in range(0, 4): terrainId, ok = Int2(terrains[i]) if (ok and terrainId >= 0 and terrainId < tileset.terrainCount()): tile.setCornerTerrainId(i, terrainId) probability, ok = Float2(tileVar.get("probability", 0.0)) if (ok): tile.setProbability(probability) imageVariant = tileVar.get("image", '') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) tileset.setTileImage(tileIndex, QPixmap(imagePath), imagePath) objectGroupVariant = tileVar.get("objectgroup", {}) if len(objectGroupVariant) > 0: tile.setObjectGroup(self.toObjectGroup(objectGroupVariant)) frameList = tileVar.get("animation", []) lenFrames = len(frameList) if lenFrames > 0: frames = QVector() for i in range(lenFrames): frames.append(Frame()) for i in range(lenFrames - 1, -1, -1): frameVariantMap = frameList[i] frame = frames[i] frame.tileId = frameVariantMap.get("tileid", 0) frame.duration = frameVariantMap.get("duration", 0) tile.setFrames(frames) # Read tile properties propertiesVariantMap = variantMap.get("tileproperties", {}) for it in propertiesVariantMap.items(): tileIndex = Int(it[0]) propertiesVar = it[1] if (tileIndex >= 0 and tileIndex < tileset.tileCount()): properties = self.toProperties(propertiesVar) tileset.tileAt(tileIndex).setProperties(properties) if not self.mReadingExternalTileset: self.mGidMapper.insert(firstGid, tileset) return tileset def toLayer(self, variant): variantMap = variant layer = None if (variantMap["type"] == "tilelayer"): layer = self.toTileLayer(variantMap) elif (variantMap["type"] == "objectgroup"): layer = self.toObjectGroup(variantMap) elif (variantMap["type"] == "imagelayer"): layer = self.toImageLayer(variantMap) if (layer): layer.setProperties( self.toProperties(variantMap.get("properties", {}))) offset = QPointF(variantMap.get("offsetx", 0.0), variantMap.get("offsety", 0.0)) layer.setOffset(offset) return layer def toTileLayer(self, variantMap): name = variantMap.get("name", '') width = variantMap.get("width", 0) height = variantMap.get("height", 0) dataVariant = variantMap["data"] tileLayer = TileLayer(name, variantMap.get("x", 0), variantMap.get("y", 0), width, height) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) tileLayer.setOpacity(opacity) tileLayer.setVisible(visible) encoding = variantMap.get("encoding", '') compression = variantMap.get("compression", '') if encoding == '' or encoding == "csv": layerDataFormat = Map.LayerDataFormat.CSV elif (encoding == "base64"): if compression == '': layerDataFormat = Map.LayerDataFormat.Base64 elif (compression == "gzip"): layerDataFormat = Map.LayerDataFormat.Base64Gzip elif (compression == "zlib"): layerDataFormat = Map.LayerDataFormat.Base64Zlib else: self.mError = self.tr("Compression method '%s' not supported" % compression) return None else: self.mError = self.tr("Unknown encoding: %1" % encoding) return None self.mMap.setLayerDataFormat(layerDataFormat) if layerDataFormat == Map.LayerDataFormat.XML or layerDataFormat == Map.LayerDataFormat.CSV: dataVariantList = dataVariant if len(dataVariantList) != width * height: self.mError = self.tr("Corrupt layer data for layer '%s'" % name) return None x = 0 y = 0 ok = False if (len(dataVariantList) != width * height): self.mError = self.tr("Corrupt layer data for layer '%s'" % name) return None for gidVariant in dataVariantList: gid, ok = Int2(gidVariant) if (not ok): self.mError = self.tr( "Unable to parse tile at (%d,%d) on layer '%s'" % (x, y, tileLayer.name())) return None cell = self.mGidMapper.gidToCell(gid, ok) tileLayer.setCell(x, y, cell) x += 1 if (x >= tileLayer.width()): x = 0 y += 1 elif layerDataFormat==Map.LayerDataFormat.Base64 \ or layerDataFormat==Map.LayerDataFormat.Base64Zlib \ or layerDataFormat==Map.LayerDataFormat.Base64Gzip: data = QByteArray(dataVariant) error = self.mGidMapper.decodeLayerData(tileLayer, data, layerDataFormat) if error == DecodeError.CorruptLayerData: self.mError = self.tr("Corrupt layer data for layer '%s'" % name) return None elif error == DecodeError.TileButNoTilesets: self.mError = self.tr("Tile used but no tilesets specified") return None elif error == DecodeError.InvalidTile: self.mError = self.tr("Invalid tile: %d" % self.mGidMapper.invalidTile()) return None elif error == DecodeError.NoError: pass return tileLayer def toObjectGroup(self, variantMap): objectGroup = ObjectGroup(variantMap.get("name", ''), variantMap.get("x", 0), variantMap.get("y", 0), variantMap.get("width", 0), variantMap.get("height", 0)) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) objectGroup.setOpacity(opacity) objectGroup.setVisible(visible) objectGroup.setColor(variantMap.get("color", '')) drawOrderString = variantMap.get("draworder", '') if drawOrderString != '': objectGroup.setDrawOrder(drawOrderFromString(drawOrderString)) if (objectGroup.drawOrder() == ObjectGroup.DrawOrder.UnknownOrder): self.mError = self.tr("Invalid draw order: %s" % drawOrderString) return None for objectVariant in variantMap.get("objects", []): objectVariantMap = objectVariant name = objectVariantMap.get("name", '') type = objectVariantMap.get("type", '') id = objectVariantMap.get("id", 0.0) gid = objectVariantMap.get("gid", 0.0) x = objectVariantMap.get("x", 0.0) y = objectVariantMap.get("y", 0.0) width = objectVariantMap.get("width", 0.0) height = objectVariantMap.get("height", 0.0) rotation = objectVariantMap.get("rotation", 0.0) pos = QPointF(x, y) size = QSizeF(width, height) object = MapObject(name, type, pos, size) object.setId(id) object.setRotation(rotation) if (gid): cell, ok = self.mGidMapper.gidToCell(gid) object.setCell(cell) if not object.cell().isEmpty(): tileSize = object.cell().tile.size() if width == 0: object.setWidth(tileSize.width()) if height == 0: object.setHeight(tileSize.height()) if (objectVariantMap.__contains__("visible")): object.setVisible(objectVariantMap.get("visible", True)) object.setProperties( self.toProperties(objectVariantMap.get("properties", {}))) objectGroup.addObject(object) polylineVariant = objectVariantMap.get("polyline", []) polygonVariant = objectVariantMap.get("polygon", []) if len(polygonVariant) > 0: object.setShape(MapObject.Polygon) object.setPolygon(self.toPolygon(polygonVariant)) if len(polylineVariant) > 0: object.setShape(MapObject.Polyline) object.setPolygon(self.toPolygon(polylineVariant)) if (objectVariantMap.__contains__("ellipse")): object.setShape(MapObject.Ellipse) return objectGroup def toImageLayer(self, variantMap): imageLayer = ImageLayer(variantMap.get("name", ''), variantMap.get("x", 0), variantMap.get("y", 0), variantMap.get("width", 0), variantMap.get("height", 0)) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) imageLayer.setOpacity(opacity) imageLayer.setVisible(visible) trans = variantMap.get("transparentcolor", '') if (not trans.isEmpty() and QColor.isValidColor(trans)): imageLayer.setTransparentColor(QColor(trans)) imageVariant = variantMap.get("image", '') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) if (not imageLayer.loadFromImage(QImage(imagePath), imagePath)): self.mError = self.tr("Error loading image:\n'%s'" % imagePath) return None return imageLayer def toPolygon(self, variant): polygon = QPolygonF() for pointVariant in variant: pointVariantMap = pointVariant pointX = pointVariantMap.get("x", 0.0) pointY = pointVariantMap.get("y", 0.0) polygon.append(QPointF(pointX, pointY)) return polygon
class MapReaderPrivate(): def tr(self, sourceText, disambiguation='', n=-1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def trUtf8(self, sourceText, disambiguation='', n=-1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def __init__(self, mapReader): self.p = mapReader self.mMap = None self.mError = QString('') self.mReadingExternalTileset = False self.xml = QXmlStreamReader() self.mGidMapper = GidMapper() def readMap(self, device, path): self.mError = QString('') self.mPath = path map = None self.xml.setDevice(device) if (self.xml.readNextStartElement() and self.xml.name() == "map"): map = self.__readMap() else: self.xml.raiseError(self.tr("Not a map file.")) self.mGidMapper.clear() return map def readTileset(self, device, path): self.mError = '' self.mPath = path tileset = None self.mReadingExternalTileset = True self.xml.setDevice(device) if (self.xml.readNextStartElement() and self.xml.name() == "tileset"): tileset = self.__readTileset() else: self.xml.raiseError(self.tr("Not a tileset file.")) self.mReadingExternalTileset = False return tileset def openFile(self, file): if (not file.exists()): self.mError = self.tr("File not found: %s" % file.fileName()) return False elif (not file.open(QFile.ReadOnly | QFile.Text)): self.mError = self.tr("Unable to read file: %s" % file.fileName()) return False return True def errorString(self): if self.mError != '': return self.mError else: return self.tr("%d\n\nLine %d, column %s" % (self.xml.lineNumber(), self.xml.columnNumber(), self.xml.errorString())) def __readUnknownElement(self): qDebug("Unknown element (fixme): " + self.xml.name() + " at line " + self.xml.lineNumber() + ", column " + self.xml.columnNumber()) self.xml.skipCurrentElement() def __readMap(self): atts = self.xml.attributes() mapWidth = Int(atts.value("width")) mapHeight = Int(atts.value("height")) tileWidth = Int(atts.value("tilewidth")) tileHeight = Int(atts.value("tileheight")) hexSideLength = Int(atts.value("hexsidelength")) orientationString = atts.value("orientation") orientation = orientationFromString(orientationString) if (orientation == Map.Orientation.Unknown): self.xml.raiseError( self.tr("Unsupported map orientation: \"%s\"" % orientationString)) staggerAxisString = atts.value("staggeraxis") staggerAxis = staggerAxisFromString(staggerAxisString) staggerIndexString = atts.value("staggerindex") staggerIndex = staggerIndexFromString(staggerIndexString) renderOrderString = atts.value("renderorder") renderOrder = renderOrderFromString(renderOrderString) nextObjectId = Int(atts.value("nextobjectid")) self.mMap = Map(orientation, mapWidth, mapHeight, tileWidth, tileHeight) self.mMap.setHexSideLength(hexSideLength) self.mMap.setStaggerAxis(staggerAxis) self.mMap.setStaggerIndex(staggerIndex) self.mMap.setRenderOrder(renderOrder) if (nextObjectId): self.mMap.setNextObjectId(nextObjectId) bgColorString = atts.value("backgroundcolor") if len(bgColorString) > 0: self.mMap.setBackgroundColor(QColor(bgColorString)) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): self.mMap.mergeProperties(self.__readProperties()) elif (self.xml.name() == "tileset"): self.mMap.addTileset(self.__readTileset()) elif (self.xml.name() == "layer"): self.mMap.addLayer(self.__readLayer()) elif (self.xml.name() == "objectgroup"): self.mMap.addLayer(self.__readObjectGroup()) elif (self.xml.name() == "imagelayer"): self.mMap.addLayer(self.__readImageLayer()) else: self.__readUnknownElement() # Clean up in case of error if (self.xml.hasError()): self.mMap = None return self.mMap def __readTileset(self): atts = self.xml.attributes() source = atts.value("source") firstGid = Int(atts.value("firstgid")) tileset = None if source == '': # Not an external tileset name = atts.value("name") tileWidth = Int(atts.value("tilewidth")) tileHeight = Int(atts.value("tileheight")) tileSpacing = Int(atts.value("spacing")) margin = Int(atts.value("margin")) if (tileWidth < 0 or tileHeight < 0 or (firstGid == 0 and not self.mReadingExternalTileset)): self.xml.raiseError( self.tr("Invalid tileset parameters for tileset '%s'" % name)) else: tileset = Tileset.create(name, tileWidth, tileHeight, tileSpacing, margin) while (self.xml.readNextStartElement()): if (self.xml.name() == "tile"): self.__readTilesetTile(tileset) elif (self.xml.name() == "tileoffset"): oa = self.xml.attributes() x = Int(oa.value("x")) y = Int(oa.value("y")) tileset.setTileOffset(QPoint(x, y)) self.xml.skipCurrentElement() elif (self.xml.name() == "properties"): tileset.mergeProperties(self.__readProperties()) elif (self.xml.name() == "image"): if (tileWidth == 0 or tileHeight == 0): self.xml.raiseError( self. tr("Invalid tileset parameters for tileset '%s'" % name)) tileset.clear() break else: self.__readTilesetImage(tileset) elif (self.xml.name() == "terraintypes"): self.__readTilesetTerrainTypes(tileset) else: self.__readUnknownElement() else: # External tileset absoluteSource = self.p.resolveReference(source, self.mPath) tileset, error = self.p.readExternalTileset(absoluteSource) if (not tileset): self.xml.raiseError( self.tr("Error while loading tileset '%s': %s" % (absoluteSource, error))) self.xml.skipCurrentElement() if (tileset and not self.mReadingExternalTileset): self.mGidMapper.insert(firstGid, tileset) return tileset def __readTilesetTile(self, tileset): atts = self.xml.attributes() id = Int(atts.value("id")) if (id < 0): self.xml.raiseError(self.tr("Invalid tile ID: %d" % id)) return hasImage = tileset.imageSource() != '' if (hasImage and id >= tileset.tileCount()): self.xml.raiseError( self.tr("Tile ID does not exist in tileset image: %d" % id)) return if (id > tileset.tileCount()): self.xml.raiseError( self.tr("Invalid (nonconsecutive) tile ID: %d" % id)) return # For tilesets without image source, consecutive tile IDs are allowed (for # tiles with individual images) if (id == tileset.tileCount()): tileset.addTile(QPixmap()) tile = tileset.tileAt(id) # Read tile quadrant terrain ids terrain = atts.value("terrain") if terrain != '': quadrants = terrain.split(",") if (len(quadrants) == 4): for i in range(4): if quadrants[i] == '': t = -1 else: t = Int(quadrants[i]) tile.setCornerTerrainId(i, t) # Read tile probability probability = atts.value("probability") if probability != '': tile.setProbability(Float(probability)) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): tile.mergeProperties(self.__readProperties()) elif (self.xml.name() == "image"): source = self.xml.attributes().value("source") if source != '': source = self.p.resolveReference(source, self.mPath) tileset.setTileImage(id, QPixmap.fromImage(self.__readImage()), source) elif (self.xml.name() == "objectgroup"): tile.setObjectGroup(self.__readObjectGroup()) elif (self.xml.name() == "animation"): tile.setFrames(self.__readAnimationFrames()) else: self.__readUnknownElement() # Temporary code to support TMW-style animation frame properties if (not tile.isAnimated() and tile.hasProperty("animation-frame0")): frames = QVector() i = 0 while (i >= 0): frameName = "animation-frame" + str(i) delayName = "animation-delay" + str(i) if (tile.hasProperty(frameName) and tile.hasProperty(delayName)): frame = Frame() frame.tileId = tile.property(frameName) frame.duration = tile.property(delayName) * 10 frames.append(frame) else: break i += 1 tile.setFrames(frames) def __readTilesetImage(self, tileset): atts = self.xml.attributes() source = atts.value("source") trans = atts.value("trans") if len(trans) > 0: if (not trans.startswith('#')): trans = '#' + trans tileset.setTransparentColor(QColor(trans)) if len(source) > 0: source = self.p.resolveReference(source, self.mPath) # Set the width that the tileset had when the map was saved width = Int(atts.value("width")) self.mGidMapper.setTilesetWidth(tileset, width) if (not tileset.loadFromImage(self.__readImage(), source)): self.xml.raiseError( self.tr("Error loading tileset image:\n'%s'" % source)) def __readTilesetTerrainTypes(self, tileset): while (self.xml.readNextStartElement()): if (self.xml.name() == "terrain"): atts = self.xml.attributes() name = atts.value("name") tile = Int(atts.value("tile")) terrain = tileset.addTerrain(name, tile) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): terrain.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() else: self.__readUnknownElement() def __readImage(self): atts = self.xml.attributes() source = atts.value("source") format = atts.value("format") if len(source) == 0: while (self.xml.readNextStartElement()): if (self.xml.name() == "data"): atts = self.xml.attributes() encoding = atts.value("encoding") data = self.xml.readElementText().toLatin1() if (encoding == "base64"): data = QByteArray.fromBase64(data) self.xml.skipCurrentElement() return QImage.fromData(data, format.toLatin1()) else: self.__readUnknownElement() else: self.xml.skipCurrentElement() source = self.p.resolveReference(source, self.mPath) image = self.p.readExternalImage(source) if (image.isNull()): self.xml.raiseError( self.tr("Error loading image:\n'%s'" % source)) return image return QImage() def __readLayer(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) tileLayer = TileLayer(name, x, y, width, height) readLayerAttributes(tileLayer, atts) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): tileLayer.mergeProperties(self.__readProperties()) elif (self.xml.name() == "data"): self.__readLayerData(tileLayer) else: self.__readUnknownElement() return tileLayer def __readLayerData(self, tileLayer): atts = self.xml.attributes() encoding = atts.value("encoding") compression = atts.value("compression") layerDataFormat = 0 if (encoding == ''): layerDataFormat = Map.LayerDataFormat.XML elif (encoding == "csv"): layerDataFormat = Map.LayerDataFormat.CSV elif (encoding == "base64"): if (compression == ''): layerDataFormat = Map.LayerDataFormat.Base64 elif (compression == "gzip"): layerDataFormat = Map.LayerDataFormat.Base64Gzip elif (compression == "zlib"): layerDataFormat = Map.LayerDataFormat.Base64Zlib else: self.xml.raiseError( self.tr("Compression method '%s' not supported" % compression)) return else: self.xml.raiseError(self.tr("Unknown encoding: %s" % encoding)) return self.mMap.setLayerDataFormat(layerDataFormat) x = 0 y = 0 while (self.xml.readNext() != QXmlStreamReader.Invalid): if (self.xml.isEndElement()): break elif (self.xml.isStartElement()): if (self.xml.name() == "tile"): if (y >= tileLayer.height()): self.xml.raiseError( self.tr("Too many <tile> elements")) continue atts = self.xml.attributes() gid = Int(atts.value("gid")) tileLayer.setCell(x, y, self.__cellForGid(gid)) x += 1 if (x >= tileLayer.width()): x = 0 y += 1 self.xml.skipCurrentElement() else: self.__readUnknownElement() elif (self.xml.isCharacters() and not self.xml.isWhitespace()): if (encoding == "base64"): self.__decodeBinaryLayerData(tileLayer, self.xml.text(), layerDataFormat) elif (encoding == "csv"): self.__decodeCSVLayerData(tileLayer, self.xml.text()) def __decodeBinaryLayerData(self, tileLayer, data, format): error = self.mGidMapper.decodeLayerData(tileLayer, data, format) if error == DecodeError.CorruptLayerData: self.xml.raiseError( self.tr("Corrupt layer data for layer '%s'" % tileLayer.name())) return elif error == DecodeError.TileButNoTilesets: self.xml.raiseError(self.tr("Tile used but no tilesets specified")) return elif error == DecodeError.InvalidTile: self.xml.raiseError( self.tr("Invalid tile: %d" % self.mGidMapper.invalidTile())) return elif error == DecodeError.NoError: pass def __decodeCSVLayerData(self, tileLayer, text): trimText = text.strip() tiles = trimText.split(',') if (len(tiles) != tileLayer.width() * tileLayer.height()): self.xml.raiseError( self.tr("Corrupt layer data for layer '%s'" % tileLayer.name())) return for y in range(tileLayer.height()): for x in range(tileLayer.width()): conversionOk = False gid, conversionOk = Int2(tiles[y * tileLayer.width() + x]) if (not conversionOk): self.xml.raiseError( self.tr( "Unable to parse tile at (%d,%d) on layer '%s'" % (x + 1, y + 1, tileLayer.name()))) return tileLayer.setCell(x, y, self.__cellForGid(gid)) ## # Returns the cell for the given global tile ID. Errors are raised with # the QXmlStreamReader. # # @param gid the global tile ID # @return the cell data associated with the given global tile ID, or an # empty cell if not found ## def __cellForGid(self, gid): ok = False result, ok = self.mGidMapper.gidToCell(gid) if (not ok): if (self.mGidMapper.isEmpty()): self.xml.raiseError( self.tr("Tile used but no tilesets specified")) else: self.xml.raiseError(self.tr("Invalid tile: %d" % gid)) return result def __readImageLayer(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) imageLayer = ImageLayer(name, x, y, width, height) readLayerAttributes(imageLayer, atts) while (self.xml.readNextStartElement()): if (self.xml.name() == "image"): self.__readImageLayerImage(imageLayer) elif (self.xml.name() == "properties"): imageLayer.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() return imageLayer def __readImageLayerImage(self, imageLayer): atts = self.xml.attributes() source = atts.value("source") trans = atts.value("trans") if trans != '': if (not trans.startswith('#')): trans = '#' + trans imageLayer.setTransparentColor(QColor(trans)) source = self.p.resolveReference(source, self.mPath) imageLayerImage = self.p.readExternalImage(source) if (not imageLayer.loadFromImage(imageLayerImage, source)): self.xml.raiseError( self.tr("Error loading image layer image:\n'%s'" % source)) self.xml.skipCurrentElement() def __readObjectGroup(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) objectGroup = ObjectGroup(name, x, y, width, height) readLayerAttributes(objectGroup, atts) color = atts.value("color") if color != '': objectGroup.setColor(color) if (atts.hasAttribute("draworder")): value = atts.value("draworder") drawOrder = drawOrderFromString(value) if (drawOrder == ObjectGroup.DrawOrder.UnknownOrder): #del objectGroup self.xml.raiseError(self.tr("Invalid draw order: %s" % value)) return None objectGroup.setDrawOrder(drawOrder) while (self.xml.readNextStartElement()): if (self.xml.name() == "object"): objectGroup.addObject(self.__readObject()) elif (self.xml.name() == "properties"): objectGroup.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() return objectGroup def __readObject(self): atts = self.xml.attributes() id = Int(atts.value("id")) name = atts.value("name") gid = Int(atts.value("gid")) x = Float(atts.value("x")) y = Float(atts.value("y")) width = Float(atts.value("width")) height = Float(atts.value("height")) type = atts.value("type") visibleRef = atts.value("visible") pos = QPointF(x, y) size = QSizeF(width, height) object = MapObject(name, type, pos, size) object.setId(id) try: rotation = Float(atts.value("rotation")) ok = True except: ok = False if (ok): object.setRotation(rotation) if (gid): object.setCell(self.__cellForGid(gid)) if (not object.cell().isEmpty()): tileSize = object.cell().tile.size() if (width == 0): object.setWidth(tileSize.width()) if (height == 0): object.setHeight(tileSize.height()) try: visible = int(visibleRef) ok = True except: ok = False if ok: object.setVisible(visible) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): object.mergeProperties(self.__readProperties()) elif (self.xml.name() == "polygon"): object.setPolygon(self.__readPolygon()) object.setShape(MapObject.Polygon) elif (self.xml.name() == "polyline"): object.setPolygon(self.__readPolygon()) object.setShape(MapObject.Polyline) elif (self.xml.name() == "ellipse"): self.xml.skipCurrentElement() object.setShape(MapObject.Ellipse) else: self.__readUnknownElement() return object def __readPolygon(self): atts = self.xml.attributes() points = atts.value("points") pointsList = list(filter(lambda x: x.strip() != '', points.split(' '))) polygon = QPolygonF() ok = True for point in pointsList: try: x, y = point.split(',') except: ok = False break x, ok = Float2(x) if (not ok): break y, ok = Float2(y) if (not ok): break polygon.append(QPointF(x, y)) if (not ok): self.xml.raiseError(self.tr("Invalid points data for polygon")) self.xml.skipCurrentElement() return polygon def __readAnimationFrames(self): frames = QVector() while (self.xml.readNextStartElement()): if (self.xml.name() == "frame"): atts = self.xml.attributes() frame = Frame() frame.tileId = Int(atts.value("tileid")) frame.duration = Int(atts.value("duration")) frames.append(frame) self.xml.skipCurrentElement() else: self.__readUnknownElement() return frames def __readProperties(self): properties = Properties() while (self.xml.readNextStartElement()): if (self.xml.name() == "property"): self.__readProperty(properties) else: self.__readUnknownElement() return properties def __readProperty(self, properties): atts = self.xml.attributes() propertyName = atts.value("name") propertyValue = atts.value("value") while (self.xml.readNext() != QXmlStreamReader.Invalid): if (self.xml.isEndElement()): break elif (self.xml.isCharacters() and not self.xml.isWhitespace()): if (propertyValue.isEmpty()): propertyValue = self.xml.text() elif (self.xml.isStartElement()): self.__readUnknownElement() properties.insert(propertyName, propertyValue)
def __init__(self): pass self.mMapDir = QDir() self.mGidMapper = GidMapper()
class MapToVariantConverter(): def __init__(self): pass self.mMapDir = QDir() self.mGidMapper = GidMapper() def toVariant(self, arg1, arg2): tp1 = type(arg1) tp2 = type(arg2) if tp1==Map and tp2==QDir: ## # Converts the given \s map to a QVariant. The \a mapDir is used to # construct relative paths to external resources. ## map, mapDir = arg1, arg2 self.mMapDir = mapDir self.mGidMapper.clear() mapVariant = {} mapVariant["version"] = 1.0 mapVariant["orientation"] = orientationToString(map.orientation()) mapVariant["renderorder"] = renderOrderToString(map.renderOrder()) mapVariant["width"] = map.width() mapVariant["height"] = map.height() mapVariant["tilewidth"] = map.tileWidth() mapVariant["tileheight"] = map.tileHeight() mapVariant["properties"] = self.__toVariant(map.properties()) mapVariant["nextobjectid"] = map.nextObjectId() if (map.orientation() == Map.Orientation.Hexagonal) : mapVariant["hexsidelength"] = map.hexSideLength() if (map.orientation() == Map.Orientation.Hexagonal or map.orientation() == Map.Orientation.Staggered) : mapVariant["staggeraxis"] = staggerAxisToString(map.staggerAxis()) mapVariant["staggerindex"] = staggerIndexToString(map.staggerIndex()) bgColor = map.backgroundColor() if (bgColor.isValid()): mapVariant["backgroundcolor"] = bgColor.name() tilesetVariants = [] firstGid = 1 for tileset in map.tilesets(): tilesetVariants.append(self.__toVariant(tileset, firstGid)) self.mGidMapper.insert(firstGid, tileset) firstGid += tileset.tileCount() mapVariant["tilesets"] = tilesetVariants layerVariants = [] for layer in map.layers(): x = layer.layerType() if x==Layer.TileLayerType: layerVariants.append(self.__toVariant(layer, map.layerDataFormat())) elif x==Layer.ObjectGroupType: layerVariants.append(self.__toVariant(layer)) elif x==Layer.ImageLayerType: layerVariants.append(self.__toVariant(layer)) mapVariant["layers"] = layerVariants return mapVariant elif tp1==Tileset and tp2==QDir: ## # Converts the given \s tileset to a QVariant. The \a directory is used to # construct relative paths to external resources. ## tileset, directory = arg1, arg2 self.mMapDir = directory return self.__toVariant(tileset, 0) def __toVariant(self, *args): l = len(args) if l==1: arg = args[0] tp = type(arg) if tp == Properties: properties = arg variantMap = {} for it in properties: variantMap[it[0]] = it[1] return variantMap elif tp == ObjectGroup: objectGroup = arg objectGroupVariant = {} objectGroupVariant["type"] = "objectgroup" if (objectGroup.color().isValid()): objectGroupVariant["color"] = objectGroup.color().name() objectGroupVariant["draworder"] = drawOrderToString(objectGroup.drawOrder()) self.addLayerAttributes(objectGroupVariant, objectGroup) objectVariants = [] for object in objectGroup.objects(): objectVariant = {} name = object.name() _type = object.type() objectVariant["properties"] = self.__toVariant(object.properties()) objectVariant["id"] = object.id() objectVariant["name"] = name objectVariant["type"] = _type if (not object.cell().isEmpty()): objectVariant["gid"] = self.mGidMapper.cellToGid(object.cell()) objectVariant["x"] = object.x() objectVariant["y"] = object.y() objectVariant["width"] = object.width() objectVariant["height"] = object.height() objectVariant["rotation"] = object.rotation() objectVariant["visible"] = object.isVisible() ## Polygons are stored in this format: # # "polygon/polyline": [ # { "x": 0, "y": 0 }, # { "x": 1, "y": 1 }, # ... # ] ## polygon = object.polygon() if (not polygon.isEmpty()) : pointVariants = [] for point in polygon: pointVariant = {} pointVariant["x"] = point.x() pointVariant["y"] = point.y() pointVariants.append(pointVariant) if (object.shape() == MapObject.Polygon): objectVariant["polygon"] = pointVariants else: objectVariant["polyline"] = pointVariants if (object.shape() == MapObject.Ellipse): objectVariant["ellipse"] = True objectVariants.append(objectVariant) objectGroupVariant["objects"] = objectVariants return objectGroupVariant elif tp == ImageLayer: imageLayer = arg imageLayerVariant = {} imageLayerVariant["type"] = "imagelayer" self.addLayerAttributes(imageLayerVariant, imageLayer) rel = self.mMapDir.relativeFilePath(imageLayer.imageSource()) imageLayerVariant["image"] = rel transColor = imageLayer.transparentColor() if (transColor.isValid()): imageLayerVariant["transparentcolor"] = transColor.name() return imageLayerVariant elif l==2: arg1, arg2 = args tp1 = type(arg1) tp2 = type(arg2) if tp1==Tileset and tp2==int: tileset, firstGid = arg1, arg2 tilesetVariant = {} if firstGid > 0: tilesetVariant["firstgid"] = firstGid fileName = tileset.fileName() if fileName != '': source = self.mMapDir.relativeFilePath(fileName) tilesetVariant["source"] = source # Tileset is external, so no need to write any of the stuff below return tilesetVariant tilesetVariant["firstgid"] = firstGid tilesetVariant["name"] = tileset.name() tilesetVariant["tilewidth"] = tileset.tileWidth() tilesetVariant["tileheight"] = tileset.tileHeight() tilesetVariant["spacing"] = tileset.tileSpacing() tilesetVariant["margin"] = tileset.margin() tilesetVariant["tilecount"] = tileset.tileCount() tilesetVariant["properties"] = self.__toVariant(tileset.properties()) offset = tileset.tileOffset() if (not offset.isNull()) : tileOffset = {} tileOffset["x"] = offset.x() tileOffset["y"] = offset.y() tilesetVariant["tileoffset"] = tileOffset # Write the image element imageSource = tileset.imageSource() if imageSource != '': rel = self.mMapDir.relativeFilePath(tileset.imageSource()) tilesetVariant["image"] = rel transColor = tileset.transparentColor() if (transColor.isValid()): tilesetVariant["transparentcolor"] = transColor.name() tilesetVariant["imagewidth"] = tileset.imageWidth() tilesetVariant["imageheight"] = tileset.imageHeight() ## # Write the properties, terrain, external image, object group and # animation for those tiles that have them. ## tilePropertiesVariant = {} tilesVariant = {} for i in range(0, tileset.tileCount()): tile = tileset.tileAt(i) properties = tile.properties() if (not properties.isEmpty()): tilePropertiesVariant[QString.number(i)] = self.__toVariant(properties) tileVariant = {} if (tile.terrain() != 0xFFFFFFFF) : terrainIds = [] for j in range(0, 4): terrainIds.append(tile.cornerTerrainId(j)) tileVariant["terrain"] = terrainIds if (tile.probability() != 1.0): tileVariant["probability"] = tile.probability() if tile.imageSource() != '': rel = self.mMapDir.relativeFilePath(tile.imageSource()) tileVariant["image"] = rel if (tile.objectGroup()): tileVariant["objectgroup"] = self.__toVariant(tile.objectGroup()) if (tile.isAnimated()) : frameVariants = [] for frame in tile.frames(): frameVariant = {} frameVariant["tileid"] = frame.tileId frameVariant["duration"] = frame.duration frameVariants.append(frameVariant) tileVariant["animation"] = frameVariants if len(tileVariant) > 0: tilesVariant[QString.number(i)] = tileVariant if len(tilePropertiesVariant) > 0: tilesetVariant["tileproperties"] = tilePropertiesVariant if len(tilesVariant) > 0: tilesetVariant["tiles"] = tilesVariant # Write terrains if (tileset.terrainCount() > 0) : terrainsVariant = [] for i in range(0, tileset.terrainCount()): terrain = tileset.terrain(i) properties = terrain.properties() terrainVariant = {} terrainVariant["name"] = terrain.name() if (not properties.isEmpty()): terrainVariant["properties"] = self.__toVariant(properties) terrainVariant["tile"] = terrain.imageTileId() terrainsVariant.append(terrainVariant) tilesetVariant["terrains"] = terrainsVariant return tilesetVariant elif tp1==TileLayer and tp2==Map.LayerDataFormat: tileLayer, format = arg1, arg2 tileLayerVariant = {} tileLayerVariant["type"] = "tilelayer" self.addLayerAttributes(tileLayerVariant, tileLayer) if format == Map.LayerDataFormat.XML or format == Map.LayerDataFormat.CSV: tileVariants = [] for y in range(tileLayer.height()): for x in range(tileLayer.width()): tileVariants.append(self.mGidMapper.cellToGid(tileLayer.cellAt(x, y))) tileLayerVariant["data"] = tileVariants elif format in [Map.LayerDataFormat.Base64, Map.LayerDataFormat.Base64Zlib, Map.LayerDataFormat.Base64Gzip]: tileLayerVariant["encoding"] = "base64" if format == Map.LayerDataFormat.Base64Zlib: tileLayerVariant["compression"] = "zlib" elif format == Map.LayerDataFormat.Base64Gzip: tileLayerVariant["compression"] = "gzip" layerData = self.mGidMapper.encodeLayerData(tileLayer, format) tileLayerVariant["data"] = layerData.data().decode() return tileLayerVariant def addLayerAttributes(self, layerVariant, layer): layerVariant["name"] = layer.name() layerVariant["width"] = layer.width() layerVariant["height"] = layer.height() layerVariant["x"] = layer.x() layerVariant["y"] = layer.y() layerVariant["visible"] = layer.isVisible() layerVariant["opacity"] = layer.opacity() offset = layer.offset() if not offset.isNull(): layerVariant["offsetx"] = offset.x() layerVariant["offsety"] = offset.y() properties = layer.properties() if (not properties.isEmpty()): layerVariant["properties"] = self.__toVariant(properties)
def read(self, fileName): file = QFile(fileName) if (not file.open (QIODevice.ReadOnly)): self.mError = self.tr("Could not open file for reading.") return None # default to values of the original flare alpha game. map = Map(Map.Orientation.Isometric, 256, 256, 64, 32) stream = QTextStream(file) line = QString() sectionName = QString() newsection = False path = QFileInfo(file).absolutePath() base = 10 gidMapper = GidMapper() gid = 1 tilelayer = None objectgroup = None mapobject = None tilesetsSectionFound = False headerSectionFound = False tilelayerSectionFound = False # tile layer or objects while (not stream.atEnd()): line = stream.readLine() if line == '': continue startsWith = line[0] if (startsWith == '['): sectionName = line[1:line.index(']')] newsection = True continue if (sectionName == "header"): headerSectionFound = True #get map properties epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "width"): map.setWidth(Int(value)) elif (key == "height"): map.setHeight(Int(value)) elif (key == "tilewidth"): map.setTileWidth(Int(value)) elif (key == "tileheight"): map.setTileHeight(Int(value)) elif (key == "orientation"): map.setOrientation(orientationFromString(value)) else: map.setProperty(key, value) elif (sectionName == "tilesets"): tilesetsSectionFound = True epos = line.index('=') key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "tileset"): _list = value.split(',') absoluteSource = _list[0] if (QDir.isRelativePath(absoluteSource)): absoluteSource = path + '/' + absoluteSource tilesetwidth = 0 tilesetheight = 0 if len(_list) > 2: tilesetwidth = Int(_list[1]) tilesetheight = Int(_list[2]) tileset = Tileset.create(QFileInfo(absoluteSource).fileName(), tilesetwidth, tilesetheight) ok = tileset.loadFromImage(absoluteSource) if not ok: self.mError = self.tr("Error loading tileset %s, which expands to %s. Path not found!"%(_list[0], absoluteSource)) return None else : if len(_list) > 4: tileset.setTileOffset(QPoint(Int(_list[3]),Int(_list[4]))) gidMapper.insert(gid, tileset) if len(_list) > 5: gid += Int(_list[5]) else : gid += tileset.tileCount() map.addTileset(tileset) elif (sectionName == "layer"): if (not tilesetsSectionFound): self.mError = self.tr("No tilesets section found before layer section.") return None tilelayerSectionFound = True epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "type"): tilelayer = TileLayer(value, 0, 0, map.width(),map.height()) map.addLayer(tilelayer) elif (key == "format"): if (value == "dec"): base = 10 elif (value == "hex"): base = 16 elif (key == "data"): for y in range(map.height()): line = stream.readLine() l = line.split(',') for x in range(min(map.width(), len(l))): ok = False tileid = int(l[x], base) c, ok = gidMapper.gidToCell(tileid) if (not ok): self.mError += self.tr("Error mapping tile id %1.").arg(tileid) return None tilelayer.setCell(x, y, c) else : tilelayer.setProperty(key, value) else : if (newsection): if (map.indexOfLayer(sectionName) == -1): objectgroup = ObjectGroup(sectionName, 0,0,map.width(), map.height()) map.addLayer(objectgroup) else : objectgroup = map.layerAt(map.indexOfLayer(sectionName)) mapobject = MapObject() objectgroup.addObject(mapobject) newsection = False if (not mapobject): continue if (startsWith == '#'): name = line[1].strip() mapobject.setName(name) epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "type"): mapobject.setType(value) elif (key == "location"): loc = value.split(',') x,y = 0.0, 0.0 w,h = 0, 0 if (map.orientation() == Map.Orthogonal): x = loc[0].toFloat()*map.tileWidth() y = loc[1].toFloat()*map.tileHeight() if len(loc) > 3: w = Int(loc[2])*map.tileWidth() h = Int(loc[3])*map.tileHeight() else : w = map.tileWidth() h = map.tileHeight() else : x = loc[0].toFloat()*map.tileHeight() y = loc[1].toFloat()*map.tileHeight() if len(loc) > 3: w = Int(loc[2])*map.tileHeight() h = Int(loc[3])*map.tileHeight() else : w = h = map.tileHeight() mapobject.setPosition(QPointF(x, y)) mapobject.setSize(w, h) else : mapobject.setProperty(key, value) if (not headerSectionFound or not tilesetsSectionFound or not tilelayerSectionFound): self.mError = self.tr("This seems to be no valid flare map. " "A Flare map consists of at least a header " "section, a tileset section and one tile layer.") return None return map
class LuaPlugin(WritableMapFormat): def __init__(self): super().__init__() self.mMapDir = QDir() self.mError = '' self.mGidMapper = GidMapper() # MapWriterInterface def write(self, map, fileName): file = QSaveFile(fileName) if (not file.open(QIODevice.WriteOnly | QIODevice.Text)) : self.mError = self.tr("Could not open file for writing.") return False self.mMapDir = QDir(QFileInfo(fileName).path()) writer = LuaTableWriter(file) writer.writeStartDocument() self.writeMap(writer, map) writer.writeEndDocument() if (file.error() != QFile.NoError) : self.mError = file.errorString() return False if (not file.commit()) : self.mError = file.errorString() return False return True def nameFilter(self): return self.tr("Lua files (*.lua)") def errorString(self): return self.mError def writeMap(self, writer, map): writer.writeStartReturnTable() writer.writeKeyAndValue("version", "1.1") writer.writeKeyAndValue("luaversion", "5.1") writer.writeKeyAndValue("tiledversion", QCoreApplication.applicationVersion()) orientation = orientationToString(map.orientation()) renderOrder = renderOrderToString(map.renderOrder()) writer.writeKeyAndValue("orientation", orientation) writer.writeKeyAndValue("renderorder", renderOrder) writer.writeKeyAndValue("width", map.width()) writer.writeKeyAndValue("height", map.height()) writer.writeKeyAndValue("tilewidth", map.tileWidth()) writer.writeKeyAndValue("tileheight", map.tileHeight()) writer.writeKeyAndValue("nextobjectid", map.nextObjectId()) if (map.orientation() == Map.Orientation.Hexagonal): writer.writeKeyAndValue("hexsidelength", map.hexSideLength()) if (map.orientation() == Map.Orientation.Staggered or map.orientation() == Map.Orientation.Hexagonal) : writer.writeKeyAndValue("staggeraxis", staggerAxisToString(map.staggerAxis())) writer.writeKeyAndValue("staggerindex", staggerIndexToString(map.staggerIndex())) backgroundColor = map.backgroundColor() if (backgroundColor.isValid()) : # Example: backgroundcolor = { 255, 200, 100 } writer.writeStartTable("backgroundcolor") writer.setSuppressNewlines(True) writer.writeValue(backgroundColor.red()) writer.writeValue(backgroundColor.green()) writer.writeValue(backgroundColor.blue()) if (backgroundColor.alpha() != 255): writer.writeValue(backgroundColor.alpha()) writer.writeEndTable() writer.setSuppressNewlines(False) self.writeProperties(writer, map.properties()) writer.writeStartTable("tilesets") self.mGidMapper.clear() firstGid = 1 for tileset in map.tilesets(): self.writeTileset(writer, tileset, firstGid) self.mGidMapper.insert(firstGid, tileset) firstGid += tileset.tileCount() writer.writeEndTable() writer.writeStartTable("layers") for layer in map.layers(): x = layer.layerType() if x==Layer.TileLayerType: self.writeTileLayer(writer, layer, map.layerDataFormat()) elif x==Layer.ObjectGroupType: self.writeObjectGroup(writer, layer) elif x==Layer.ImageLayerType: self.writeImageLayer(writer, layer) writer.writeEndTable() writer.writeEndTable() def writeProperties(self, writer, properties): writer.writeStartTable("properties") for it in properties: writer.writeQuotedKeyAndValue(it[0], it[1]) writer.writeEndTable() def writeTileset(self, writer, tileset, firstGid): writer.writeStartTable() writer.writeKeyAndValue("name", tileset.name()) writer.writeKeyAndValue("firstgid", firstGid) if tileset.fileName() != '': rel = self.mMapDir.relativeFilePath(tileset.fileName()) writer.writeKeyAndValue("filename", rel) ## # Include all tileset information even for external tilesets, since the # external reference is generally a .tsx file (in XML format). ## writer.writeKeyAndValue("tilewidth", tileset.tileWidth()) writer.writeKeyAndValue("tileheight", tileset.tileHeight()) writer.writeKeyAndValue("spacing", tileset.tileSpacing()) writer.writeKeyAndValue("margin", tileset.margin()) if tileset.imageSource() != '': rel = self.mMapDir.relativeFilePath(tileset.imageSource()) writer.writeKeyAndValue("image", rel) writer.writeKeyAndValue("imagewidth", tileset.imageWidth()) writer.writeKeyAndValue("imageheight", tileset.imageHeight()) if (tileset.transparentColor().isValid()): writer.writeKeyAndValue("transparentcolor",tileset.transparentColor().name()) offset = tileset.tileOffset() writer.writeStartTable("tileoffset") writer.writeKeyAndValue("x", offset.x()) writer.writeKeyAndValue("y", offset.y()) writer.writeEndTable() self.writeProperties(writer, tileset.properties()) writer.writeStartTable("terrains") for i in range(tileset.terrainCount()): t = tileset.terrain(i) writer.writeStartTable() writer.writeKeyAndValue("name", t.name()) writer.writeKeyAndValue("tile", t.imageTileId()) self.writeProperties(writer, t.properties()) writer.writeEndTable() writer.writeEndTable() writer.writeKeyAndValue("tilecount", tileset.tileCount()) writer.writeStartTable("tiles") for i in range(0, tileset.tileCount()): tile = tileset.tileAt(i) # For brevity only write tiles with interesting properties if (not includeTile(tile)): continue writer.writeStartTable() writer.writeKeyAndValue("id", i) if (not tile.properties().isEmpty()): self.writeProperties(writer, tile.properties()) if tile.imageSource() != '': src = self.mMapDir.relativeFilePath(tile.imageSource()) tileSize = tile.size() writer.writeKeyAndValue("image", src) if (not tileSize.isNull()) : writer.writeKeyAndValue("width", tileSize.width()) writer.writeKeyAndValue("height", tileSize.height()) terrain = tile.terrain() if (terrain != 0xFFFFFFFF) : writer.writeStartTable("terrain") writer.setSuppressNewlines(True) for i in range(0, 4): writer.writeValue(tile.cornerTerrainId(i)) writer.writeEndTable() writer.setSuppressNewlines(False) if (tile.probability() != 1.0): writer.writeKeyAndValue("probability", tile.probability()) objectGroup = tile.objectGroup() if objectGroup: self.writeObjectGroup(writer, objectGroup, "objectGroup") if (tile.isAnimated()) : frames = tile.frames() writer.writeStartTable("animation") for frame in frames: writer.writeStartTable() writer.writeKeyAndValue("tileid", QString.number(frame.tileId)) writer.writeKeyAndValue("duration", QString.number(frame.duration)) writer.writeEndTable() writer.writeEndTable() # animation writer.writeEndTable() # tile writer.writeEndTable() # tiles writer.writeEndTable() # tileset def writeTileLayer(self, writer, tileLayer, format): writer.writeStartTable() writer.writeKeyAndValue("type", "tilelayer") writer.writeKeyAndValue("name", tileLayer.name()) writer.writeKeyAndValue("x", tileLayer.x()) writer.writeKeyAndValue("y", tileLayer.y()) writer.writeKeyAndValue("width", tileLayer.width()) writer.writeKeyAndValue("height", tileLayer.height()) writer.writeKeyAndValue("visible", tileLayer.isVisible()) writer.writeKeyAndValue("opacity", tileLayer.opacity()) offset = tileLayer.offset() writer.writeKeyAndValue("offsetx", offset.x()) writer.writeKeyAndValue("offsety", offset.y()) if format==Map.LayerDataFormat.XML or format==Map.LayerDataFormat.CSV: self.writeProperties(writer, tileLayer.properties()) writer.writeKeyAndValue("encoding", "lua") writer.writeStartTable("data") for y in range(0, tileLayer.height()): if (y > 0): writer.prepareNewLine() for x in range(0, tileLayer.width()): writer.writeValue(self.mGidMapper.cellToGid(tileLayer.cellAt(x, y))) elif format==Map.LayerDataFormat.Base64 \ or format==Map.LayerDataFormat.Base64Zlib \ or format==Map.LayerDataFormat.Base64Gzip: writer.writeKeyAndValue("encoding", "base64") if format == Map.LayerDataFormat.Base64Zlib: writer.writeKeyAndValue("compression", "zlib") elif format == Map.LayerDataFormat.Base64Gzip: writer.writeKeyAndValue("compression", "gzip") layerData = self.mGidMapper.encodeLayerData(tileLayer, format) writer.writeKeyAndValue("data", layerData) writer.writeEndTable() writer.writeEndTable() def writeObjectGroup(self, writer, objectGroup, key=QByteArray()): if key=='': writer.writeStartTable() else: writer.writeStartTable(key) writer.writeKeyAndValue("type", "objectgroup") writer.writeKeyAndValue("name", objectGroup.name()) writer.writeKeyAndValue("visible", objectGroup.isVisible()) writer.writeKeyAndValue("opacity", objectGroup.opacity()) offset = objectGroup.offset() writer.writeKeyAndValue("offsetx", offset.x()) writer.writeKeyAndValue("offsety", offset.y()) self.writeProperties(writer, objectGroup.properties()) writer.writeStartTable("objects") for mapObject in objectGroup.objects(): self.writeMapObject(writer, mapObject) writer.writeEndTable() writer.writeEndTable() def writeImageLayer(self, writer, imageLayer): writer.writeStartTable() writer.writeKeyAndValue("type", "imagelayer") writer.writeKeyAndValue("name", imageLayer.name()) writer.writeKeyAndValue("x", imageLayer.x()) writer.writeKeyAndValue("y", imageLayer.y()) writer.writeKeyAndValue("visible", imageLayer.isVisible()) writer.writeKeyAndValue("opacity", imageLayer.opacity()) rel = self.mMapDir.relativeFilePath(imageLayer.imageSource()) writer.writeKeyAndValue("image", rel) if (imageLayer.transparentColor().isValid()) : writer.writeKeyAndValue("transparentcolor", imageLayer.transparentColor().name()) self.writeProperties(writer, imageLayer.properties()) writer.writeEndTable() def writeMapObject(self, writer, mapObject): writer.writeStartTable() writer.writeKeyAndValue("id", mapObject.id()) writer.writeKeyAndValue("name", mapObject.name()) writer.writeKeyAndValue("type", mapObject.type()) writer.writeKeyAndValue("shape", toString(mapObject.shape())) writer.writeKeyAndValue("x", mapObject.x()) writer.writeKeyAndValue("y", mapObject.y()) writer.writeKeyAndValue("width", mapObject.width()) writer.writeKeyAndValue("height", mapObject.height()) writer.writeKeyAndValue("rotation", mapObject.rotation()) if (not mapObject.cell().isEmpty()): writer.writeKeyAndValue("gid", self.mGidMapper.cellToGid(mapObject.cell())) writer.writeKeyAndValue("visible", mapObject.isVisible()) polygon = mapObject.polygon() if (not polygon.isEmpty()) : if (mapObject.shape() == MapObject.Polygon): writer.writeStartTable("polygon") else: writer.writeStartTable("polyline") #if defined(POLYGON_FORMAT_FULL) ## This format is the easiest to read and understand: # # : # { x = 1, y = 1 }, # { x = 2, y = 2 }, # { x = 3, y = 3 }, # ... # } ## for point in polygon: writer.writeStartTable() writer.setSuppressNewlines(True) writer.writeKeyAndValue("x", point.x()) writer.writeKeyAndValue("y", point.y()) writer.writeEndTable() writer.setSuppressNewlines(False) #elif defined(POLYGON_FORMAT_PAIRS) ## This is an alternative that takes about 25% less memory. # # : # { 1, 1 }, # { 2, 2 }, # { 3, 3 }, # ... # } ## for point in polygon: writer.writeStartTable() writer.setSuppressNewlines(True) writer.writeValue(point.x()) writer.writeValue(point.y()) writer.writeEndTable() writer.setSuppressNewlines(False) #elif defined(POLYGON_FORMAT_OPTIMAL) ## Writing it out in two tables, one for the x coordinates and one for # the y coordinates. It is a compromise between code readability and # performance. This takes the least amount of memory (60% less than # the first approach). # # x = { 1, 2, 3, ... } # y = { 1, 2, 3, ... } ## writer.writeStartTable("x") writer.setSuppressNewlines(True) for point in polygon: writer.writeValue(point.x()) writer.writeEndTable() writer.setSuppressNewlines(False) writer.writeStartTable("y") writer.setSuppressNewlines(True) for point in polygon: writer.writeValue(point.y()) writer.writeEndTable() writer.setSuppressNewlines(False) #endif writer.writeEndTable() self.writeProperties(writer, mapObject.properties()) writer.writeEndTable()
class MapWriterPrivate(): def tr(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapWriter', sourceText, disambiguation, n) def trUtf8(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapWriter', sourceText, disambiguation, n) def __init__(self): self.mLayerDataFormat = Map.LayerDataFormat.Base64Zlib self.mDtdEnabled = False self.mUseAbsolutePaths = False self.mMapDir = QDir() self.mGidMapper = GidMapper() self.mError = '' def writeMap(self, map, device, path): self.mMapDir = QDir(path) self.mUseAbsolutePaths = path=='' self.mLayerDataFormat = map.layerDataFormat() writer = createWriter(device) writer.writeStartDocument() if (self.mDtdEnabled): writer.writeDTD("<not DOCTYPE map SYSTEM \"http://mapeditor.org/dtd/1.0/map.dtd\">") self.__writeMap(writer, map) writer.writeEndDocument() del writer def writeTileset(self, tileset, device, path): self.mMapDir = QDir(path) self.mUseAbsolutePaths = path=='' writer = createWriter(device) writer.writeStartDocument() if (self.mDtdEnabled): writer.writeDTD("<not DOCTYPE tileset SYSTEM \"http://mapeditor.org/dtd/1.0/map.dtd\">") self.__writeTileset(writer, tileset, 0) writer.writeEndDocument() del writer def openFile(self, file): if (not file.open(QIODevice.WriteOnly | QIODevice.Text)): self.mError = self.tr("Could not open file for writing.") return False return True def __writeMap(self, w, map): w.writeStartElement("map") orientation = orientationToString(map.orientation()) renderOrder = renderOrderToString(map.renderOrder()) w.writeAttribute("version", "1.0") w.writeAttribute("orientation", orientation) w.writeAttribute("renderorder", renderOrder) w.writeAttribute("width", str(map.width())) w.writeAttribute("height", str(map.height())) w.writeAttribute("tilewidth", str(map.tileWidth())) w.writeAttribute("tileheight", str(map.tileHeight())) if (map.orientation() == Map.Orientation.Hexagonal): w.writeAttribute("hexsidelength", str(map.hexSideLength())) if (map.orientation() == Map.Orientation.Staggered or map.orientation() == Map.Orientation.Hexagonal): w.writeAttribute("staggeraxis", staggerAxisToString(map.staggerAxis())) w.writeAttribute("staggerindex", staggerIndexToString(map.staggerIndex())) if (map.backgroundColor().isValid()): w.writeAttribute("backgroundcolor", map.backgroundColor().name()) w.writeAttribute("nextobjectid", str(map.nextObjectId())) self.__writeProperties(w, map.properties()) self.mGidMapper.clear() firstGid = 1 for tileset in map.tilesets(): self.__writeTileset(w, tileset, firstGid) self.mGidMapper.insert(firstGid, tileset) firstGid += tileset.tileCount() for layer in map.layers(): type = layer.layerType() if (type == Layer.TileLayerType): self.__writeTileLayer(w, layer) elif (type == Layer.ObjectGroupType): self.__writeObjectGroup(w, layer) elif (type == Layer.ImageLayerType): self.__writeImageLayer(w, layer) w.writeEndElement() def __writeTileset(self, w, tileset, firstGid): w.writeStartElement("tileset") if (firstGid > 0): w.writeAttribute("firstgid", str(firstGid)) fileName = tileset.fileName() if fileName != '': source = fileName if (not self.mUseAbsolutePaths): source = self.mMapDir.relativeFilePath(source) w.writeAttribute("source", source) # Tileset is external, so no need to write any of the stuff below w.writeEndElement() return w.writeAttribute("name", tileset.name()) w.writeAttribute("tilewidth", str(tileset.tileWidth())) w.writeAttribute("tileheight", str(tileset.tileHeight())) tileSpacing = tileset.tileSpacing() margin = tileset.margin() if (tileSpacing != 0): w.writeAttribute("spacing", str(tileSpacing)) if (margin != 0): w.writeAttribute("margin", str(margin)) w.writeAttribute("tilecount", str(tileset.tileCount())) offset = tileset.tileOffset() if (not offset.isNull()): w.writeStartElement("tileoffset") w.writeAttribute("x", str(offset.x())) w.writeAttribute("y", str(offset.y())) w.writeEndElement() # Write the tileset properties self.__writeProperties(w, tileset.properties()) # Write the image element imageSource = tileset.imageSource() if imageSource != '': w.writeStartElement("image") source = imageSource if (not self.mUseAbsolutePaths): source = self.mMapDir.relativeFilePath(source) w.writeAttribute("source", source) transColor = tileset.transparentColor() if (transColor.isValid()): w.writeAttribute("trans", transColor.name()[1]) if (tileset.imageWidth() > 0): w.writeAttribute("width", str(tileset.imageWidth())) if (tileset.imageHeight() > 0): w.writeAttribute("height", str(tileset.imageHeight())) w.writeEndElement() # Write the terrain types if (tileset.terrainCount() > 0): w.writeStartElement("terraintypes") for i in range(tileset.terrainCount()): t = tileset.terrain(i) w.writeStartElement("terrain") w.writeAttribute("name", t.name()) w.writeAttribute("tile", str(t.imageTileId())) self.__writeProperties(w, t.properties()) w.writeEndElement() w.writeEndElement() # Write the properties for those tiles that have them for i in range(tileset.tileCount()): tile = tileset.tileAt(i) properties = tile.properties() terrain = tile.terrain() probability = tile.probability() objectGroup = tile.objectGroup() if (not properties.isEmpty() or terrain != 0xFFFFFFFF or probability != 1.0 or imageSource=='' or objectGroup or tile.isAnimated()): w.writeStartElement("tile") w.writeAttribute("id", str(i)) if (terrain != 0xFFFFFFFF): w.writeAttribute("terrain", makeTerrainAttribute(tile)) if (probability != 1.0): w.writeAttribute("probability", str(probability)) if (not properties.isEmpty()): self.__writeProperties(w, properties) if imageSource=='': w.writeStartElement("image") tileSize = tile.size() if (not tileSize.isNull()): w.writeAttribute("width", str(tileSize.width())) w.writeAttribute("height", str(tileSize.height())) if (tile.imageSource()==''): w.writeAttribute("format", "png") w.writeStartElement("data") w.writeAttribute("encoding", "base64") buffer = QBuffer() tile.image().save(buffer, "png") w.writeCharacters(buffer.data().toBase64()) w.writeEndElement() # else: source = tile.imageSource() if (not self.mUseAbsolutePaths): source = self.mMapDir.relativeFilePath(source) w.writeAttribute("source", source) w.writeEndElement() # if (objectGroup): self.__writeObjectGroup(w, objectGroup) if (tile.isAnimated()): frames = tile.frames() w.writeStartElement("animation") for frame in frames: w.writeStartElement("frame") w.writeAttribute("tileid", str(frame.tileId)) w.writeAttribute("duration", str(frame.duration)) w.writeEndElement() # w.writeEndElement() # w.writeEndElement() # w.writeEndElement() def __writeTileLayer(self, w, tileLayer): w.writeStartElement("layer") self.__writeLayerAttributes(w, tileLayer) self.__writeProperties(w, tileLayer.properties()) encoding = QString() compression = QString() if (self.mLayerDataFormat == Map.LayerDataFormat.Base64 or self.mLayerDataFormat == Map.LayerDataFormat.Base64Gzip or self.mLayerDataFormat == Map.LayerDataFormat.Base64Zlib): encoding = "base64" if (self.mLayerDataFormat == Map.LayerDataFormat.Base64Gzip): compression = "gzip" elif (self.mLayerDataFormat == Map.LayerDataFormat.Base64Zlib): compression = "zlib" elif (self.mLayerDataFormat == Map.LayerDataFormat.CSV): encoding = "csv" w.writeStartElement("data") if encoding != '': w.writeAttribute("encoding", encoding) if compression != '': w.writeAttribute("compression", compression) if (self.mLayerDataFormat == Map.LayerDataFormat.XML): for y in range(tileLayer.height()): for x in range(tileLayer.width()): gid = self.mGidMapper.cellToGid(tileLayer.cellAt(x, y)) w.writeStartElement("tile") w.writeAttribute("gid", str(gid)) w.writeEndElement() elif (self.mLayerDataFormat == Map.LayerDataFormat.CSV): tileData = '' for y in range(tileLayer.height()): for x in range(tileLayer.width()): gid = self.mGidMapper.cellToGid(tileLayer.cellAt(x, y)) tileData += str(gid) if (x != tileLayer.width() - 1 or y != tileLayer.height() - 1): tileData += "," tileData += "\n" w.writeCharacters("\n") w.writeCharacters(tileData) else: tileData = self.mGidMapper.encodeLayerData(tileLayer, self.mLayerDataFormat) w.writeCharacters("\n ") w.writeCharacters(tileData.data().decode()) w.writeCharacters("\n ") w.writeEndElement() # </data> w.writeEndElement() # </layer> def __writeLayerAttributes(self, w, layer): if layer.name() != '': w.writeAttribute("name", layer.name()) x = layer.x() y = layer.y() opacity = layer.opacity() if (x != 0): w.writeAttribute("x", str(x)) if (y != 0): w.writeAttribute("y", str(y)) if (layer.layerType() == Layer.TileLayerType): w.writeAttribute("width", str(layer.width())) w.writeAttribute("height", str(layer.height())) if (not layer.isVisible()): w.writeAttribute("visible", "0") if (opacity != 1.0): w.writeAttribute("opacity", str(opacity)) offset = layer.offset() if (not offset.isNull()): w.writeAttribute("offsetx", str(offset.x())) w.writeAttribute("offsety", str(offset.y())) def __writeObjectGroup(self, w, objectGroup): w.writeStartElement("objectgroup") if (objectGroup.color().isValid()): w.writeAttribute("color", objectGroup.color().name()) if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.TopDownOrder): w.writeAttribute("draworder", drawOrderToString(objectGroup.drawOrder())) self.__writeLayerAttributes(w, objectGroup) self.__writeProperties(w, objectGroup.properties()) for mapObject in objectGroup.objects(): self.__writeObject(w, mapObject) w.writeEndElement() def __writeObject(self, w, mapObject): w.writeStartElement("object") w.writeAttribute("id", str(mapObject.id())) name = mapObject.name() type = mapObject.type() if name != '': w.writeAttribute("name", name) if type != '': w.writeAttribute("type", type) if (not mapObject.cell().isEmpty()): gid = self.mGidMapper.cellToGid(mapObject.cell()) w.writeAttribute("gid", str(gid)) pos = mapObject.position() size = mapObject.size() w.writeAttribute("x", str(pos.x())) w.writeAttribute("y", str(pos.y())) if (size.width() != 0): w.writeAttribute("width", str(size.width())) if (size.height() != 0): w.writeAttribute("height", str(size.height())) rotation = mapObject.rotation() if (rotation != 0.0): w.writeAttribute("rotation", str(rotation)) if (not mapObject.isVisible()): w.writeAttribute("visible"), "0" self.__writeProperties(w, mapObject.properties()) polygon = mapObject.polygon() if (not polygon.isEmpty()): if (mapObject.shape() == MapObject.Polygon): w.writeStartElement("polygon") else: w.writeStartElement("polyline") points = '' for point in polygon: points += str(point.x()) + ',' + str(point.y()) + ' ' points = points[:-1] w.writeAttribute("points", points) w.writeEndElement() if (mapObject.shape() == MapObject.Ellipse): w.writeEmptyElement("ellipse") w.writeEndElement() def __writeImageLayer(self, w, imageLayer): w.writeStartElement("imagelayer") self.__writeLayerAttributes(w, imageLayer) # Write the image element imageSource = imageLayer.imageSource() if imageSource != '': w.writeStartElement("image") source = imageSource if (not self.mUseAbsolutePaths): source = self.mMapDir.relativeFilePath(source) w.writeAttribute("source", source) transColor = imageLayer.transparentColor() if (transColor.isValid()): w.writeAttribute("trans", transColor.name()[1]) w.writeEndElement() self.__writeProperties(w, imageLayer.properties()) w.writeEndElement() def __writeProperties(self, w, properties): if (properties.isEmpty()): return w.writeStartElement("properties") for it in properties: w.writeStartElement("property") w.writeAttribute("name", it[0]) value = str(it[1]) if value.__contains__('\n'): w.writeCharacters(value) else: w.writeAttribute("value", value) w.writeEndElement() w.writeEndElement()
def read(self, fileName): file = QFile(fileName) if (not file.open(QIODevice.ReadOnly)): self.mError = self.tr("Could not open file for reading.") return None # default to values of the original flare alpha game. map = Map(Map.Orientation.Isometric, 256, 256, 64, 32) stream = QTextStream(file) line = QString() sectionName = QString() newsection = False path = QFileInfo(file).absolutePath() base = 10 gidMapper = GidMapper() gid = 1 tilelayer = None objectgroup = None mapobject = None tilesetsSectionFound = False headerSectionFound = False tilelayerSectionFound = False # tile layer or objects while (not stream.atEnd()): line = stream.readLine() if line == '': continue startsWith = line[0] if (startsWith == '['): sectionName = line[1:line.index(']')] newsection = True continue if (sectionName == "header"): headerSectionFound = True #get map properties epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "width"): map.setWidth(Int(value)) elif (key == "height"): map.setHeight(Int(value)) elif (key == "tilewidth"): map.setTileWidth(Int(value)) elif (key == "tileheight"): map.setTileHeight(Int(value)) elif (key == "orientation"): map.setOrientation(orientationFromString(value)) else: map.setProperty(key, value) elif (sectionName == "tilesets"): tilesetsSectionFound = True epos = line.index('=') key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "tileset"): _list = value.split(',') absoluteSource = _list[0] if (QDir.isRelativePath(absoluteSource)): absoluteSource = path + '/' + absoluteSource tilesetwidth = 0 tilesetheight = 0 if len(_list) > 2: tilesetwidth = Int(_list[1]) tilesetheight = Int(_list[2]) tileset = Tileset.create( QFileInfo(absoluteSource).fileName(), tilesetwidth, tilesetheight) ok = tileset.loadFromImage(absoluteSource) if not ok: self.mError = self.tr( "Error loading tileset %s, which expands to %s. Path not found!" % (_list[0], absoluteSource)) return None else: if len(_list) > 4: tileset.setTileOffset( QPoint(Int(_list[3]), Int(_list[4]))) gidMapper.insert(gid, tileset) if len(_list) > 5: gid += Int(_list[5]) else: gid += tileset.tileCount() map.addTileset(tileset) elif (sectionName == "layer"): if (not tilesetsSectionFound): self.mError = self.tr( "No tilesets section found before layer section.") return None tilelayerSectionFound = True epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "type"): tilelayer = TileLayer(value, 0, 0, map.width(), map.height()) map.addLayer(tilelayer) elif (key == "format"): if (value == "dec"): base = 10 elif (value == "hex"): base = 16 elif (key == "data"): for y in range(map.height()): line = stream.readLine() l = line.split(',') for x in range(min(map.width(), len(l))): ok = False tileid = int(l[x], base) c, ok = gidMapper.gidToCell(tileid) if (not ok): self.mError += self.tr( "Error mapping tile id %1.").arg( tileid) return None tilelayer.setCell(x, y, c) else: tilelayer.setProperty(key, value) else: if (newsection): if (map.indexOfLayer(sectionName) == -1): objectgroup = ObjectGroup(sectionName, 0, 0, map.width(), map.height()) map.addLayer(objectgroup) else: objectgroup = map.layerAt( map.indexOfLayer(sectionName)) mapobject = MapObject() objectgroup.addObject(mapobject) newsection = False if (not mapobject): continue if (startsWith == '#'): name = line[1].strip() mapobject.setName(name) epos = line.index('=') if (epos != -1): key = line[:epos].strip() value = line[epos + 1:].strip() if (key == "type"): mapobject.setType(value) elif (key == "location"): loc = value.split(',') x, y = 0.0, 0.0 w, h = 0, 0 if (map.orientation() == Map.Orthogonal): x = loc[0].toFloat() * map.tileWidth() y = loc[1].toFloat() * map.tileHeight() if len(loc) > 3: w = Int(loc[2]) * map.tileWidth() h = Int(loc[3]) * map.tileHeight() else: w = map.tileWidth() h = map.tileHeight() else: x = loc[0].toFloat() * map.tileHeight() y = loc[1].toFloat() * map.tileHeight() if len(loc) > 3: w = Int(loc[2]) * map.tileHeight() h = Int(loc[3]) * map.tileHeight() else: w = h = map.tileHeight() mapobject.setPosition(QPointF(x, y)) mapobject.setSize(w, h) else: mapobject.setProperty(key, value) if (not headerSectionFound or not tilesetsSectionFound or not tilelayerSectionFound): self.mError = self.tr( "This seems to be no valid flare map. " "A Flare map consists of at least a header " "section, a tileset section and one tile layer.") return None return map
def write(self, map, fileName): file = QSaveFile(fileName) if (not file.open(QFile.WriteOnly | QFile.Text)): self.mError = self.tr("Could not open file for writing.") return False out = QTextStream(file) out.setCodec("UTF-8") mapWidth = map.width() mapHeight = map.height() # write [header] out << "[header]\n" out << "width=" << str(mapWidth) << "\n" out << "height=" << str(mapHeight) << "\n" out << "tilewidth=" << str(map.tileWidth()) << "\n" out << "tileheight=" << str(map.tileHeight()) << "\n" out << "orientation=" << str(orientationToString( map.orientation())) << "\n" # write all properties for this map for it in map.properties().__iter__(): out << it[0] << "=" << it[1] << "\n" out << "\n" mapDir = QFileInfo(fileName).absoluteDir() out << "[tilesets]\n" for tileset in map.tilesets(): imageSource = tileset.imageSource() source = mapDir.relativeFilePath(imageSource) out << "tileset=" << source \ << "," << str(tileset.tileWidth()) \ << "," << str(tileset.tileHeight()) \ << "," << str(tileset.tileOffset().x()) \ << "," << str(tileset.tileOffset().y()) \ << "\n" out << "\n" gidMapper = GidMapper(map.tilesets()) # write layers for layer in map.layers(): tileLayer = layer.asTileLayer() if tileLayer: out << "[layer]\n" out << "type=" << layer.name() << "\n" out << "data=\n" for y in range(0, mapHeight): for x in range(0, mapWidth): t = tileLayer.cellAt(x, y) id = 0 if (t.tile): id = gidMapper.cellToGid(t) out << id if (x < mapWidth - 1): out << "," if (y < mapHeight - 1): out << "," out << "\n" out << "\n" group = layer.asObjectGroup() if group: for o in group.objects(): if o.type() != '': out << "[" << group.name() << "]\n" # display object name as comment if o.name() != '': out << "# " << o.name() << "\n" out << "type=" << o.type() << "\n" x, y, w, h = 0 if (map.orientation() == Map.Orthogonal): x = o.x() / map.tileWidth() y = o.y() / map.tileHeight() w = o.width() / map.tileWidth() h = o.height() / map.tileHeight() else: x = o.x() / map.tileHeight() y = o.y() / map.tileHeight() w = o.width() / map.tileHeight() h = o.height() / map.tileHeight() out << "location=" << x << "," << y out << "," << w << "," << h << "\n" # write all properties for this object for it in o.properties().__iter__(): out << it[0] << "=" << it[1] << "\n" out << "\n" if not file.commit(): self.mError = file.errorString() return False return True
class VariantToMapConverter(): def __init__(self): self.mError = '' self.mGidMapper = GidMapper() self.mMap = None self.mMapDir = QDir() self.mReadingExternalTileset = False # Using the MapReader context since the messages are the same def tr(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def trUtf8(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) ## # Tries to convert the given \a variant to a Map instance. The \a mapDir # is necessary to resolve any relative references to external images. # # Returns 0 in case of an error. The error can be obstained using # self.errorString(). ## def toMap(self, variant, mapDir): self.mGidMapper.clear() self.mMapDir = mapDir variantMap = variant orientationString = variantMap.get("orientation", '') orientation = orientationFromString(orientationString) if (orientation == Map.Orientation.Unknown): self.mError = self.tr("Unsupported map orientation: \"%s\""%orientationString) return None staggerAxisString = variantMap.get("staggeraxis", '') staggerAxis = staggerAxisFromString(staggerAxisString) staggerIndexString = variantMap.get("staggerindex", '') staggerIndex = staggerIndexFromString(staggerIndexString) renderOrderString = variantMap.get("renderorder", '') renderOrder = renderOrderFromString(renderOrderString) nextObjectId = variantMap.get("nextobjectid", 0) map = Map(orientation, variantMap.get("width",0), variantMap.get("height",0), variantMap.get("tilewidth",0), variantMap.get("tileheight",0)) map.setHexSideLength(variantMap.get("hexsidelength", 0)) map.setStaggerAxis(staggerAxis) map.setStaggerIndex(staggerIndex) map.setRenderOrder(renderOrder) if (nextObjectId): map.setNextObjectId(nextObjectId) self.mMap = map map.setProperties(self.toProperties(variantMap.get("properties", {}))) bgColor = variantMap.get("backgroundcolor", '') if (bgColor!='' and QColor.isValidColor(bgColor)): map.setBackgroundColor(QColor(bgColor)) for tilesetVariant in variantMap.get("tilesets", []): tileset = self.__toTileset(tilesetVariant) if not tileset: return None map.addTileset(tileset) for layerVariant in variantMap.get("layers", []): layer = self.toLayer(layerVariant) if not layer: return None map.addLayer(layer) return map ## # Returns the last error, if any. ## def errorString(self): return self.mError def toProperties(self, variant): variantMap = variant properties = Properties() for it in variantMap.items(): properties[it[0]] = it[1] return properties def toTileset(self, variant, directory): self.mMapDir = directory self.mReadingExternalTileset = True tileset = self.__toTileset(variant) self.mReadingExternalTileset = False return tileset def __toTileset(self, variant): variantMap = variant firstGid = variantMap.get("firstgid",0) # Handle external tilesets sourceVariant = variantMap.get("source", '') if sourceVariant != '': source = resolvePath(self.mMapDir, sourceVariant) tileset, error = readTileset(source) if not tileset: self.mError = self.tr("Error while loading tileset '%s': %s"%(source, error)) else: self.mGidMapper.insert(firstGid, tileset) return tileset name = variantMap.get("name",'') tileWidth = variantMap.get("tilewidth",0) tileHeight = variantMap.get("tileheight",0) spacing = variantMap.get("spacing",0) margin = variantMap.get("margin",0) tileOffset = variantMap.get("tileoffset", {}) tileOffsetX = tileOffset.get("x",0) tileOffsetY = tileOffset.get("y",0) if (tileWidth <= 0 or tileHeight <= 0 or (firstGid == 0 and not self.mReadingExternalTileset)): self.mError = self.tr("Invalid tileset parameters for tileset '%s'"%name) return None tileset = Tileset.create(name, tileWidth, tileHeight, spacing, margin) tileset.setTileOffset(QPoint(tileOffsetX, tileOffsetY)) trans = variantMap.get("transparentcolor", '') if (trans!='' and QColor.isValidColor(trans)): tileset.setTransparentColor(QColor(trans)) imageVariant = variantMap.get("image",'') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) if (not tileset.loadFromImage(imagePath)): self.mError = self.tr("Error loading tileset image:\n'%s'"%imagePath) return None tileset.setProperties(self.toProperties(variantMap.get("properties", {}))) # Read terrains terrainsVariantList = variantMap.get("terrains", []) for terrainMap in terrainsVariantList: tileset.addTerrain(terrainMap.get("name", ''), terrainMap.get("tile", 0)) # Read tile terrain and external image information tilesVariantMap = variantMap.get("tiles", {}) for it in tilesVariantMap.items(): ok = False tileIndex = Int(it[0]) if (tileIndex < 0): self.mError = self.tr("Tileset tile index negative:\n'%d'"%tileIndex) if (tileIndex >= tileset.tileCount()): # Extend the tileset to fit the tile if (tileIndex >= len(tilesVariantMap)): # If tiles are defined this way, there should be an entry # for each tile. # Limit the index to number of entries to prevent running out # of memory on malicious input.f self.mError = self.tr("Tileset tile index too high:\n'%d'"%tileIndex) return None for i in range(tileset.tileCount(), tileIndex+1): tileset.addTile(QPixmap()) tile = tileset.tileAt(tileIndex) if (tile): tileVar = it[1] terrains = tileVar.get("terrain", []) if len(terrains) == 4: for i in range(0, 4): terrainId, ok = Int2(terrains[i]) if (ok and terrainId >= 0 and terrainId < tileset.terrainCount()): tile.setCornerTerrainId(i, terrainId) probability, ok = Float2(tileVar.get("probability", 0.0)) if (ok): tile.setProbability(probability) imageVariant = tileVar.get("image",'') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) tileset.setTileImage(tileIndex, QPixmap(imagePath), imagePath) objectGroupVariant = tileVar.get("objectgroup", {}) if len(objectGroupVariant) > 0: tile.setObjectGroup(self.toObjectGroup(objectGroupVariant)) frameList = tileVar.get("animation", []) lenFrames = len(frameList) if lenFrames > 0: frames = QVector() for i in range(lenFrames): frames.append(Frame()) for i in range(lenFrames - 1, -1, -1): frameVariantMap = frameList[i] frame = frames[i] frame.tileId = frameVariantMap.get("tileid", 0) frame.duration = frameVariantMap.get("duration", 0) tile.setFrames(frames) # Read tile properties propertiesVariantMap = variantMap.get("tileproperties", {}) for it in propertiesVariantMap.items(): tileIndex = Int(it[0]) propertiesVar = it[1] if (tileIndex >= 0 and tileIndex < tileset.tileCount()): properties = self.toProperties(propertiesVar) tileset.tileAt(tileIndex).setProperties(properties) if not self.mReadingExternalTileset: self.mGidMapper.insert(firstGid, tileset) return tileset def toLayer(self, variant): variantMap = variant layer = None if (variantMap["type"] == "tilelayer"): layer = self.toTileLayer(variantMap) elif (variantMap["type"] == "objectgroup"): layer = self.toObjectGroup(variantMap) elif (variantMap["type"] == "imagelayer"): layer = self.toImageLayer(variantMap) if (layer): layer.setProperties(self.toProperties(variantMap.get("properties", {}))) offset = QPointF(variantMap.get("offsetx", 0.0), variantMap.get("offsety", 0.0)) layer.setOffset(offset) return layer def toTileLayer(self, variantMap): name = variantMap.get("name",'') width = variantMap.get("width",0) height = variantMap.get("height",0) dataVariant = variantMap["data"] tileLayer = TileLayer(name, variantMap.get("x",0), variantMap.get("y",0), width, height) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) tileLayer.setOpacity(opacity) tileLayer.setVisible(visible) encoding = variantMap.get("encoding", '') compression = variantMap.get("compression", '') if encoding=='' or encoding == "csv": layerDataFormat = Map.LayerDataFormat.CSV elif (encoding == "base64"): if compression=='': layerDataFormat = Map.LayerDataFormat.Base64 elif (compression == "gzip"): layerDataFormat = Map.LayerDataFormat.Base64Gzip elif (compression == "zlib"): layerDataFormat = Map.LayerDataFormat.Base64Zlib else: self.mError = self.tr("Compression method '%s' not supported"%compression) return None else: self.mError = self.tr("Unknown encoding: %1"%encoding) return None self.mMap.setLayerDataFormat(layerDataFormat) if layerDataFormat==Map.LayerDataFormat.XML or layerDataFormat==Map.LayerDataFormat.CSV: dataVariantList = dataVariant if len(dataVariantList) != width * height: self.mError = self.tr("Corrupt layer data for layer '%s'"%name) return None x = 0 y = 0 ok = False if (len(dataVariantList) != width * height): self.mError = self.tr("Corrupt layer data for layer '%s'"%name) return None for gidVariant in dataVariantList: gid, ok = Int2(gidVariant) if (not ok): self.mError = self.tr("Unable to parse tile at (%d,%d) on layer '%s'"%(x, y, tileLayer.name())) return None cell = self.mGidMapper.gidToCell(gid, ok) tileLayer.setCell(x, y, cell) x += 1 if (x >= tileLayer.width()): x = 0 y += 1 elif layerDataFormat==Map.LayerDataFormat.Base64 \ or layerDataFormat==Map.LayerDataFormat.Base64Zlib \ or layerDataFormat==Map.LayerDataFormat.Base64Gzip: data = QByteArray(dataVariant) error = self.mGidMapper.decodeLayerData(tileLayer, data, layerDataFormat) if error==DecodeError.CorruptLayerData: self.mError = self.tr("Corrupt layer data for layer '%s'"%name) return None elif error==DecodeError.TileButNoTilesets: self.mError = self.tr("Tile used but no tilesets specified") return None elif error==DecodeError.InvalidTile: self.mError = self.tr("Invalid tile: %d"%self.mGidMapper.invalidTile()) return None elif error==DecodeError.NoError: pass return tileLayer def toObjectGroup(self, variantMap): objectGroup = ObjectGroup(variantMap.get("name",''), variantMap.get("x",0), variantMap.get("y",0), variantMap.get("width",0), variantMap.get("height",0)) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) objectGroup.setOpacity(opacity) objectGroup.setVisible(visible) objectGroup.setColor(variantMap.get("color", '')) drawOrderString = variantMap.get("draworder", '') if drawOrderString != '': objectGroup.setDrawOrder(drawOrderFromString(drawOrderString)) if (objectGroup.drawOrder() == ObjectGroup.DrawOrder.UnknownOrder): self.mError = self.tr("Invalid draw order: %s"%drawOrderString) return None for objectVariant in variantMap.get("objects", []): objectVariantMap = objectVariant name = objectVariantMap.get("name",'') type = objectVariantMap.get("type", '') id = objectVariantMap.get("id",0.0) gid = objectVariantMap.get("gid",0.0) x = objectVariantMap.get("x",0.0) y = objectVariantMap.get("y",0.0) width = objectVariantMap.get("width",0.0) height = objectVariantMap.get("height",0.0) rotation = objectVariantMap.get("rotation", 0.0) pos = QPointF(x, y) size = QSizeF(width, height) object = MapObject(name, type, pos, size) object.setId(id) object.setRotation(rotation) if (gid): cell, ok = self.mGidMapper.gidToCell(gid) object.setCell(cell) if not object.cell().isEmpty(): tileSize = object.cell().tile.size() if width == 0: object.setWidth(tileSize.width()) if height == 0: object.setHeight(tileSize.height()) if (objectVariantMap.__contains__("visible")): object.setVisible(objectVariantMap.get("visible", True)) object.setProperties(self.toProperties(objectVariantMap.get("properties", {}))) objectGroup.addObject(object) polylineVariant = objectVariantMap.get("polyline", []) polygonVariant = objectVariantMap.get("polygon", []) if len(polygonVariant) > 0: object.setShape(MapObject.Polygon) object.setPolygon(self.toPolygon(polygonVariant)) if len(polylineVariant) > 0: object.setShape(MapObject.Polyline) object.setPolygon(self.toPolygon(polylineVariant)) if (objectVariantMap.__contains__("ellipse")): object.setShape(MapObject.Ellipse) return objectGroup def toImageLayer(self, variantMap): imageLayer = ImageLayer(variantMap.get("name",''), variantMap.get("x",0), variantMap.get("y",0), variantMap.get("width",0), variantMap.get("height",0)) opacity = variantMap.get("opacity", 0.0) visible = variantMap.get("visible", True) imageLayer.setOpacity(opacity) imageLayer.setVisible(visible) trans = variantMap.get("transparentcolor", '') if (not trans.isEmpty() and QColor.isValidColor(trans)): imageLayer.setTransparentColor(QColor(trans)) imageVariant = variantMap.get("image",'') if imageVariant != '': imagePath = resolvePath(self.mMapDir, imageVariant) if (not imageLayer.loadFromImage(QImage(imagePath), imagePath)): self.mError = self.tr("Error loading image:\n'%s'"%imagePath) return None return imageLayer def toPolygon(self, variant): polygon = QPolygonF() for pointVariant in variant: pointVariantMap = pointVariant pointX = pointVariantMap.get("x",0.0) pointY = pointVariantMap.get("y",0.0) polygon.append(QPointF(pointX, pointY)) return polygon
class MapReaderPrivate(): def tr(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def trUtf8(self, sourceText, disambiguation = '', n = -1): return QCoreApplication.translate('MapReader', sourceText, disambiguation, n) def __init__(self, mapReader): self.p = mapReader self.mMap = None self.mError = QString('') self.mReadingExternalTileset = False self.xml = QXmlStreamReader() self.mGidMapper = GidMapper() def readMap(self, device, path): self.mError = QString('') self.mPath = path map = None self.xml.setDevice(device) if (self.xml.readNextStartElement() and self.xml.name() == "map"): map = self.__readMap() else: self.xml.raiseError(self.tr("Not a map file.")) self.mGidMapper.clear() return map def readTileset(self, device, path): self.mError = '' self.mPath = path tileset = None self.mReadingExternalTileset = True self.xml.setDevice(device) if (self.xml.readNextStartElement() and self.xml.name() == "tileset"): tileset = self.__readTileset() else: self.xml.raiseError(self.tr("Not a tileset file.")) self.mReadingExternalTileset = False return tileset def openFile(self, file): if (not file.exists()): self.mError = self.tr("File not found: %s"%file.fileName()) return False elif (not file.open(QFile.ReadOnly | QFile.Text)): self.mError = self.tr("Unable to read file: %s"%file.fileName()) return False return True def errorString(self): if self.mError != '': return self.mError else: return self.tr("%d\n\nLine %d, column %s"%(self.xml.lineNumber(), self.xml.columnNumber(), self.xml.errorString())) def __readUnknownElement(self): qDebug("Unknown element (fixme): "+self.xml.name()+" at line "+self.xml.lineNumber()+", column "+self.xml.columnNumber()) self.xml.skipCurrentElement() def __readMap(self): atts = self.xml.attributes() mapWidth = Int(atts.value("width")) mapHeight = Int(atts.value("height")) tileWidth = Int(atts.value("tilewidth")) tileHeight = Int(atts.value("tileheight")) hexSideLength = Int(atts.value("hexsidelength")) orientationString = atts.value("orientation") orientation = orientationFromString(orientationString) if (orientation == Map.Orientation.Unknown): self.xml.raiseError(self.tr("Unsupported map orientation: \"%s\""%orientationString)) staggerAxisString = atts.value("staggeraxis") staggerAxis = staggerAxisFromString(staggerAxisString) staggerIndexString = atts.value("staggerindex") staggerIndex = staggerIndexFromString(staggerIndexString) renderOrderString = atts.value("renderorder") renderOrder = renderOrderFromString(renderOrderString) nextObjectId = Int(atts.value("nextobjectid")) self.mMap = Map(orientation, mapWidth, mapHeight, tileWidth, tileHeight) self.mMap.setHexSideLength(hexSideLength) self.mMap.setStaggerAxis(staggerAxis) self.mMap.setStaggerIndex(staggerIndex) self.mMap.setRenderOrder(renderOrder) if (nextObjectId): self.mMap.setNextObjectId(nextObjectId) bgColorString = atts.value("backgroundcolor") if len(bgColorString)>0: self.mMap.setBackgroundColor(QColor(bgColorString)) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): self.mMap.mergeProperties(self.__readProperties()) elif (self.xml.name() == "tileset"): self.mMap.addTileset(self.__readTileset()) elif (self.xml.name() == "layer"): self.mMap.addLayer(self.__readLayer()) elif (self.xml.name() == "objectgroup"): self.mMap.addLayer(self.__readObjectGroup()) elif (self.xml.name() == "imagelayer"): self.mMap.addLayer(self.__readImageLayer()) else: self.__readUnknownElement() # Clean up in case of error if (self.xml.hasError()): self.mMap = None return self.mMap def __readTileset(self): atts = self.xml.attributes() source = atts.value("source") firstGid = Int(atts.value("firstgid")) tileset = None if source == '': # Not an external tileset name = atts.value("name") tileWidth = Int(atts.value("tilewidth")) tileHeight = Int(atts.value("tileheight")) tileSpacing = Int(atts.value("spacing")) margin = Int(atts.value("margin")) if (tileWidth < 0 or tileHeight < 0 or (firstGid == 0 and not self.mReadingExternalTileset)): self.xml.raiseError(self.tr("Invalid tileset parameters for tileset '%s'"%name)) else: tileset = Tileset.create(name, tileWidth, tileHeight, tileSpacing, margin) while (self.xml.readNextStartElement()): if (self.xml.name() == "tile"): self.__readTilesetTile(tileset) elif (self.xml.name() == "tileoffset"): oa = self.xml.attributes() x = Int(oa.value("x")) y = Int(oa.value("y")) tileset.setTileOffset(QPoint(x, y)) self.xml.skipCurrentElement() elif (self.xml.name() == "properties"): tileset.mergeProperties(self.__readProperties()) elif (self.xml.name() == "image"): if (tileWidth == 0 or tileHeight == 0): self.xml.raiseError(self.tr("Invalid tileset parameters for tileset '%s'"%name)) tileset.clear() break else: self.__readTilesetImage(tileset) elif (self.xml.name() == "terraintypes"): self.__readTilesetTerrainTypes(tileset) else: self.__readUnknownElement() else: # External tileset absoluteSource = self.p.resolveReference(source, self.mPath) tileset, error = self.p.readExternalTileset(absoluteSource) if (not tileset): self.xml.raiseError(self.tr("Error while loading tileset '%s': %s"%(absoluteSource, error))) self.xml.skipCurrentElement() if (tileset and not self.mReadingExternalTileset): self.mGidMapper.insert(firstGid, tileset) return tileset def __readTilesetTile(self, tileset): atts = self.xml.attributes() id = Int(atts.value("id")) if (id < 0): self.xml.raiseError(self.tr("Invalid tile ID: %d"%id)) return hasImage = tileset.imageSource()!='' if (hasImage and id >= tileset.tileCount()): self.xml.raiseError(self.tr("Tile ID does not exist in tileset image: %d"%id)) return if (id > tileset.tileCount()): self.xml.raiseError(self.tr("Invalid (nonconsecutive) tile ID: %d"%id)) return # For tilesets without image source, consecutive tile IDs are allowed (for # tiles with individual images) if (id == tileset.tileCount()): tileset.addTile(QPixmap()) tile = tileset.tileAt(id) # Read tile quadrant terrain ids terrain = atts.value("terrain") if terrain != '': quadrants = terrain.split(",") if (len(quadrants) == 4): for i in range(4): if quadrants[i]=='': t = -1 else: t = Int(quadrants[i]) tile.setCornerTerrainId(i, t) # Read tile probability probability = atts.value("probability") if probability != '': tile.setProbability(Float(probability)) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): tile.mergeProperties(self.__readProperties()) elif (self.xml.name() == "image"): source = self.xml.attributes().value("source") if source != '': source = self.p.resolveReference(source, self.mPath) tileset.setTileImage(id, QPixmap.fromImage(self.__readImage()), source) elif (self.xml.name() == "objectgroup"): tile.setObjectGroup(self.__readObjectGroup()) elif (self.xml.name() == "animation"): tile.setFrames(self.__readAnimationFrames()) else: self.__readUnknownElement() # Temporary code to support TMW-style animation frame properties if (not tile.isAnimated() and tile.hasProperty("animation-frame0")): frames = QVector() i = 0 while(i>=0): frameName = "animation-frame" + str(i) delayName = "animation-delay" + str(i) if (tile.hasProperty(frameName) and tile.hasProperty(delayName)): frame = Frame() frame.tileId = tile.property(frameName) frame.duration = tile.property(delayName) * 10 frames.append(frame) else: break i += 1 tile.setFrames(frames) def __readTilesetImage(self, tileset): atts = self.xml.attributes() source = atts.value("source") trans = atts.value("trans") if len(trans)>0: if (not trans.startswith('#')): trans = '#' + trans tileset.setTransparentColor(QColor(trans)) if len(source)>0: source = self.p.resolveReference(source, self.mPath) # Set the width that the tileset had when the map was saved width = Int(atts.value("width")) self.mGidMapper.setTilesetWidth(tileset, width) if (not tileset.loadFromImage(self.__readImage(), source)): self.xml.raiseError(self.tr("Error loading tileset image:\n'%s'"%source)) def __readTilesetTerrainTypes(self, tileset): while (self.xml.readNextStartElement()): if (self.xml.name() == "terrain"): atts = self.xml.attributes() name = atts.value("name") tile = Int(atts.value("tile")) terrain = tileset.addTerrain(name, tile) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): terrain.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() else: self.__readUnknownElement() def __readImage(self): atts = self.xml.attributes() source = atts.value("source") format = atts.value("format") if len(source)==0: while (self.xml.readNextStartElement()): if (self.xml.name() == "data"): atts = self.xml.attributes() encoding = atts.value("encoding") data = self.xml.readElementText().toLatin1() if (encoding == "base64"): data = QByteArray.fromBase64(data) self.xml.skipCurrentElement() return QImage.fromData(data, format.toLatin1()) else: self.__readUnknownElement() else: self.xml.skipCurrentElement() source = self.p.resolveReference(source, self.mPath) image = self.p.readExternalImage(source) if (image.isNull()): self.xml.raiseError(self.tr("Error loading image:\n'%s'"%source)) return image return QImage() def __readLayer(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) tileLayer = TileLayer(name, x, y, width, height) readLayerAttributes(tileLayer, atts) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): tileLayer.mergeProperties(self.__readProperties()) elif (self.xml.name() == "data"): self.__readLayerData(tileLayer) else: self.__readUnknownElement() return tileLayer def __readLayerData(self, tileLayer): atts = self.xml.attributes() encoding = atts.value("encoding") compression = atts.value("compression") layerDataFormat = 0 if (encoding == ''): layerDataFormat = Map.LayerDataFormat.XML elif (encoding == "csv"): layerDataFormat = Map.LayerDataFormat.CSV elif (encoding == "base64"): if (compression == ''): layerDataFormat = Map.LayerDataFormat.Base64 elif (compression == "gzip"): layerDataFormat = Map.LayerDataFormat.Base64Gzip elif (compression == "zlib"): layerDataFormat = Map.LayerDataFormat.Base64Zlib else: self.xml.raiseError(self.tr("Compression method '%s' not supported"%compression)) return else: self.xml.raiseError(self.tr("Unknown encoding: %s"%encoding)) return self.mMap.setLayerDataFormat(layerDataFormat) x = 0 y = 0 while (self.xml.readNext() != QXmlStreamReader.Invalid): if (self.xml.isEndElement()): break elif (self.xml.isStartElement()): if (self.xml.name() == "tile"): if (y >= tileLayer.height()): self.xml.raiseError(self.tr("Too many <tile> elements")) continue atts = self.xml.attributes() gid = Int(atts.value("gid")) tileLayer.setCell(x, y, self.__cellForGid(gid)) x += 1 if (x >= tileLayer.width()): x = 0 y += 1 self.xml.skipCurrentElement() else: self.__readUnknownElement() elif (self.xml.isCharacters() and not self.xml.isWhitespace()): if (encoding == "base64"): self.__decodeBinaryLayerData(tileLayer, self.xml.text(), layerDataFormat) elif (encoding == "csv"): self.__decodeCSVLayerData(tileLayer, self.xml.text()) def __decodeBinaryLayerData(self, tileLayer, data, format): error = self.mGidMapper.decodeLayerData(tileLayer, data, format) if error==DecodeError.CorruptLayerData: self.xml.raiseError(self.tr("Corrupt layer data for layer '%s'"%tileLayer.name())) return elif error==DecodeError.TileButNoTilesets: self.xml.raiseError(self.tr("Tile used but no tilesets specified")) return elif error==DecodeError.InvalidTile: self.xml.raiseError(self.tr("Invalid tile: %d"%self.mGidMapper.invalidTile())) return elif error==DecodeError.NoError: pass def __decodeCSVLayerData(self, tileLayer, text): trimText = text.strip() tiles = trimText.split(',') if (len(tiles) != tileLayer.width() * tileLayer.height()): self.xml.raiseError(self.tr("Corrupt layer data for layer '%s'"%tileLayer.name())) return for y in range(tileLayer.height()): for x in range(tileLayer.width()): conversionOk = False gid, conversionOk = Int2(tiles[y * tileLayer.width() + x]) if (not conversionOk): self.xml.raiseError(self.tr("Unable to parse tile at (%d,%d) on layer '%s'"%(x + 1, y + 1, tileLayer.name()))) return tileLayer.setCell(x, y, self.__cellForGid(gid)) ## # Returns the cell for the given global tile ID. Errors are raised with # the QXmlStreamReader. # # @param gid the global tile ID # @return the cell data associated with the given global tile ID, or an # empty cell if not found ## def __cellForGid(self, gid): ok = False result, ok = self.mGidMapper.gidToCell(gid) if (not ok): if (self.mGidMapper.isEmpty()): self.xml.raiseError(self.tr("Tile used but no tilesets specified")) else: self.xml.raiseError(self.tr("Invalid tile: %d"%gid)) return result def __readImageLayer(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) imageLayer = ImageLayer(name, x, y, width, height) readLayerAttributes(imageLayer, atts) while (self.xml.readNextStartElement()): if (self.xml.name() == "image"): self.__readImageLayerImage(imageLayer) elif (self.xml.name() == "properties"): imageLayer.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() return imageLayer def __readImageLayerImage(self, imageLayer): atts = self.xml.attributes() source = atts.value("source") trans = atts.value("trans") if trans != '': if (not trans.startswith('#')): trans = '#' + trans imageLayer.setTransparentColor(QColor(trans)) source = self.p.resolveReference(source, self.mPath) imageLayerImage = self.p.readExternalImage(source) if (not imageLayer.loadFromImage(imageLayerImage, source)): self.xml.raiseError(self.tr("Error loading image layer image:\n'%s'"%source)) self.xml.skipCurrentElement() def __readObjectGroup(self): atts = self.xml.attributes() name = atts.value("name") x = Int(atts.value("x")) y = Int(atts.value("y")) width = Int(atts.value("width")) height = Int(atts.value("height")) objectGroup = ObjectGroup(name, x, y, width, height) readLayerAttributes(objectGroup, atts) color = atts.value("color") if color != '': objectGroup.setColor(color) if (atts.hasAttribute("draworder")): value = atts.value("draworder") drawOrder = drawOrderFromString(value) if (drawOrder == ObjectGroup.DrawOrder.UnknownOrder): #del objectGroup self.xml.raiseError(self.tr("Invalid draw order: %s"%value)) return None objectGroup.setDrawOrder(drawOrder) while (self.xml.readNextStartElement()): if (self.xml.name() == "object"): objectGroup.addObject(self.__readObject()) elif (self.xml.name() == "properties"): objectGroup.mergeProperties(self.__readProperties()) else: self.__readUnknownElement() return objectGroup def __readObject(self): atts = self.xml.attributes() id = Int(atts.value("id")) name = atts.value("name") gid = Int(atts.value("gid")) x = Float(atts.value("x")) y = Float(atts.value("y")) width = Float(atts.value("width")) height = Float(atts.value("height")) type = atts.value("type") visibleRef = atts.value("visible") pos = QPointF(x, y) size = QSizeF(width, height) object = MapObject(name, type, pos, size) object.setId(id) try: rotation = Float(atts.value("rotation")) ok = True except: ok = False if (ok): object.setRotation(rotation) if (gid): object.setCell(self.__cellForGid(gid)) if (not object.cell().isEmpty()): tileSize = object.cell().tile.size() if (width == 0): object.setWidth(tileSize.width()) if (height == 0): object.setHeight(tileSize.height()) try: visible = int(visibleRef) ok = True except: ok = False if ok: object.setVisible(visible) while (self.xml.readNextStartElement()): if (self.xml.name() == "properties"): object.mergeProperties(self.__readProperties()) elif (self.xml.name() == "polygon"): object.setPolygon(self.__readPolygon()) object.setShape(MapObject.Polygon) elif (self.xml.name() == "polyline"): object.setPolygon(self.__readPolygon()) object.setShape(MapObject.Polyline) elif (self.xml.name() == "ellipse"): self.xml.skipCurrentElement() object.setShape(MapObject.Ellipse) else: self.__readUnknownElement() return object def __readPolygon(self): atts = self.xml.attributes() points = atts.value("points") pointsList = list(filter(lambda x:x.strip()!='', points.split(' '))) polygon = QPolygonF() ok = True for point in pointsList: try: x, y = point.split(',') except: ok = False break x, ok = Float2(x) if (not ok): break y, ok = Float2(y) if (not ok): break polygon.append(QPointF(x, y)) if (not ok): self.xml.raiseError(self.tr("Invalid points data for polygon")) self.xml.skipCurrentElement() return polygon def __readAnimationFrames(self): frames = QVector() while (self.xml.readNextStartElement()): if (self.xml.name() == "frame"): atts = self.xml.attributes() frame = Frame() frame.tileId = Int(atts.value("tileid")) frame.duration = Int(atts.value("duration")) frames.append(frame) self.xml.skipCurrentElement() else: self.__readUnknownElement() return frames def __readProperties(self): properties = Properties() while (self.xml.readNextStartElement()): if (self.xml.name() == "property"): self.__readProperty(properties) else: self.__readUnknownElement() return properties def __readProperty(self, properties): atts = self.xml.attributes() propertyName = atts.value("name") propertyValue = atts.value("value") while (self.xml.readNext() != QXmlStreamReader.Invalid): if (self.xml.isEndElement()): break elif (self.xml.isCharacters() and not self.xml.isWhitespace()): if (propertyValue.isEmpty()): propertyValue = self.xml.text() elif (self.xml.isStartElement()): self.__readUnknownElement() properties.insert(propertyName, propertyValue)
def __init__(self): super().__init__() self.mMapDir = QDir() self.mError = '' self.mGidMapper = GidMapper()
def write(self, map, fileName): file = QSaveFile(fileName) if (not file.open(QFile.WriteOnly | QFile.Text)): self.mError = self.tr("Could not open file for writing.") return False out = QTextStream(file) out.setCodec("UTF-8") mapWidth = map.width() mapHeight = map.height() # write [header] out << "[header]\n" out << "width=" << str(mapWidth) << "\n" out << "height=" << str(mapHeight) << "\n" out << "tilewidth=" << str(map.tileWidth()) << "\n" out << "tileheight=" << str(map.tileHeight()) << "\n" out << "orientation=" << str(orientationToString(map.orientation())) << "\n" # write all properties for this map for it in map.properties().__iter__(): out << it[0] << "=" << it[1] << "\n" out << "\n" mapDir = QFileInfo(fileName).absoluteDir() out << "[tilesets]\n" for tileset in map.tilesets(): imageSource = tileset.imageSource() source = mapDir.relativeFilePath(imageSource) out << "tileset=" << source \ << "," << str(tileset.tileWidth()) \ << "," << str(tileset.tileHeight()) \ << "," << str(tileset.tileOffset().x()) \ << "," << str(tileset.tileOffset().y()) \ << "\n" out << "\n" gidMapper = GidMapper(map.tilesets()) # write layers for layer in map.layers(): tileLayer = layer.asTileLayer() if tileLayer: out << "[layer]\n" out << "type=" << layer.name() << "\n" out << "data=\n" for y in range(0, mapHeight): for x in range(0, mapWidth): t = tileLayer.cellAt(x, y) id = 0 if (t.tile): id = gidMapper.cellToGid(t) out << id if (x < mapWidth - 1): out << "," if (y < mapHeight - 1): out << "," out << "\n" out << "\n" group = layer.asObjectGroup() if group: for o in group.objects(): if o.type() != '': out << "[" << group.name() << "]\n" # display object name as comment if o.name() != '': out << "# " << o.name() << "\n" out << "type=" << o.type() << "\n" x,y,w,h = 0 if (map.orientation() == Map.Orthogonal): x = o.x()/map.tileWidth() y = o.y()/map.tileHeight() w = o.width()/map.tileWidth() h = o.height()/map.tileHeight() else : x = o.x()/map.tileHeight() y = o.y()/map.tileHeight() w = o.width()/map.tileHeight() h = o.height()/map.tileHeight() out << "location=" << x << "," << y out << "," << w << "," << h << "\n" # write all properties for this object for it in o.properties().__iter__(): out << it[0] << "=" << it[1] << "\n" out << "\n" if not file.commit(): self.mError = file.errorString() return False return True