Package games.stendhal.tools

Source Code of games.stendhal.tools.TilesetConverter$Mapping

/* $Id: TilesetConverter.java,v 1.5 2010/09/19 01:28:53 nhnb 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;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import tiled.core.Map;
import tiled.core.MapLayer;
import tiled.core.Tile;
import tiled.core.TileLayer;
import tiled.core.TileSet;
import tiled.io.xml.XMLMapTransformer;
import tiled.io.xml.XMLMapWriter;
import tiled.mapeditor.util.cutter.BasicTileCutter;

/**
* A tool for converting tileset mappings.
*
* The new mapping is lines in format:
* <p>
* [oldtilesetpath]:[tilenumber]:[newtilesetpath]:[tilenumber]
* <p>
* These are read from the standard input.
* <p>
* Description of the process:
* <p>
* <ol>
* <li>Loads the map
* <li>Adds any new tilesets defined in the mapping if needed
* <li>Converts the old tileset mappings to new
* <li>Removes any unused tilesets from the map
* <li>Saves the map
* </ol>
*/
public class TilesetConverter {
  private Mapping mapping = new Mapping();
  /**
   * For quick lookup by tileset name
   */
  private HashMap<String, TileSet> setByName = new HashMap<String, TileSet>();
 
  /**
   * Helper to make <code>namePattern</code> construction a bit more readable.
   */
  private String sep =  Pattern.quote(File.separator);
  /**
   * A pattern for picking the name of the tileset from the image name.
   * The trailing "dir/image" without ".png"
   */
  Pattern namePattern = Pattern.compile(".*" + sep + "([^" + sep + "]+"
      + sep + "[^" + sep + "]+)\\.png$");
 
  /**
   * For returning the translated tile information.
   */
  private static class TileInfo {
    public String file;
    public int index;
   
    public TileInfo(String file, int index) {
      this.file = file;
      this.index = index;
    }
  }

  /**
   * A class for keeping the tile translation information
   */
  private static class Mapping {
    private HashMap<String, HashMap<Integer, TileInfo>> mappings = new HashMap<String, HashMap<Integer, TileInfo>>();
    private HashSet<String> newTilesets = new HashSet<String>();
   
    /**
     * Add a new translation mapping.
     *
     * @param oldImg path to the old image file
     * @param oldIndex index of the tile to be translated
     * @param newImg path to the new image file
     * @param newIndex index of the translated tile
     */
    public void addMapping(String oldImg, int oldIndex, String newImg, int newIndex) {
      newTilesets.add(newImg);
      HashMap<Integer, TileInfo> mapping = mappings.get(oldImg);
      if (mapping == null) {
        mapping = new HashMap<Integer, TileInfo>();
        mappings.put(oldImg, mapping);
      }
      mapping.put(oldIndex, new TileInfo(newImg, newIndex));
    }
   
    /**
     * Get a translated tile corresponding to an old tile.
     *
     * @param oldImg path to the old image file
     * @param index index of the tile in the image
     * @return new tile information, or <code>null</code>
     * if the old tile should be kept
     */
    public TileInfo getTile(String oldImg, int index) {
      TileInfo result = null;
      HashMap<Integer, TileInfo> mapping = mappings.get(oldImg);
      if (mapping != null) {
        result = mapping.get(index);
      }
      return result;
    }
   
    /**
     * Get the new tilesets the translation adds to the map.
     *
     * @return an iterable set of image paths
     */
    public Iterable<String> getNewSets() {
      return newTilesets;
    }
  }
 
  /**
   * Check whether a tileset is in use by a map.
   *
   * @param map the map to be checked
   * @param tileset the tileset to be checked
   * @return true iff the tileset is in use
   */
  private boolean isUsedTileset(final Map map, final TileSet tileset) {
    for (final Iterator< ? > tiles = tileset.iterator(); tiles.hasNext();) {
      final Tile tile = (Tile) tiles.next();

      for (final MapLayer layer : map) {
        if ((layer instanceof TileLayer) && (((TileLayer) layer).isUsed(tile))) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Remove any tilesets in a map that are not actually in use.
   *
   * @param map the map to be broomed
   */
  private void removeUnusedTilesets(final Map map) {
    for (final Iterator< ? > sets = map.getTilesets().iterator(); sets.hasNext();) {
      final TileSet tileset = (TileSet) sets.next();

      if (!isUsedTileset(map, tileset)) {
        sets.remove();
      }
    }
  }
 
  /**
   * Construct a nice name for a tileset based on the image name.
   * The substring used for the name is specified in <code>namePattern</code>
   *
   * @param name image path
   * @return a human readable tileset name
   */
  private String constructTilesetName(String name) {
    Matcher matcher = namePattern.matcher(name);
   
    if (matcher.find()) {
      name = matcher.group(1);
    }
    return name;
  }
 
  /**
   * Add all the tilesets that the translation mapping uses to a map.
   *
   * @param map the map to add the tilesets to
   * @throws IOException
   */
  private void addNewTilesets(Map map) throws IOException {
    // First build up the mapping of old sets
    for (TileSet set : map.getTilesets()) {
      setByName.put(set.getTilebmpFile(), set);
    }   
   
    // then add all missing new sets
    for (String name : mapping.getNewSets()) {
      if (name.equals("")) {
        continue;
      }
     
      if (!setByName.containsKey(name)) {
        // The tileset's not yet included. Add it to the map
        TileSet set = new TileSet();
        set.setName(constructTilesetName(name));
        BasicTileCutter cutter = new BasicTileCutter(32, 32, 0, 0);
        set.importTileBitmap(name, cutter);
       
        setByName.put(name, set);
        map.addTileset(set);
      }
    }
  }
 
  /**
   * Find the translated tile that corresponds to a tile
   * in the original tile mapping.
   * 
   * @param tile The tile to be translated
   * @return Translated tile
   */
  Tile translateTile(Tile tile) {
    int id = tile.getId();
    TileSet set = tile.getTileSet();
    TileInfo info = mapping.getTile(set.getTilebmpFile(), id);
    if (info != null) {
      TileSet newSet = setByName.get(info.file);
      tile = newSet.getTile(info.index);
    }
   
    return tile;
  }
 
  /**
   * Translate all the tiles of a layer.
   *
   * @param layer the layer to be translated
   */
  private void translateLayer(MapLayer layer) {
    if (!(layer instanceof TileLayer)) {
      return;
    }
    TileLayer tileLayer = (TileLayer) layer;
    for (int y = 0; y < tileLayer.getHeight(); y++) {
      for (int x = 0; x < tileLayer.getWidth(); x++) {
        Tile tile = tileLayer.getTileAt(x, y);
        if (tile != null) {
          tile = translateTile(tile);
          tileLayer.setTileAt(x, y, tile);
        }
      }
    }
  }
 
  /**
   * Translate all the layers of a map.
   *
   * @param map the map to be converted
   */
  private void translateMap(Map map) {
    for (MapLayer layer : map) {
      translateLayer(layer);
    }
  }

  /**
   * Converts a map file according to the tile mapping.
   * 
   * @param tmxFile the map to be converted
   * @throws Exception
   */
  private void convert(final String tmxFile) throws Exception {
    final File file = new File(tmxFile);

    final String filename = file.getAbsolutePath();
    final Map map = new XMLMapTransformer().readMap(filename);
    addNewTilesets(map);
    translateMap(map);
    removeUnusedTilesets(map);
    new XMLMapWriter().writeMap(map, filename);
  }
 
  /**
   * Load tile mapping information from the standard input.
   *
   * @param path The path of the <b>map</b>. Needed for proper
   * conversion of the tileset paths.
   * @throws IOException
   */
  private void loadMapping(String path) throws IOException {
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
   
    // needed for constructing the full path of the tilesets
    File f = new File(path);
    String dir = f.getParent();   

    String line;
    while ((line = input.readLine()) != null) {
      String[] elements = line.split(":", -1);
      if (elements.length != 4) {
        System.err.println("Invalid line: '" + line + "'");
      } else {
        int newIndex = 0;
        if (!"".equals(elements[3])) {
          newIndex = Integer.parseInt(elements[3]);
        }
       
        /*
         * Oh, yay. Tiled likes to translate the filenames internally
         * to full paths.
         * Great fun with java to compare the paths when the system
         * allows no playing with directories whatsoever. We can't rely
         * on the current directory being the same as that of the map.
         * Building the full path from scratch, and hope for the best. 
         */
        String path1 = (new File(dir + File.separator + elements[0])).getCanonicalPath();
        String path2 = (new File(dir + File.separator + elements[2])).getCanonicalPath();
       
        mapping.addMapping(path1, Integer.parseInt(elements[1]), path2, newIndex);
      }
    }
  }

  public static void main(final String[] args) throws Exception {
    if (args.length < 1) {
      System.out.println("usage: java games.stendhal.tools.TilesetConverter <tmx file>");
      return;
    }
   
    final TilesetConverter converter = new TilesetConverter();
    converter.loadMapping(args[0]);

    converter.convert(args[0]);
  }
}
TOP

Related Classes of games.stendhal.tools.TilesetConverter$Mapping

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.