OSDN Git Service

69d604766f50c95146b16c5eb2c744ab83bbe4ae
[mikumikustudio/libgdx-mikumikustudio.git] / backends / gdx-backends-gwt / src / com / badlogic / gdx / backends / gwt / emu / com / badlogic / gdx / maps / tiled / TmxMapLoader.java
1 package com.badlogic.gdx.maps.tiled;
2
3 import java.io.IOException;
4 import java.util.StringTokenizer;
5
6 import com.badlogic.gdx.assets.AssetDescriptor;
7 import com.badlogic.gdx.assets.AssetLoaderParameters;
8 import com.badlogic.gdx.assets.AssetManager;
9 import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader;
10 import com.badlogic.gdx.assets.loaders.FileHandleResolver;
11 import com.badlogic.gdx.assets.loaders.TextureLoader;
12 import com.badlogic.gdx.assets.loaders.TextureLoader.TextureParameter;
13 import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
14 import com.badlogic.gdx.files.FileHandle;
15 import com.badlogic.gdx.graphics.Texture;
16 import com.badlogic.gdx.graphics.Texture.TextureFilter;
17 import com.badlogic.gdx.graphics.g2d.TextureRegion;
18 import com.badlogic.gdx.maps.ImageResolver;
19 import com.badlogic.gdx.maps.ImageResolver.AssetManagerImageResolver;
20 import com.badlogic.gdx.maps.ImageResolver.DirectImageResolver;
21 import com.badlogic.gdx.maps.MapLayer;
22 import com.badlogic.gdx.maps.MapObject;
23 import com.badlogic.gdx.maps.MapProperties;
24 import com.badlogic.gdx.maps.objects.EllipseMapObject;
25 import com.badlogic.gdx.maps.objects.PolygonMapObject;
26 import com.badlogic.gdx.maps.objects.PolylineMapObject;
27 import com.badlogic.gdx.maps.objects.RectangleMapObject;
28 import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell;
29 import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile;
30 import com.badlogic.gdx.math.Polygon;
31 import com.badlogic.gdx.math.Polyline;
32 import com.badlogic.gdx.utils.Array;
33 import com.badlogic.gdx.utils.Base64Coder;
34 import com.badlogic.gdx.utils.GdxRuntimeException;
35 import com.badlogic.gdx.utils.ObjectMap;
36 import com.badlogic.gdx.utils.XmlReader;
37 import com.badlogic.gdx.utils.XmlReader.Element;
38
39 /** @brief synchronous loader for TMX maps created with the Tiled tool */
40 public class TmxMapLoader extends AsynchronousAssetLoader<TiledMap, TmxMapLoader.Parameters> {
41
42         public static class Parameters extends AssetLoaderParameters<TiledMap> {
43                 /** Whether to load the map for a y-up coordinate system */
44                 public boolean yUp = true;
45                 /** generate mipmaps? **/
46                 public boolean generateMipMaps = false;
47                 /** The TextureFilter to use for minification **/
48                 public TextureFilter textureMinFilter = TextureFilter.Nearest;
49                 /** The TextureFilter to use for magnification **/
50                 public TextureFilter textureMagFilter = TextureFilter.Nearest;
51         }
52
53         protected static final int FLAG_FLIP_HORIZONTALLY = 0x80000000;
54         protected static final int FLAG_FLIP_VERTICALLY = 0x40000000;
55         protected static final int FLAG_FLIP_DIAGONALLY = 0x20000000;
56         protected static final int MASK_CLEAR = 0xE0000000;
57
58         protected XmlReader xml = new XmlReader();
59         protected Element root;
60         protected boolean yUp;
61
62         protected int mapWidthInPixels;
63         protected int mapHeightInPixels;
64
65         protected TiledMap map;
66
67         public TmxMapLoader () {
68                 super(new InternalFileHandleResolver());
69         }
70
71         /** Creates loader
72          * 
73          * @param resolver */
74         public TmxMapLoader (FileHandleResolver resolver) {
75                 super(resolver);
76         }
77
78         /** Loads the {@link TiledMap} from the given file. The file is resolved via the {@link FileHandleResolver} set in the
79          * constructor of this class. By default it will resolve to an internal file. The map will be loaded for a y-up coordinate
80          * system.
81          * @param fileName the filename
82          * @return the TiledMap */
83         public TiledMap load (String fileName) {
84                 return load(fileName, new TmxMapLoader.Parameters());
85         }
86
87         /** Loads the {@link TiledMap} from the given file. The file is resolved via the {@link FileHandleResolver} set in the
88          * constructor of this class. By default it will resolve to an internal file.
89          * @param fileName the filename
90          * @param parameters specifies whether to use y-up, generate mip maps etc.
91          * @return the TiledMap */
92         public TiledMap load (String fileName, TmxMapLoader.Parameters parameters) {
93                 try {
94                         this.yUp = parameters.yUp;
95                         FileHandle tmxFile = resolve(fileName);
96                         root = xml.parse(tmxFile);
97                         ObjectMap<String, Texture> textures = new ObjectMap<String, Texture>();
98                         for (FileHandle textureFile : loadTilesets(root, tmxFile)) {
99                                 Texture texture = new Texture(textureFile, parameters.generateMipMaps);
100                                 texture.setFilter(parameters.textureMinFilter, parameters.textureMagFilter);
101                                 textures.put(textureFile.path(), texture);
102                         }
103                         DirectImageResolver imageResolver = new DirectImageResolver(textures);
104                         TiledMap map = loadTilemap(root, tmxFile, imageResolver);
105                         map.setOwnedResources(textures.values().toArray());
106                         return map;
107                 } catch (IOException e) {
108                         throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
109                 }
110         }
111
112         @Override
113         public void loadAsync (AssetManager manager, String fileName, FileHandle tmxFile, TmxMapLoader.Parameters parameter) {
114                 map = null;
115
116                 if (parameter != null) {
117                         yUp = parameter.yUp;
118                 } else {
119                         yUp = true;
120                 }
121                 try {
122                         map = loadTilemap(root, tmxFile, new AssetManagerImageResolver(manager));
123                 } catch (Exception e) {
124                         throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
125                 }
126         }
127
128         @Override
129         public TiledMap loadSync (AssetManager manager, String fileName, FileHandle fileHandle, TmxMapLoader.Parameters parameter) {
130                 return map;
131         }
132
133         /** Retrieves TiledMap resource dependencies
134          * 
135          * @param fileName
136          * @param parameter not used for now
137          * @return dependencies for the given .tmx file */
138         @Override
139         public Array<AssetDescriptor> getDependencies (String fileName, FileHandle tmxFile, Parameters parameter) {
140                 Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
141                 try {
142                         root = xml.parse(tmxFile);
143                         boolean generateMipMaps = (parameter != null ? parameter.generateMipMaps : false);
144                         TextureLoader.TextureParameter texParams = new TextureParameter();
145                         texParams.genMipMaps = generateMipMaps;
146                         if (parameter != null) {
147                                 texParams.minFilter = parameter.textureMinFilter;
148                                 texParams.magFilter = parameter.textureMagFilter;
149                         }
150                         for (FileHandle image : loadTilesets(root, tmxFile)) {
151                                 dependencies.add(new AssetDescriptor(image, Texture.class, texParams));
152                         }
153                         return dependencies;
154                 } catch (IOException e) {
155                         throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
156                 }
157         }
158
159         /** Loads the map data, given the XML root element and an {@link ImageResolver} used to return the tileset Textures
160          * @param root the XML root element
161          * @param tmxFile the Filehandle of the tmx file
162          * @param imageResolver the {@link ImageResolver}
163          * @return the {@link TiledMap} */
164         protected TiledMap loadTilemap (Element root, FileHandle tmxFile, ImageResolver imageResolver) {
165                 TiledMap map = new TiledMap();
166
167                 String mapOrientation = root.getAttribute("orientation", null);
168                 int mapWidth = root.getIntAttribute("width", 0);
169                 int mapHeight = root.getIntAttribute("height", 0);
170                 int tileWidth = root.getIntAttribute("tilewidth", 0);
171                 int tileHeight = root.getIntAttribute("tileheight", 0);
172                 String mapBackgroundColor = root.getAttribute("backgroundcolor", null);
173
174                 MapProperties mapProperties = map.getProperties();
175                 if (mapOrientation != null) {
176                         mapProperties.put("orientation", mapOrientation);
177                 }
178                 mapProperties.put("width", mapWidth);
179                 mapProperties.put("height", mapHeight);
180                 mapProperties.put("tilewidth", tileWidth);
181                 mapProperties.put("tileheight", tileHeight);
182                 if (mapBackgroundColor != null) {
183                         mapProperties.put("backgroundcolor", mapBackgroundColor);
184                 }
185                 mapWidthInPixels = mapWidth * tileWidth;
186                 mapHeightInPixels = mapHeight * tileHeight;
187
188                 Element properties = root.getChildByName("properties");
189                 if (properties != null) {
190                         loadProperties(map.getProperties(), properties);
191                 }
192                 Array<Element> tilesets = root.getChildrenByName("tileset");
193                 for (Element element : tilesets) {
194                         loadTileSet(map, element, tmxFile, imageResolver);
195                         root.removeChild(element);
196                 }
197                 for (int i = 0, j = root.getChildCount(); i < j; i++) {
198                         Element element = root.getChild(i);
199                         String name = element.getName();
200                         if (name.equals("layer")) {
201                                 loadTileLayer(map, element);
202                         } else if (name.equals("objectgroup")) {
203                                 loadObjectGroup(map, element);
204                         }
205                 }
206                 return map;
207         }
208
209         /** Loads the tilesets
210          * @param root the root XML element
211          * @return a list of filenames for images containing tiles
212          * @throws IOException */
213         protected Array<FileHandle> loadTilesets (Element root, FileHandle tmxFile) throws IOException {
214                 Array<FileHandle> images = new Array<FileHandle>();
215                 for (Element tileset : root.getChildrenByName("tileset")) {
216                         String source = tileset.getAttribute("source", null);
217                         FileHandle image = null;
218                         if (source != null) {
219                                 FileHandle tsx = getRelativeFileHandle(tmxFile, source);
220                                 tileset = xml.parse(tsx);
221                                 String imageSource = tileset.getChildByName("image").getAttribute("source");
222                                 image = getRelativeFileHandle(tsx, imageSource);
223                         } else {
224                                 String imageSource = tileset.getChildByName("image").getAttribute("source");
225                                 image = getRelativeFileHandle(tmxFile, imageSource);
226                         }
227                         images.add(image);
228                 }
229                 return images;
230         }
231
232         /** Loads the specified tileset data, adding it to the collection of the specified map, given the XML element, the tmxFile and
233          * an {@link ImageResolver} used to retrieve the tileset Textures.
234          * 
235          * <p>
236          * Default tileset's property keys that are loaded by default are:
237          * </p>
238          * 
239          * <ul>
240          * <li><em>firstgid</em>, (int, defaults to 1) the first valid global id used for tile numbering</li>
241          * <li><em>imagesource</em>, (String, defaults to empty string) the tileset source image filename</li>
242          * <li><em>imagewidth</em>, (int, defaults to 0) the tileset source image width</li>
243          * <li><em>imageheight</em>, (int, defaults to 0) the tileset source image height</li>
244          * <li><em>tilewidth</em>, (int, defaults to 0) the tile width</li>
245          * <li><em>tileheight</em>, (int, defaults to 0) the tile height</li>
246          * <li><em>margin</em>, (int, defaults to 0) the tileset margin</li>
247          * <li><em>spacing</em>, (int, defaults to 0) the tileset spacing</li>
248          * </ul>
249          * 
250          * <p>
251          * The values are extracted from the specified Tmx file, if a value can't be found then the default is used.
252          * </p>
253          * @param map the Map whose tilesets collection will be populated
254          * @param element the XML element identifying the tileset to load
255          * @param tmxFile the Filehandle of the tmx file
256          * @param imageResolver the {@link ImageResolver} */
257         protected void loadTileSet (TiledMap map, Element element, FileHandle tmxFile, ImageResolver imageResolver) {
258                 if (element.getName().equals("tileset")) {
259                         String name = element.get("name", null);
260                         int firstgid = element.getIntAttribute("firstgid", 1);
261                         int tilewidth = element.getIntAttribute("tilewidth", 0);
262                         int tileheight = element.getIntAttribute("tileheight", 0);
263                         int spacing = element.getIntAttribute("spacing", 0);
264                         int margin = element.getIntAttribute("margin", 0);
265                         String source = element.getAttribute("source", null);
266
267                         String imageSource = "";
268                         int imageWidth = 0, imageHeight = 0;
269
270                         FileHandle image = null;
271                         if (source != null) {
272                                 FileHandle tsx = getRelativeFileHandle(tmxFile, source);
273                                 try {
274                                         element = xml.parse(tsx);
275                                         name = element.get("name", null);
276                                         tilewidth = element.getIntAttribute("tilewidth", 0);
277                                         tileheight = element.getIntAttribute("tileheight", 0);
278                                         spacing = element.getIntAttribute("spacing", 0);
279                                         margin = element.getIntAttribute("margin", 0);
280                                         imageSource = element.getChildByName("image").getAttribute("source");
281                                         imageWidth = element.getChildByName("image").getIntAttribute("width", 0);
282                                         imageHeight = element.getChildByName("image").getIntAttribute("height", 0);
283                                         image = getRelativeFileHandle(tsx, imageSource);
284                                 } catch (IOException e) {
285                                         throw new GdxRuntimeException("Error parsing external tileset.");
286                                 }
287                         } else {
288                                 imageSource = element.getChildByName("image").getAttribute("source");
289                                 imageWidth = element.getChildByName("image").getIntAttribute("width", 0);
290                                 imageHeight = element.getChildByName("image").getIntAttribute("height", 0);
291                                 image = getRelativeFileHandle(tmxFile, imageSource);
292                         }
293
294                         TextureRegion texture = imageResolver.getImage(image.path());
295
296                         TiledMapTileSet tileset = new TiledMapTileSet();
297                         MapProperties props = tileset.getProperties();
298                         tileset.setName(name);
299                         props.put("firstgid", firstgid);
300                         props.put("imagesource", imageSource);
301                         props.put("imagewidth", imageWidth);
302                         props.put("imageheight", imageHeight);
303                         props.put("tilewidth", tilewidth);
304                         props.put("tileheight", tileheight);
305                         props.put("margin", margin);
306                         props.put("spacing", spacing);
307
308                         int stopWidth = texture.getRegionWidth() - tilewidth;
309                         int stopHeight = texture.getRegionHeight() - tileheight;
310
311                         int id = firstgid;
312
313                         for (int y = margin; y <= stopHeight; y += tileheight + spacing) {
314                                 for (int x = margin; x <= stopWidth; x += tilewidth + spacing) {
315                                         TextureRegion tileRegion = new TextureRegion(texture, x, y, tilewidth, tileheight);
316                                         if (!yUp) {
317                                                 tileRegion.flip(false, true);
318                                         }
319                                         TiledMapTile tile = new StaticTiledMapTile(tileRegion);
320                                         tile.setId(id);
321                                         tileset.putTile(id++, tile);
322                                 }
323                         }
324
325                         Array<Element> tileElements = element.getChildrenByName("tile");
326
327                         for (Element tileElement : tileElements) {
328                                 int localtid = tileElement.getIntAttribute("id", 0);
329                                 TiledMapTile tile = tileset.getTile(firstgid + localtid);
330                                 if (tile != null) {
331                                         String terrain = tileElement.getAttribute("terrain", null);
332                                         if (terrain != null) {
333                                                 tile.getProperties().put("terrain", terrain);
334                                         }
335                                         String probability = tileElement.getAttribute("probability", null);
336                                         if (probability != null) {
337                                                 tile.getProperties().put("probability", probability);
338                                         }
339                                         Element properties = tileElement.getChildByName("properties");
340                                         if (properties != null) {
341                                                 loadProperties(tile.getProperties(), properties);
342                                         }
343                                 }
344                         }
345
346                         Element properties = element.getChildByName("properties");
347                         if (properties != null) {
348                                 loadProperties(tileset.getProperties(), properties);
349                         }
350                         map.getTileSets().addTileSet(tileset);
351                 }
352         }
353
354         /** Load one layer (a 'layer' tag).
355          * @param map
356          * @param element */
357         protected void loadTileLayer (TiledMap map, Element element) {
358                 if (element.getName().equals("layer")) {
359                         String name = element.getAttribute("name", null);
360                         int width = element.getIntAttribute("width", 0);
361                         int height = element.getIntAttribute("height", 0);
362                         int tileWidth = element.getParent().getIntAttribute("tilewidth", 0);
363                         int tileHeight = element.getParent().getIntAttribute("tileheight", 0);
364                         boolean visible = element.getIntAttribute("visible", 1) == 1;
365                         float opacity = element.getFloatAttribute("opacity", 1.0f);
366                         TiledMapTileLayer layer = new TiledMapTileLayer(width, height, tileWidth, tileHeight);
367                         layer.setVisible(visible);
368                         layer.setOpacity(opacity);
369                         layer.setName(name);
370
371                         TiledMapTileSets tilesets = map.getTileSets();
372
373                         Element data = element.getChildByName("data");
374                         String encoding = data.getAttribute("encoding", null);
375                         String compression = data.getAttribute("compression", null);
376                         if (encoding == null) { // no 'encoding' attribute means that the encoding is XML
377                                 throw new GdxRuntimeException("Unsupported encoding (XML) for TMX Layer Data");
378                         }
379                         if (encoding.equals("csv")) {
380                                 String[] array = data.getText().split(",");
381                                 for (int y = 0; y < height; y++) {
382                                         for (int x = 0; x < width; x++) {
383                                                 int id = (int)Long.parseLong(array[y * width + x].trim());
384
385                                                 final boolean flipHorizontally = ((id & FLAG_FLIP_HORIZONTALLY) != 0);
386                                                 final boolean flipVertically = ((id & FLAG_FLIP_VERTICALLY) != 0);
387                                                 final boolean flipDiagonally = ((id & FLAG_FLIP_DIAGONALLY) != 0);
388
389                                                 id = id & ~MASK_CLEAR;
390
391                                                 tilesets.getTile(id);
392                                                 TiledMapTile tile = tilesets.getTile(id);
393                                                 if (tile != null) {
394                                                         Cell cell = createTileLayerCell(flipHorizontally, flipVertically, flipDiagonally);
395                                                         cell.setTile(tile);
396                                                         layer.setCell(x, yUp ? height - 1 - y : y, cell);
397                                                 }
398                                         }
399                                 }
400                         } else {
401                                 if (encoding.equals("base64")) {
402                                         byte[] bytes = Base64Coder.decode(data.getText());
403                                         if (compression == null) {
404                                                 int read = 0;
405                                                 for (int y = 0; y < height; y++) {
406                                                         for (int x = 0; x < width; x++) {
407
408                                                                 int id = unsignedByteToInt(bytes[read++]) | unsignedByteToInt(bytes[read++]) << 8
409                                                                         | unsignedByteToInt(bytes[read++]) << 16 | unsignedByteToInt(bytes[read++]) << 24;
410
411                                                                 final boolean flipHorizontally = ((id & FLAG_FLIP_HORIZONTALLY) != 0);
412                                                                 final boolean flipVertically = ((id & FLAG_FLIP_VERTICALLY) != 0);
413                                                                 final boolean flipDiagonally = ((id & FLAG_FLIP_DIAGONALLY) != 0);
414
415                                                                 id = id & ~MASK_CLEAR;
416
417                                                                 tilesets.getTile(id);
418                                                                 TiledMapTile tile = tilesets.getTile(id);
419                                                                 if (tile != null) {
420                                                                         Cell cell = createTileLayerCell(flipHorizontally, flipVertically, flipDiagonally);
421                                                                         cell.setTile(tile);
422                                                                         layer.setCell(x, yUp ? height - 1 - y : y, cell);
423                                                                 }
424                                                         }
425                                                 }
426                                         } else if (compression.equals("gzip")) {
427                                                 throw new GdxRuntimeException("GZIP compression not supported in GWT backend");
428                                         } else if (compression.equals("zlib")) {
429                                                 throw new GdxRuntimeException("ZLIB compression not supported in GWT backend");
430                                         }
431                                 } else {
432                                         // any other value of 'encoding' is one we're not aware of, probably a feature of a future version of Tiled
433                                         throw new GdxRuntimeException("Unrecognised encoding (" + encoding + ") for TMX Layer Data");
434                                 }
435                         }
436                         Element properties = element.getChildByName("properties");
437                         if (properties != null) {
438                                 loadProperties(layer.getProperties(), properties);
439                         }
440                         map.getLayers().add(layer);
441                 }
442         }
443
444         protected void loadObjectGroup (TiledMap map, Element element) {
445                 if (element.getName().equals("objectgroup")) {
446                         String name = element.getAttribute("name", null);
447                         MapLayer layer = new MapLayer();
448                         layer.setName(name);
449                         Element properties = element.getChildByName("properties");
450                         if (properties != null) {
451                                 loadProperties(layer.getProperties(), properties);
452                         }
453
454                         for (Element objectElement : element.getChildrenByName("object")) {
455                                 loadObject(layer, objectElement);
456                         }
457
458                         map.getLayers().add(layer);
459                 }
460         }
461
462         protected void loadObject (MapLayer layer, Element element) {
463                 if (element.getName().equals("object")) {
464                         MapObject object = null;
465
466                         int x = element.getIntAttribute("x", 0);
467                         int y = (yUp ? mapHeightInPixels - element.getIntAttribute("y", 0) : element.getIntAttribute("y", 0));
468
469                         int width = element.getIntAttribute("width", 0);
470                         int height = element.getIntAttribute("height", 0);
471
472                         if (element.getChildCount() > 0) {
473                                 Element child = null;
474                                 if ((child = element.getChildByName("polygon")) != null) {
475                                         String[] points = child.getAttribute("points").split(" ");
476                                         float[] vertices = new float[points.length * 2];
477                                         for (int i = 0; i < points.length; i++) {
478                                                 String[] point = points[i].split(",");
479                                                 vertices[i * 2] = Integer.parseInt(point[0]);
480                                                 vertices[i * 2 + 1] = Integer.parseInt(point[1]);
481                                                 if (yUp) {
482                                                         vertices[i * 2 + 1] *= -1;
483                                                 }
484                                         }
485                                         Polygon polygon = new Polygon(vertices);
486                                         polygon.setPosition(x, y);
487                                         object = new PolygonMapObject(polygon);
488                                 } else if ((child = element.getChildByName("polyline")) != null) {
489                                         String[] points = child.getAttribute("points").split(" ");
490                                         float[] vertices = new float[points.length * 2];
491                                         for (int i = 0; i < points.length; i++) {
492                                                 String[] point = points[i].split(",");
493                                                 vertices[i * 2] = Integer.parseInt(point[0]);
494                                                 vertices[i * 2 + 1] = Integer.parseInt(point[1]);
495                                                 if (yUp) {
496                                                         vertices[i * 2 + 1] *= -1;
497                                                 }
498                                         }
499                                         Polyline polyline = new Polyline(vertices);
500                                         polyline.setPosition(x, y);
501                                         object = new PolylineMapObject(polyline);
502                                 } else if ((child = element.getChildByName("ellipse")) != null) {
503                                         object = new EllipseMapObject(x, yUp ? y - height : y, width, height);
504                                 }
505                         }
506                         if (object == null) {
507                                 object = new RectangleMapObject(x, yUp ? y - height : y, width, height);
508                         }
509                         object.setName(element.getAttribute("name", null));
510                         String type = element.getAttribute("type", null);
511                         if (type != null) {
512                                 object.getProperties().put("type", type);
513                         }
514                         int gid = element.getIntAttribute("gid", -1);
515                         if (gid != -1) {
516                                 object.getProperties().put("gid", gid);
517                         }
518                         object.getProperties().put("x", x);
519                         object.getProperties().put("y", yUp ? y - height : y);
520                         object.setVisible(element.getIntAttribute("visible", 1) == 1);
521                         Element properties = element.getChildByName("properties");
522                         if (properties != null) {
523                                 loadProperties(object.getProperties(), properties);
524                         }
525                         layer.getObjects().add(object);
526                 }
527         }
528
529         protected void loadProperties (MapProperties properties, Element element) {
530                 if (element.getName().equals("properties")) {
531                         for (Element property : element.getChildrenByName("property")) {
532                                 String name = property.getAttribute("name", null);
533                                 String value = property.getAttribute("value", null);
534                                 if (value == null) {
535                                         value = property.getText();
536                                 }
537                                 properties.put(name, value);
538                         }
539                 }
540         }
541
542         protected Cell createTileLayerCell (boolean flipHorizontally, boolean flipVertically, boolean flipDiagonally) {
543                 Cell cell = new Cell();
544                 if (flipDiagonally) {
545                         if (flipHorizontally && flipVertically) {
546                                 cell.setFlipHorizontally(true);
547                                 cell.setRotation(yUp ? Cell.ROTATE_270 : Cell.ROTATE_90);
548                         } else if (flipHorizontally) {
549                                 cell.setRotation(yUp ? Cell.ROTATE_270 : Cell.ROTATE_90);
550                         } else if (flipVertically) {
551                                 cell.setRotation(yUp ? Cell.ROTATE_90 : Cell.ROTATE_270);
552                         } else {
553                                 cell.setFlipVertically(true);
554                                 cell.setRotation(yUp ? Cell.ROTATE_270 : Cell.ROTATE_90);
555                         }
556                 } else {
557                         cell.setFlipHorizontally(flipHorizontally);
558                         cell.setFlipVertically(flipVertically);
559                 }
560                 return cell;
561         }
562
563         protected static FileHandle getRelativeFileHandle (FileHandle file, String path) {
564                 StringTokenizer tokenizer = new StringTokenizer(path, "\\/");
565                 FileHandle result = file.parent();
566                 while (tokenizer.hasMoreElements()) {
567                         String token = tokenizer.nextToken();
568                         if (token.equals(".."))
569                                 result = result.parent();
570                         else {
571                                 result = result.child(token);
572                         }
573                 }
574                 return result;
575         }
576
577         protected static int unsignedByteToInt (byte b) {
578                 return (int)b & 0xFF;
579         }
580
581 }