Behold, Nenya, Ring of Water and repository for our media and animation related

goodies, both Java 2D and LWJGL/JME 3D.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2006-06-23 18:07:28 +00:00
commit c2117ee86d
570 changed files with 61913 additions and 0 deletions
@@ -0,0 +1,82 @@
//
// $Id: BundleUtil.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import com.samskivert.io.StreamUtil;
import com.threerings.resource.ResourceBundle;
/**
* Bundle related utility functions.
*/
public class BundleUtil
{
/** The path to the metadata resource that we will attempt to load
* from our resource set. */
public static final String METADATA_PATH = "tsbundles.dat";
/**
* Extracts, but does not initialize, a serialized tileset bundle
* instance from the supplied resource bundle.
*/
public static TileSetBundle extractBundle (ResourceBundle bundle)
throws IOException, ClassNotFoundException
{
// unserialize the tileset bundles array
InputStream tbin = null;
try {
tbin = bundle.getResource(METADATA_PATH);
ObjectInputStream oin = new ObjectInputStream(
new BufferedInputStream(tbin));
TileSetBundle tsb = (TileSetBundle)oin.readObject();
return tsb;
} finally {
StreamUtil.close(tbin);
}
}
/**
* Extracts, but does not initialize, a serialized tileset bundle
* instance from the supplied file.
*/
public static TileSetBundle extractBundle (File file)
throws IOException, ClassNotFoundException
{
// unserialize the tileset bundles array
FileInputStream fin = new FileInputStream(file);
try {
ObjectInputStream oin = new ObjectInputStream(
new BufferedInputStream(fin));
TileSetBundle tsb = (TileSetBundle)oin.readObject();
return tsb;
} finally {
StreamUtil.close(fin);
}
}
}
@@ -0,0 +1,232 @@
//
// $Id: BundledTileSetRepository.java 3608 2005-06-20 22:59:07Z andrzej $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle;
import java.util.HashMap;
import java.util.Iterator;
import com.samskivert.io.PersistenceException;
import com.samskivert.util.HashIntMap;
import com.threerings.resource.ResourceBundle;
import com.threerings.resource.ResourceManager;
import com.threerings.media.Log;
import com.threerings.media.image.ImageManager;
import com.threerings.media.tile.IMImageProvider;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileSetRepository;
/**
* Loads tileset data from a set of resource bundles.
*
* @see ResourceManager
*/
public class BundledTileSetRepository
implements TileSetRepository
{
/**
* Constructs a repository which will obtain its resource set from the
* supplied resource manager.
*
* @param rmgr the resource manager from which to obtain our resource
* set.
* @param imgr the image manager through which we will configure the
* tile sets to load their images, or <code>null</code> if image tiles
* should not be loaded (only the tile metadata)
* @param name the name of the resource set from which we will be
* loading our tile data.
*/
public BundledTileSetRepository (final ResourceManager rmgr,
final ImageManager imgr,
final String name)
{
_imgr = imgr;
// unpack our bundles in the background
new Thread(new Runnable() {
public void run () {
initBundles(rmgr, name);
}
}).start();
}
/**
* Initializes our bundles,
*/
protected void initBundles (ResourceManager rmgr, String name)
{
// first we obtain the resource set from which we will load up our
// tileset bundles
ResourceBundle[] rbundles = rmgr.getResourceSet(name);
// sanity check
if (rbundles == null) {
Log.warning("Unable to fetch tileset resource set " +
"[name=" + name + "]. Perhaps it's not defined " +
"in the resource manager config?");
return;
}
HashIntMap idmap = new HashIntMap();
HashMap namemap = new HashMap();
// iterate over the resource bundles in the set, loading up the
// tileset bundles in each resource bundle
for (int i = 0; i < rbundles.length; i++) {
addBundle(idmap, namemap, rbundles[i]);
}
// fill in our bundles array and wake up any waiters
synchronized (this) {
_idmap = idmap;
_namemap = namemap;
notifyAll();
}
}
/**
* Registers the bundle with the tileset repository, overriding any
* bundle with the same id or name.
*/
public void addBundle (ResourceBundle bundle)
{
addBundle(_idmap, _namemap, bundle);
}
/**
* Extracts the tileset bundle from the supplied resource bundle
* and registers it.
*/
protected void addBundle (HashIntMap idmap, HashMap namemap,
ResourceBundle bundle)
{
try {
TileSetBundle tsb = BundleUtil.extractBundle(bundle);
// initialize it and add it to the list
tsb.init(bundle);
addBundle(idmap, namemap, tsb);
} catch (Exception e) {
Log.warning("Unable to load tileset bundle '" +
BundleUtil.METADATA_PATH + "' from resource " +
"bundle [rbundle=" + bundle +
", error=" + e + "].");
Log.logStackTrace(e);
}
}
/**
* Adds the tilesets in the supplied bundle to our tileset mapping
* tables. Any tilesets with the same name or id will be overwritten.
*/
protected void addBundle (HashIntMap idmap, HashMap namemap,
TileSetBundle bundle)
{
IMImageProvider improv = (_imgr == null) ?
null : new IMImageProvider(_imgr, bundle);
// map all of the tilesets in this bundle
for (Iterator iter = bundle.entrySet().iterator(); iter.hasNext(); ) {
HashIntMap.Entry entry = (HashIntMap.Entry)iter.next();
Integer tsid = (Integer)entry.getKey();
TileSet tset = (TileSet)entry.getValue();
tset.setImageProvider(improv);
idmap.put(tsid.intValue(), tset);
namemap.put(tset.getName(), tsid);
}
}
// documentation inherited from interface
public Iterator enumerateTileSetIds ()
throws PersistenceException
{
waitForBundles();
return _idmap.keySet().iterator();
}
// documentation inherited from interface
public Iterator enumerateTileSets ()
throws PersistenceException
{
waitForBundles();
return _idmap.values().iterator();
}
// documentation inherited from interface
public TileSet getTileSet (int tileSetId)
throws NoSuchTileSetException, PersistenceException
{
waitForBundles();
TileSet tset = (TileSet)_idmap.get(tileSetId);
if (tset == null) {
throw new NoSuchTileSetException(tileSetId);
}
return tset;
}
// documentation inherited from interface
public int getTileSetId (String setName)
throws NoSuchTileSetException, PersistenceException
{
waitForBundles();
Integer tsid = (Integer)_namemap.get(setName);
if (tsid != null) {
return tsid.intValue();
}
throw new NoSuchTileSetException(setName);
}
// documentation inherited from interface
public TileSet getTileSet (String setName)
throws NoSuchTileSetException, PersistenceException
{
waitForBundles();
TileSet tset = null;
Integer tsid = (Integer)_namemap.get(setName);
if (tsid != null) {
return getTileSet(tsid.intValue());
}
throw new NoSuchTileSetException(setName);
}
/** Used to allow bundle unpacking to proceed asynchronously. */
protected synchronized void waitForBundles ()
{
while (_idmap == null) {
try {
wait();
} catch (InterruptedException ie) {
Log.warning("Interrupted waiting for bundles " + ie);
}
}
}
/** The image manager via which we load our images. */
protected ImageManager _imgr;
/** A mapping from tileset id to tileset. */
protected HashIntMap _idmap;
/** A mapping from tileset name to tileset id. */
protected HashMap _namemap;
}
@@ -0,0 +1,148 @@
//
// $Id: TileSetBundle.java 4007 2006-04-10 08:59:30Z mdb $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Iterator;
import javax.imageio.ImageIO;
import com.samskivert.util.HashIntMap;
import com.threerings.resource.ResourceBundle;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageDataProvider;
import com.threerings.media.tile.TileSet;
/**
* A tileset bundle is used to load up tilesets by id from a persistent
* bundle of tilesets stored on the local filesystem.
*/
public class TileSetBundle extends HashIntMap
implements Serializable, ImageDataProvider
{
/**
* Initializes this resource bundle with a reference to the jarfile
* from which it was loaded and from which it can load image data. The
* image manager will be used to decode the images.
*/
public void init (ResourceBundle bundle)
{
_bundle = bundle;
}
/**
* Returns the bundle file from which our tiles are fetched.
*/
public File getSource ()
{
return _bundle.getSource();
}
/**
* Adds a tileset to this tileset bundle.
*/
public final void addTileSet (int tileSetId, TileSet set)
{
put(tileSetId, set);
}
/**
* Retrieves a tileset from this tileset bundle.
*/
public final TileSet getTileSet (int tileSetId)
{
return (TileSet)get(tileSetId);
}
/**
* Enumerates the tileset ids in this tileset bundle.
*/
public Iterator enumerateTileSetIds ()
{
return keySet().iterator();
}
/**
* Enumerates the tilesets in this tileset bundle.
*/
public Iterator enumerateTileSets ()
{
return values().iterator();
}
// documentation inherited from interface
public String getIdent ()
{
return "tsb:" + _bundle.getSource();
}
// documentation inherited from interface
public BufferedImage loadImage (String path)
throws IOException
{
if (path.endsWith(FastImageIO.FILE_SUFFIX)) {
return FastImageIO.read(_bundle.getResourceFile(path));
} else {
return ImageIO.read(_bundle.getResourceFile(path));
}
}
// custom serialization process
private void writeObject (ObjectOutputStream out)
throws IOException
{
out.writeInt(size());
Iterator entries = intEntrySet().iterator();
while (entries.hasNext()) {
IntEntry entry = (IntEntry)entries.next();
out.writeInt(entry.getIntKey());
out.writeObject(entry.getValue());
}
}
// custom unserialization process
private void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
int count = in.readInt();
for (int i = 0; i < count; i++) {
int tileSetId = in.readInt();
TileSet set = (TileSet)in.readObject();
put(tileSetId, set);
}
}
/** That from which we load our tile images. */
protected transient ResourceBundle _bundle;
/** Increase this value when object's serialized state is impacted by
* a class change (modification of fields, inheritance). */
private static final long serialVersionUID = 2;
}
@@ -0,0 +1,93 @@
//
// $Id: DumpBundle.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle.tools;
import java.io.File;
import java.util.Iterator;
import com.threerings.resource.ResourceManager;
import com.threerings.resource.ResourceBundle;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.bundle.BundleUtil;
import com.threerings.media.tile.bundle.TileSetBundle;
/**
* Dumps the contents of a tileset bundle to stdout (just the serialized
* object info, not the image data).
*/
public class DumpBundle
{
public static void main (String[] args)
{
boolean dumpTiles = false;
if (args.length < 1) {
String usage = "Usage: DumpBundle [-tiles] " +
"(bundle.jar|tsbundle.dat) [...]";
System.err.println(usage);
System.exit(-1);
}
// create a resource and image manager in case they want to dump
// the tiles
ResourceManager rmgr = new ResourceManager("rsrc");
for (int i = 0; i < args.length; i++) {
// oh the hackery
if (args[i].equals("-tiles")) {
dumpTiles = true;
continue;
}
File file = new File(args[i]);
try {
TileSetBundle tsb = null;
if (args[i].endsWith(".jar")) {
ResourceBundle bundle = new ResourceBundle(file);
tsb = BundleUtil.extractBundle(bundle);
tsb.init(bundle);
} else {
tsb = BundleUtil.extractBundle(file);
}
Iterator tsids = tsb.enumerateTileSetIds();
while (tsids.hasNext()) {
Integer tsid = (Integer)tsids.next();
TileSet set = tsb.getTileSet(tsid.intValue());
System.out.println(tsid + " => " + set);
if (dumpTiles) {
for (int t = 0, nn = set.getTileCount(); t < nn; t++) {
System.out.println(" " + t + " => " +
set.getTile(t));
}
}
}
} catch (Exception e) {
System.err.println("Error dumping bundle [path=" + args[i] +
", error=" + e + "].");
e.printStackTrace();
}
}
}
}
@@ -0,0 +1,482 @@
//
// $Id: TileSetBundler.java 3788 2005-12-20 02:09:18Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle.tools;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.Deflater;
import javax.imageio.ImageIO;
import org.apache.commons.digester.Digester;
import org.apache.commons.io.CopyUtils;
import org.xml.sax.SAXException;
import com.samskivert.io.PersistenceException;
import com.threerings.media.Log;
import com.threerings.media.image.FastImageIO;
import com.threerings.media.image.ImageUtil;
import com.threerings.media.tile.ImageProvider;
import com.threerings.media.tile.ObjectTileSet;
import com.threerings.media.tile.SimpleCachingImageProvider;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileSetIDBroker;
import com.threerings.media.tile.TrimmedObjectTileSet;
import com.threerings.media.tile.UniformTileSet;
import com.threerings.media.tile.bundle.BundleUtil;
import com.threerings.media.tile.bundle.TileSetBundle;
import com.threerings.media.tile.tools.xml.TileSetRuleSet;
/**
* The tileset bundler is used to create tileset bundles from a set of XML
* tileset descriptions in a bundle description file. The bundles contain
* a serialized representation of the tileset objects along with the
* actual image files referenced by those tilesets.
*
* <p> The organization of the bundle description file is customizable
* based on the an XML configuration file provided to the tileset bundler
* when constructed. The bundler configuration maps XML paths to tileset
* parsers. An example configuration follows:
*
* <pre>
* &lt;bundler-config&gt;
* &lt;mapping&gt;
* &lt;path&gt;bundle.tilesets.uniform&lt;/path&gt;
* &lt;ruleset&gt;
* com.threerings.media.tile.tools.xml.UniformTileSetRuleSet
* &lt;/ruleset&gt;
* &lt;/mapping&gt;
* &lt;mapping&gt;
* &lt;path&gt;bundle.tilesets.object&lt;/path&gt;
* &lt;ruleset&gt;
* com.threerings.media.tile.tools.xml.ObjectTileSetRuleSet
* &lt;/ruleset&gt;
* &lt;/mapping&gt;
* &lt;/bundler-config&gt;
* </pre>
*
* This configuration would be used to parse a bundle description that
* looked something like the following:
*
* <pre>
* &lt;bundle&gt;
* &lt;tilesets&gt;
* &lt;uniform&gt;
* &lt;tileset&gt;
* &lt;!-- ... --&gt;
* &lt;/tileset&gt;
* &lt;/uniform&gt;
* &lt;object&gt;
* &lt;tileset&gt;
* &lt;!-- ... --&gt;
* &lt;/tileset&gt;
* &lt;/object&gt;
* &lt;/tilesets&gt;
* </pre>
*
* The class specified in the <code>ruleset</code> element must derive
* from {@link TileSetRuleSet}. The images that will be included in the
* bundle must be in the same directory as the bundle description file and
* the tileset descriptions must reference the images without a preceding
* path.
*/
public class TileSetBundler
{
/**
* Constructs a tileset bundler with the specified path to a bundler
* configuration file. The configuration file will be loaded and used
* to configure this tileset bundler.
*/
public TileSetBundler (String configPath)
throws IOException
{
this(new File(configPath));
}
/**
* Constructs a tileset bundler with the specified bundler config
* file.
*/
public TileSetBundler (File configFile)
throws IOException
{
// we parse our configuration with a digester
Digester digester = new Digester();
// push our mappings array onto the stack
ArrayList mappings = new ArrayList();
digester.push(mappings);
// create a mapping object for each mapping entry and append it to
// our mapping list
digester.addObjectCreate("bundler-config/mapping",
Mapping.class.getName());
digester.addSetNext("bundler-config/mapping",
"add", "java.lang.Object");
// configure each mapping object with the path and ruleset
digester.addCallMethod("bundler-config/mapping", "init", 2);
digester.addCallParam("bundler-config/mapping/path", 0);
digester.addCallParam("bundler-config/mapping/ruleset", 1);
// now go like the wind
FileInputStream fin = new FileInputStream(configFile);
try {
digester.parse(fin);
} catch (SAXException saxe) {
String errmsg = "Failure parsing bundler config file " +
"[file=" + configFile.getPath() + "]";
throw (IOException) new IOException(errmsg).initCause(saxe);
}
fin.close();
// create our digester
_digester = new Digester();
// use the mappings we parsed to configure our actual digester
int msize = mappings.size();
for (int i = 0; i < msize; i++) {
Mapping map = (Mapping)mappings.get(i);
try {
TileSetRuleSet ruleset = (TileSetRuleSet)
Class.forName(map.ruleset).newInstance();
// configure the ruleset
ruleset.setPrefix(map.path);
// add it to the digester
_digester.addRuleSet(ruleset);
// and add a rule to stick the parsed tilesets onto the
// end of an array list that we'll put on the stack
_digester.addSetNext(map.path + TileSetRuleSet.TILESET_PATH,
"add", "java.lang.Object");
} catch (Exception e) {
String errmsg = "Unable to create tileset rule set " +
"instance [mapping=" + map + "].";
throw (IOException) new IOException(errmsg).initCause(e);
}
}
}
/**
* Creates a tileset bundle at the location specified by the
* <code>targetPath</code> parameter, based on the description
* provided via the <code>bundleDesc</code> parameter.
*
* @param idBroker the tileset id broker that will be used to map
* tileset names to tileset ids.
* @param bundleDesc a file object pointing to the bundle description
* file.
* @param targetPath the path of the tileset bundle file that will be
* created.
*
* @exception IOException thrown if an error occurs reading, writing
* or processing anything.
*/
public void createBundle (
TileSetIDBroker idBroker, File bundleDesc, String targetPath)
throws IOException
{
createBundle(idBroker, bundleDesc, new File(targetPath));
}
/**
* Creates a tileset bundle at the location specified by the
* <code>targetPath</code> parameter, based on the description
* provided via the <code>bundleDesc</code> parameter.
*
* @param idBroker the tileset id broker that will be used to map
* tileset names to tileset ids.
* @param bundleDesc a file object pointing to the bundle description
* file.
* @param target the tileset bundle file that will be created.
*
* @return true if the bundle was rebuilt, false if it was not because
* the bundle file was newer than all involved source files.
*
* @exception IOException thrown if an error occurs reading, writing
* or processing anything.
*/
public boolean createBundle (
TileSetIDBroker idBroker, final File bundleDesc, File target)
throws IOException
{
// stick an array list on the top of the stack into which we will
// collect parsed tilesets
ArrayList sets = new ArrayList();
_digester.push(sets);
// parse the tilesets
FileInputStream fin = new FileInputStream(bundleDesc);
try {
_digester.parse(fin);
} catch (SAXException saxe) {
String errmsg = "Failure parsing bundle description file " +
"[path=" + bundleDesc.getPath() + "]";
throw (IOException) new IOException(errmsg).initCause(saxe);
} finally {
fin.close();
}
// we want to make sure that at least one of the tileset image
// files or the bundle definition file is newer than the bundle
// file, otherwise consider the bundle up to date
long newest = bundleDesc.lastModified();
// create a tileset bundle to hold our tilesets
TileSetBundle bundle = new TileSetBundle();
// add all of the parsed tilesets to the tileset bundle
try {
for (int i = 0; i < sets.size(); i++) {
TileSet set = (TileSet)sets.get(i);
String name = set.getName();
// let's be robust
if (name == null) {
Log.warning("Tileset was parsed, but received no name " +
"[set=" + set + "]. Skipping.");
continue;
}
// make sure this tileset's image file exists and check
// it's last modified date
File tsfile = new File(bundleDesc.getParent(),
set.getImagePath());
if (!tsfile.exists()) {
System.err.println("Tile set missing image file " +
"[bundle=" + bundleDesc.getPath() +
", name=" + set.getName() +
", imgpath=" + tsfile.getPath() + "].");
continue;
}
if (tsfile.lastModified() > newest) {
newest = tsfile.lastModified();
}
// assign a tilset id to the tileset and bundle it
try {
int tileSetId = idBroker.getTileSetID(name);
bundle.addTileSet(tileSetId, set);
} catch (PersistenceException pe) {
String errmsg = "Failure obtaining a tileset id for " +
"tileset [set=" + set + "].";
throw (IOException) new IOException(errmsg).initCause(pe);
}
}
// clear out our array list in preparation for another go
sets.clear();
} finally {
// before we go, we have to commit our brokered tileset ids
// back to the broker's persistent store
try {
idBroker.commit();
} catch (PersistenceException pe) {
Log.warning("Failure committing brokered tileset ids " +
"back to broker's persistent store " +
"[error=" + pe + "].");
}
}
// see if our newest file is newer than the tileset bundle
if (newest < target.lastModified()) {
return false;
}
// create an image provider for loading our tileset images
SimpleCachingImageProvider improv = new SimpleCachingImageProvider() {
protected BufferedImage loadImage (String path)
throws IOException {
return ImageIO.read(new File(bundleDesc.getParent(), path));
}
};
return createBundle(target, bundle, improv, bundleDesc.getParent());
}
/**
* Finish the creation of a tileset bundle jar file.
*
* @param target the tileset bundle file that will be created.
* @param bundle contains the tilesets we'd like to save out to the
* bundle.
* @param improv the image provider.
* @param imageBase the base directory for getting images for non
* ObjectTileSet tilesets.
*/
public static boolean createBundle (
File target, TileSetBundle bundle, ImageProvider improv,
String imageBase)
throws IOException
{
// now we have to create the actual bundle file
FileOutputStream fout = new FileOutputStream(target);
Manifest manifest = new Manifest();
JarOutputStream jar = new JarOutputStream(fout, manifest);
jar.setLevel(Deflater.BEST_COMPRESSION);
try {
// write all of the image files to the bundle, converting the
// tilesets to trimmed tilesets in the process
Iterator iditer = bundle.enumerateTileSetIds();
while (iditer.hasNext()) {
int tileSetId = ((Integer)iditer.next()).intValue();
TileSet set = bundle.getTileSet(tileSetId);
String imagePath = set.getImagePath();
// sanity checks
if (imagePath == null) {
Log.warning("Tileset contains no image path " +
"[set=" + set + "]. It ain't gonna work.");
continue;
}
// if this is an object tileset, we can't trim it!
if (set instanceof ObjectTileSet) {
// set the tileset up with an image provider; we
// need to do this so that we can trim it!
set.setImageProvider(improv);
// we're going to trim it, so adjust the path
imagePath = adjustImagePath(imagePath);
jar.putNextEntry(new JarEntry(imagePath));
try {
// create a trimmed object tileset, which will
// write the trimmed tileset image to the jar
// output stream
TrimmedObjectTileSet tset =
TrimmedObjectTileSet.trimObjectTileSet(
(ObjectTileSet)set, jar);
tset.setImagePath(imagePath);
// replace the original set with the trimmed
// tileset in the tileset bundle
bundle.addTileSet(tileSetId, tset);
} catch (Exception e) {
System.err.println("Error adding tileset to bundle " +
"[set=" + set.getName() +
", ipath=" + imagePath + "].");
e.printStackTrace(System.err);
// replace the tileset with an error tileset
UniformTileSet ets = new UniformTileSet();
ets.setName(set.getName());
ets.setWidth(50);
ets.setHeight(50);
ets.setImagePath(imagePath);
bundle.addTileSet(tileSetId, ets);
// and write an error image to the jar file
ImageIO.write(ImageUtil.createErrorImage(50, 50),
"PNG", jar);
}
} else {
// read the image file and convert it to our custom
// format in the bundle
File ifile = new File(imageBase, imagePath);
try {
BufferedImage image = ImageIO.read(ifile);
if (FastImageIO.canWrite(image)) {
imagePath = adjustImagePath(imagePath);
jar.putNextEntry(new JarEntry(imagePath));
set.setImagePath(imagePath);
FastImageIO.write(image, jar);
} else {
jar.putNextEntry(new JarEntry(imagePath));
FileInputStream imgin = new FileInputStream(ifile);
CopyUtils.copy(imgin, jar);
}
} catch (Exception e) {
String msg = "Failure bundling image " + ifile +
": " + e;
throw (IOException) new IOException(msg).initCause(e);
}
}
}
// now write a serialized representation of the tileset bundle
// object to the bundle jar file
JarEntry entry = new JarEntry(BundleUtil.METADATA_PATH);
jar.putNextEntry(entry);
ObjectOutputStream oout = new ObjectOutputStream(jar);
oout.writeObject(bundle);
oout.flush();
// finally close up the jar file and call ourself done
jar.close();
return true;
} catch (Exception e) {
// remove the incomplete jar file and rethrow the exception
jar.close();
if (!target.delete()) {
Log.warning("Failed to close botched bundle '" + target + "'.");
}
String errmsg = "Failed to create bundle " + target + ": " + e;
throw (IOException) new IOException(errmsg).initCause(e);
}
}
/** Replaces the image suffix with <code>.raw</code>. */
protected static String adjustImagePath (String imagePath)
{
int didx = imagePath.lastIndexOf(".");
return ((didx == -1) ? imagePath :
imagePath.substring(0, didx)) + ".raw";
}
/** Used to parse our configuration. */
public static class Mapping
{
public String path;
public String ruleset;
public void init (String path, String ruleset)
{
this.path = path;
this.ruleset = ruleset;
}
public String toString ()
{
return "[path=" + path + ", ruleset=" + ruleset + "]";
}
}
/** The digester we use to parse bundle descriptions. */
protected Digester _digester;
}
@@ -0,0 +1,143 @@
//
// $Id: TileSetBundlerTask.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.tile.bundle.tools;
import java.io.File;
import java.util.ArrayList;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import com.threerings.media.tile.tools.MapFileTileSetIDBroker;
/**
* Ant task for creating tilset bundles.
*/
public class TileSetBundlerTask extends Task
{
/**
* Sets the path to the bundler configuration file that we'll use when
* creating the bundle.
*/
public void setConfig (File config)
{
_config = config;
}
/**
* Sets the path to the tileset id mapping file we'll use when
* creating the bundle.
*/
public void setMapfile (File mapfile)
{
_mapfile = mapfile;
}
/**
* Adds a nested &lt;fileset&gt; element.
*/
public void addFileset (FileSet set)
{
_filesets.add(set);
}
/**
* Performs the actual work of the task.
*/
public void execute () throws BuildException
{
// make sure everything was set up properly
ensureSet(_config, "Must specify the path to the bundler config " +
"file via the 'config' attribute.");
ensureSet(_mapfile, "Must specify the path to the tileset id map " +
"file via the 'mapfile' attribute.");
File cfile = null;
try {
// create a tileset bundler
TileSetBundler bundler = new TileSetBundler(_config);
// create our tileset id broker
MapFileTileSetIDBroker broker =
new MapFileTileSetIDBroker(_mapfile);
// deal with the filesets
for (int i = 0; i < _filesets.size(); i++) {
FileSet fs = (FileSet)_filesets.get(i);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
File fromDir = fs.getDir(getProject());
String[] srcFiles = ds.getIncludedFiles();
for (int f = 0; f < srcFiles.length; f++) {
cfile = new File(fromDir, srcFiles[f]);
// figure out the bundle file based on the definition
// file
String cpath = cfile.getPath();
if (!cpath.endsWith(".xml")) {
System.err.println("Can't infer bundle name from " +
"bundle config name " +
"[path=" + cpath + "].\n" +
"Config file should end with .xml.");
continue;
}
String bpath =
cpath.substring(0, cpath.length()-4) + ".jar";
File bfile = new File(bpath);
// create the bundle
if (bundler.createBundle(broker, cfile, bfile)) {
System.out.println(
"Created bundle from '" + cpath + "'...");
} else {
System.out.println(
"Tileset bundle up to date '" + bpath + "'.");
}
}
}
// commit changes to the tileset id mapping
broker.commit();
} catch (Exception e) {
String errmsg = "Failure creating tileset bundle [source=" + cfile +
"]: " + e.getMessage();
throw new BuildException(errmsg, e);
}
}
protected void ensureSet (Object value, String errmsg)
throws BuildException
{
if (value == null) {
throw new BuildException(errmsg);
}
}
protected File _config;
protected File _mapfile;
/** A list of filesets that contain tileset bundle definitions. */
protected ArrayList _filesets = new ArrayList();
}