Package games.stendhal.tools.tiled

Source Code of games.stendhal.tools.tiled.ServerTMXLoader

/* $Id: ServerTMXLoader.java,v 1.34 2011/03/05 23:48:01 martinfuchs Exp $ */
/***************************************************************************
*                   (C) Copyright 2003-2010 - Stendhal                    *
***************************************************************************
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************/
package games.stendhal.tools.tiled;

import games.stendhal.common.Base64;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
* Loads a TMX file to server so it can understand: a) The objects layer b) The
* collision layer c) The protection layer. d) All the layers that are sent to
* client e) The tileset data that is also transfered to client f) A preview of
* the zone for the minimap.
*
* Client would get the layers plus the tileset info.
*
* @author miguel
*
*/
public class ServerTMXLoader {
  private static Logger logger = Logger.getLogger(ServerTMXLoader.class);

  private StendhalMapStructure stendhalMap;

  private String xmlPath;

  private static String makeUrl(final String filename) throws MalformedURLException {
    final String url;
    if ((filename.indexOf("://") > -1) || filename.startsWith("file:")) {
      url = filename;
    } else {
      url = (new File(filename)).toURI().toURL().toString();
    }
    return url;
  }

  private static String getAttributeValue(final Node node, final String attribname) {
    final NamedNodeMap attributes = node.getAttributes();
    String att = null;
    if (attributes != null) {
      final Node attribute = attributes.getNamedItem(attribname);
      if (attribute != null) {
        att = attribute.getNodeValue();
      }
    }
    return att;
  }

  private static int getAttribute(final Node node, final String attribname, final int def) {
    final String attr = getAttributeValue(node, attribname);
    if (attr != null) {
      return Integer.parseInt(attr);
    } else {
      return def;
    }
  }

  private TileSetDefinition unmarshalTileset(final Node t) throws Exception {
    final String name = getAttributeValue(t, "name");
    final int firstGid = getAttribute(t, "firstgid", 1);

    final TileSetDefinition set = new TileSetDefinition(name, firstGid);

    final boolean hasTilesetImage = false;
    final NodeList children = t.getChildNodes();

    for (int i = 0; i < children.getLength(); i++) {
      final Node child = children.item(i);

      if (child.getNodeName().equalsIgnoreCase("image")) {
        if (hasTilesetImage) {
          continue;
        }

        set.setSource(getAttributeValue(child, "source"));
      }
    }

    return set;
  }

  /**
   * Reads properties from amongst the given children. When a "properties"
   * element is encountered, it recursively calls itself with the children of
   * this node. This function ensures backward compatibility with tmx version
   * 0.99a.
   *
   * @param children
   *            the children amongst which to find properties
   * @param props
   *            the properties object to set the properties of
   */
  @SuppressWarnings("unused")
  private static void readProperties(final NodeList children, final Properties props) {
    for (int i = 0; i < children.getLength(); i++) {
      final Node child = children.item(i);
      if ("property".equalsIgnoreCase(child.getNodeName())) {
        props.setProperty(getAttributeValue(child, "name"),
            getAttributeValue(child, "value"));
      } else if ("properties".equals(child.getNodeName())) {
        readProperties(child.getChildNodes(), props);
      }
    }
  }

  /**
   * Loads a map layer from a layer node.
   * @param t
   * @return the layer definition for the node
   * @throws Exception
   */
  private LayerDefinition readLayer(final Node t) throws Exception {
    final int layerWidth = getAttribute(t, "width", stendhalMap.width);
    final int layerHeight = getAttribute(t, "height", stendhalMap.height);

    final LayerDefinition layer = new LayerDefinition(layerWidth, layerHeight);

    final int offsetX = getAttribute(t, "x", 0);
    final int offsetY = getAttribute(t, "y", 0);

    if ((offsetX != 0) || (offsetY != 0)) {
      System.err.println("Severe error: maps has offset displacement");
    }

    layer.setName(getAttributeValue(t, "name"));

    for (Node child = t.getFirstChild(); child != null; child = child.getNextSibling()) {
      if ("data".equalsIgnoreCase(child.getNodeName())) {
        final String encoding = getAttributeValue(child, "encoding");

        if ((encoding != null) && "base64".equalsIgnoreCase(encoding)) {
          final Node cdata = child.getFirstChild();
          if (cdata != null) {
            final char[] enc = cdata.getNodeValue().trim().toCharArray();
            final byte[] dec = Base64.decode(enc);
            final ByteArrayInputStream bais = new ByteArrayInputStream(
                dec);
            InputStream is;

            final String comp = getAttributeValue(child, "compression");

            if ((comp != null) && "gzip".equalsIgnoreCase(comp)) {
              is = new GZIPInputStream(bais);
            } else {
              is = bais;
            }

            final byte[] raw = layer.exposeRaw();
            int offset = 0;

            while (offset != raw.length) {
              offset += is.read(raw, offset, raw.length - offset);
            }
          }
        }
      }
    }

    return layer;
  }

  private void buildMap(final Document doc) throws Exception {
    Node mapNode = doc.getDocumentElement();

    if (!"map".equals(mapNode.getNodeName())) {
      throw new Exception("Not a valid tmx map file.");
    }

    // Get the map dimensions and create the map
    final int mapWidth = getAttribute(mapNode, "width", 0);
    final int mapHeight = getAttribute(mapNode, "height", 0);

    if ((mapWidth > 0) && (mapHeight > 0)) {
      stendhalMap = new StendhalMapStructure(mapWidth, mapHeight);
    }

    if (stendhalMap == null) {
      throw new Exception("Couldn't locate map dimensions.");
    }

    // Load the tilesets, properties, layers and objectgroups
    for (Node sibs = mapNode.getFirstChild(); sibs != null; sibs = sibs.getNextSibling()) {
      if ("tileset".equals(sibs.getNodeName())) {
        stendhalMap.addTileset(unmarshalTileset(sibs));
      } else if ("layer".equals(sibs.getNodeName())) {
        stendhalMap.addLayer(readLayer(sibs));
      }
    }
  }

  private StendhalMapStructure unmarshal(final InputStream in) throws Exception {
    final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    Document doc;
    try {
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(true);
      factory.setExpandEntityReferences(false);
      // Xerces normally tries to retrieve the dtd, even when it's not used - and
      // dies if it fails.
      try {
        factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
      } catch (final IllegalArgumentException e) {
        logger.warn(e, e);
      }
     
      final DocumentBuilder builder = factory.newDocumentBuilder();
      doc = builder.parse(in, xmlPath);
    } catch (final SAXException e) {
      e.printStackTrace();
      throw new Exception("Error while parsing map file: " + e.toString());
    }

    buildMap(doc);
    return stendhalMap;
  }

  public StendhalMapStructure readMap(final String filename) throws Exception {
    xmlPath = filename.substring(0,
        filename.lastIndexOf(File.separatorChar) + 1);

    InputStream is = getClass().getClassLoader().getResourceAsStream(
        filename);

    if (is == null) {
      final String xmlFile = makeUrl(filename);
      // xmlPath = makeUrl(xmlPath);

      final URL url = new URL(xmlFile);
      is = url.openStream();
    }

    // Wrap with GZIP decoder for .tmx.gz files
    if (filename.endsWith(".gz")) {
      is = new GZIPInputStream(is);
    }

    final StendhalMapStructure unmarshalledMap = unmarshal(is);
    unmarshalledMap.setFilename(filename);

    return unmarshalledMap;
  }

  public static void main(final String[] args) throws Exception {
    System.out.println("Test: loading map");

    StendhalMapStructure map = null;
    /*
     * long start=System.currentTimeMillis(); for(int i=0;i<90;i++) {
     * map=new
     * ServerTMXLoader().readMap("D:/Desarrollo/stendhal/tiled/interiors/abstract/afterlife.tmx");
     * map=new ServerTMXLoader().readMap("D:/Desarrollo/stendhal/tiled/Level
     * 0/ados/city_n.tmx"); map=new
     * ServerTMXLoader().readMap("D:/Desarrollo/stendhal/tiled/Level
     * 0/semos/city.tmx"); map=new
     * ServerTMXLoader().readMap("D:/Desarrollo/stendhal/tiled/Level
     * 0/nalwor/city.tmx"); map=new
     * ServerTMXLoader().readMap("D:/Desarrollo/stendhal/tiled/Level
     * 0/orril/castle.tmx"); }
     *
     * System.out.println("Time ellapsed (ms):
     * "+(System.currentTimeMillis()-start)); /
     */
    map = new ServerTMXLoader().readMap("tiled/Level 0/semos/village_w.tmx");
    map.build();
    System.out.printf("MAP W: %d H:%d\n", map.width, map.height);
    final List<TileSetDefinition> tilesets = map.getTilesets();
    for (final TileSetDefinition set : tilesets) {
      System.out.printf("TILESET firstGID: '%d' name: '%s'\n",
          set.getFirstGid(), set.getSource());
    }

    final List<LayerDefinition> layers = map.getLayers();
    for (final LayerDefinition layer : layers) {
      System.out.printf("LAYER name: %s\n", layer.getName());
      final int w = layer.getWidth();
      final int h = layer.getHeight();

      for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
          final int gid = layer.getTileAt(x, y);
          if (x == w - 1) {
            System.out.print(gid);
          } else {
            System.out.print(gid + ",");
          }
         
        }
        System.out.println();
      }
    }

  }

  public static StendhalMapStructure load(final String filename) throws Exception {
    return new ServerTMXLoader().readMap(filename);
  }
}
TOP

Related Classes of games.stendhal.tools.tiled.ServerTMXLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.