com.badlogic.gdx.graphics.g2d.tiled.TiledLoader Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
******************************************************************************/
package com.badlogic.gdx.graphics.g2d.tiled;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.zip.DataFormatException;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.XmlReader;
/** Loads a Tiled Map from a tmx file
* @author David Fraska */
public class TiledLoader {
/** Loads a TiledMap
from a String
.
* @param tmxData The tmx file's content. */
public static TiledMap createMap (String tmxData) {
return createMap(null, tmxData);
}
/** Loads a Tiled Map from a tmx file
* @param tmxFile the map's tmx file */
public static TiledMap createMap (FileHandle tmxFile) {
return createMap(tmxFile, null);
}
/** Loads a TiledMap from a tmx file.
* @param tmxFile The tmx file. NULL to force load from tmxData
.
* @param tmxData The tmx file's content. NULL to force load from tmxFile
. */
private static TiledMap createMap (FileHandle tmxFile, String tmxData) {
final TiledMap map;
map = new TiledMap();
map.tmxFile = tmxFile;
try {
XmlReader xmlReader = new XmlReader() {
Stack currBranch = new Stack();
boolean awaitingData = false;
TiledLayer currLayer;
int currLayerWidth = 0, currLayerHeight = 0;
TileSet currTileSet;
TiledObjectGroup currObjectGroup;
TiledObject currObject;
int currTile;
class Polyline {
String name;
String points;
public Polyline( String name ) {
this.name = name;
}
public Polyline() {
}
}
Polyline polyline,polygon;
class Property {
String parentType, name, value;
}
Property currProperty;
String encoding, dataString, compression;
byte[] data;
int dataCounter = 0, row, col;
@Override
protected void open (String name) {
currBranch.push(name);
if ("layer".equals(name)) {
currLayer = new TiledLayer();
return;
}
if ("tileset".equals(name)) {
currTileSet = new TileSet();
return;
}
if ("data".equals(name)) {
dataString = ""; // clear the string for new data
awaitingData = true;
return;
}
if ("objectgroup".equals(name)) {
currObjectGroup = new TiledObjectGroup();
return;
}
if ("object".equals(name)) {
currObject = new TiledObject();
return;
}
if ("property".equals(name)) {
currProperty = new Property();
currProperty.parentType = currBranch.get(currBranch.size() - 3);
return;
}
if( "polyline".equals( name ) ) {
polyline = new Polyline("polyline");
return;
}
if( "polygon".equals( name ) ) {
polygon = new Polyline("polygon");
return;
}
}
@Override
protected void attribute (String name, String value) {
String element = currBranch.peek();
if ("layer".equals(element)) {
if ("width".equals(name)) {
currLayerWidth = Integer.parseInt(value);
} else if ("height".equals(name)) {
currLayerHeight = Integer.parseInt(value);
}
if (currLayerWidth != 0 && currLayerHeight != 0) {
currLayer.tiles = new int[currLayerHeight][currLayerWidth];
}
if ("name".equals(name)) {
currLayer.name = value;
}
return;
}
if ("tileset".equals(element)) {
if ("firstgid".equals(name)) {
currTileSet.firstgid = Integer.parseInt(value);
return;
}
if ("tilewidth".equals(name)) {
currTileSet.tileWidth = Integer.parseInt(value);
return;
}
if ("tileheight".equals(name)) {
currTileSet.tileHeight = Integer.parseInt(value);
return;
}
if ("name".equals(name)) {
currTileSet.name = value;
return;
}
if ("spacing".equals(name)) {
currTileSet.spacing = Integer.parseInt(value);
return;
}
if ("margin".equals(name)) {
currTileSet.margin = Integer.parseInt(value);
return;
}
return;
}
if ("image".equals(element)) {
if ("source".equals(name)) {
currTileSet.imageName = value;
return;
}
return;
}
if ("data".equals(element)) {
if ("encoding".equals(name)) {
encoding = value;
return;
}
if ("compression".equals(name)) {
compression = value;
return;
}
return;
}
if ("objectgroup".equals(element)) {
if ("name".equals(name)) {
currObjectGroup.name = value;
return;
}
if ("height".equals(name)) {
currObjectGroup.height = Integer.parseInt(value);
return;
}
if ("width".equals(name)) {
currObjectGroup.width = Integer.parseInt(value);
return;
}
return;
}
if ("object".equals(element)) {
if ("name".equals(name)) {
currObject.name = value;
return;
}
if ("type".equals(name)) {
currObject.type = value;
return;
}
if ("x".equals(name)) {
currObject.x = Integer.parseInt(value);
return;
}
if ("y".equals(name)) {
currObject.y = Integer.parseInt(value);
return;
}
if ("width".equals(name)) {
currObject.width = Integer.parseInt(value);
return;
}
if ("height".equals(name)) {
currObject.height = Integer.parseInt(value);
return;
}
if ("gid".equals(name)) {
currObject.gid = Integer.parseInt(value);
return;
}
return;
}
if ("map".equals(element)) {
if ("orientation".equals(name)) {
map.orientation = value;
return;
}
if ("width".equals(name)) {
map.width = Integer.parseInt(value);
return;
}
if ("height".equals(name)) {
map.height = Integer.parseInt(value);
return;
}
if ("tilewidth".equals(name)) {
map.tileWidth = Integer.parseInt(value);
return;
}
if ("tileheight".equals(name)) {
map.tileHeight = Integer.parseInt(value);
return;
}
return;
}
if ("tile".equals(element)) {
if (awaitingData) { // Actually getting tile data
if ("gid".equals(name)) {
col = dataCounter % currLayerWidth;
row = dataCounter / currLayerWidth;
if (row < currLayerHeight) {
currLayer.tiles[row][col] = Integer.parseInt(value);
} else {
Gdx.app.log("TiledLoader", "Warning: extra XML gid values ignored! Your map is likely corrupt!");
}
dataCounter++;
}
} else { // Not getting tile data, must be a tile Id (for properties)
if ("id".equals(name)) {
currTile = Integer.parseInt(value);
}
}
return;
}
if ("property".equals(element)) {
if ("name".equals(name)) {
currProperty.name = value;
return;
}
if ("value".equals(name)) {
currProperty.value = value;
return;
}
return;
}
if( "polyline".equals( element ) ) {
if( "points".equals( name ) ) {
polyline.points = value;
return;
}
return;
}
if( "polygon".equals( element ) ) {
if( "points".equals( name ) ) {
polygon.points = value;
return;
}
return;
}
}
@Override
protected void text (String text) {
if (awaitingData) {
dataString = dataString.concat(text);
}
}
@Override
protected void close () {
String element = currBranch.pop();
if ("layer".equals(element)) {
map.layers.add(currLayer);
currLayer = null;
return;
}
if ("tileset".equals(element)) {
map.tileSets.add(currTileSet);
currTileSet = null;
return;
}
if ("object".equals(element)) {
currObjectGroup.objects.add(currObject);
currObject = null;
return;
}
if ("objectgroup".equals(element)) {
map.objectGroups.add(currObjectGroup);
currObjectGroup = null;
return;
}
if ("property".equals(element)) {
putProperty(currProperty);
currProperty = null;
return;
}
if( "polyline".equals( element ) ) {
putPolyLine( polyline );
polyline = null;
return;
}
if( "polygon".equals( element ) ) {
putPolyLine( polygon );
polygon = null;
return;
}
if ("data".equals(element)) {
// decode and uncompress the data
if ("base64".equals(encoding)) {
if (dataString == null | "".equals(dataString.trim())) return;
data = Base64Coder.decode(dataString.trim());
if ("gzip".equals(compression)) {
unGZip();
} else if ("zlib".equals(compression)) {
unZlib();
} else if (compression == null) {
arrangeData();
}
} else if ("csv".equals(encoding) && compression == null) {
fromCSV();
} else if (encoding == null && compression == null) {
// startElement() handles most of this
dataCounter = 0;// reset counter in case another layer comes through
} else {
throw new GdxRuntimeException("Unsupported encoding and/or compression format");
}
awaitingData = false;
return;
}
if ("property".equals(element)) {
putProperty(currProperty);
currProperty = null;
}
}
private void putPolyLine( Polyline polyLine ) {
if( polyLine == null ) {
return;
}
if( "polyline".equals( polyLine.name ) ) {
currObject.polyline = polyLine.points;
return;
}
if( "polygon".equals( polyLine.name ) ) {
currObject.polygon = polyLine.points;
return;
}
return;
}
private void putProperty (Property property) {
if ("tile".equals(property.parentType)) {
map.setTileProperty(currTile + currTileSet.firstgid, property.name, property.value);
return;
}
if ("map".equals(property.parentType)) {
map.properties.put(property.name, property.value);
return;
}
if ("layer".equals(property.parentType)) {
currLayer.properties.put(property.name, property.value);
return;
}
if ("objectgroup".equals(property.parentType)) {
currObjectGroup.properties.put(property.name, property.value);
return;
}
if ("object".equals(property.parentType)) {
currObject.properties.put(property.name, property.value);
return;
}
}
private void fromCSV () {
StringTokenizer st = new StringTokenizer(dataString.trim(), ",");
for (int row = 0; row < currLayerHeight; row++) {
for (int col = 0; col < currLayerWidth; col++) {
currLayer.tiles[row][col] = Integer.parseInt(st.nextToken().trim());
}
}
}
private void arrangeData () {
int byteCounter = 0;
for (int row = 0; row < currLayerHeight; row++) {
for (int col = 0; col < currLayerWidth; col++) {
currLayer.tiles[row][col] = unsignedByteToInt(data[byteCounter++])
| unsignedByteToInt(data[byteCounter++]) << 8 | unsignedByteToInt(data[byteCounter++]) << 16
| unsignedByteToInt(data[byteCounter++]) << 24;
}
}
}
private void unZlib () {
Inflater zlib = new Inflater();
byte[] readTemp = new byte[4];
zlib.setInput(data, 0, data.length);
for (int row = 0; row < currLayerHeight; row++) {
for (int col = 0; col < currLayerWidth; col++) {
try {
zlib.inflate(readTemp, 0, 4);
currLayer.tiles[row][col] = unsignedByteToInt(readTemp[0]) | unsignedByteToInt(readTemp[1]) << 8
| unsignedByteToInt(readTemp[2]) << 16 | unsignedByteToInt(readTemp[3]) << 24;
} catch (DataFormatException e) {
throw new GdxRuntimeException("Error Reading TMX Layer Data.", e);
}
}
}
}
private void unGZip () {
GZIPInputStream GZIS = null;
try {
GZIS = new GZIPInputStream(new ByteArrayInputStream(data), data.length);
} catch (IOException e) {
throw new GdxRuntimeException("Error Reading TMX Layer Data - IOException: " + e.getMessage());
}
// Read the GZIS data into an array, 4 bytes = 1 GID
byte[] readTemp = new byte[4];
for (int row = 0; row < currLayerHeight; row++) {
for (int col = 0; col < currLayerWidth; col++) {
try {
GZIS.read(readTemp, 0, 4);
currLayer.tiles[row][col] = unsignedByteToInt(readTemp[0]) | unsignedByteToInt(readTemp[1]) << 8
| unsignedByteToInt(readTemp[2]) << 16 | unsignedByteToInt(readTemp[3]) << 24;
} catch (IOException e) {
throw new GdxRuntimeException("Error Reading TMX Layer Data.", e);
}
}
}
}
};
// Is it a file?
if (tmxFile != null) {
xmlReader.parse(tmxFile);
} else {
xmlReader.parse(tmxData);
}
} catch (IOException e) {
throw new GdxRuntimeException("Error Parsing TMX file", e);
}
return map;
}
static int unsignedByteToInt (byte b) {
return (int)b & 0xFF;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy