From: Kimmo R. <ki...@us...> - 2010-01-26 09:14:24
|
Update of /cvsroot/arianne/stendhal/src/games/stendhal/tools In directory sfp-cvsdas-1.v30.ch3.sourceforge.com:/tmp/cvs-serv22493/src/games/stendhal/tools Added Files: TilesetConverter.java Log Message: added a tool for changing tile mappings --- NEW FILE: TilesetConverter.java --- package games.stendhal.tools; /*************************************************************************** * (C) Copyright 2010 - Marauroa * *************************************************************************** *************************************************************************** * * * 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. * * * ***************************************************************************/ 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 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; /** * 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>(); /** * 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(); } } } /** * 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); } /* * 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. */ File f = new File(map.getFilename()); String dir = f.getParent(); // then add all missing new sets for (String name : mapping.getNewSets()) { if (name.equals("")) { continue; } // try to reconstruct the full path of the tileset File file = new File(dir + File.separator + name); name = file.getCanonicalPath(); if (!setByName.containsKey(name)) { // The tileset's not yet included. Add it to the map TileSet set = new TileSet(); set.setTilesetImageFilename(name); set.setName(name); 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) { tile = new Tile(); tile.setTileSet(setByName.get(info.file)); tile.setId(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.getHeight(); 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]); } // get the full paths because that's what tiled internally uses 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]); } } |