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:
@@ -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>
|
||||
* <bundler-config>
|
||||
* <mapping>
|
||||
* <path>bundle.tilesets.uniform</path>
|
||||
* <ruleset>
|
||||
* com.threerings.media.tile.tools.xml.UniformTileSetRuleSet
|
||||
* </ruleset>
|
||||
* </mapping>
|
||||
* <mapping>
|
||||
* <path>bundle.tilesets.object</path>
|
||||
* <ruleset>
|
||||
* com.threerings.media.tile.tools.xml.ObjectTileSetRuleSet
|
||||
* </ruleset>
|
||||
* </mapping>
|
||||
* </bundler-config>
|
||||
* </pre>
|
||||
*
|
||||
* This configuration would be used to parse a bundle description that
|
||||
* looked something like the following:
|
||||
*
|
||||
* <pre>
|
||||
* <bundle>
|
||||
* <tilesets>
|
||||
* <uniform>
|
||||
* <tileset>
|
||||
* <!-- ... -->
|
||||
* </tileset>
|
||||
* </uniform>
|
||||
* <object>
|
||||
* <tileset>
|
||||
* <!-- ... -->
|
||||
* </tileset>
|
||||
* </object>
|
||||
* </tilesets>
|
||||
* </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 <fileset> 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();
|
||||
}
|
||||
Reference in New Issue
Block a user