diff --git a/src/com/sk89q/worldedit/data/AlphaChunkStore.java b/src/com/sk89q/worldedit/data/AlphaChunkStore.java index cd7a61703..21a7bb1ad 100644 --- a/src/com/sk89q/worldedit/data/AlphaChunkStore.java +++ b/src/com/sk89q/worldedit/data/AlphaChunkStore.java @@ -49,11 +49,16 @@ public class AlphaChunkStore extends NestedFileChunkStore { * @param f2 * @param name * @return + * @throws DataException * @throws IOException */ protected InputStream getInputStream(String f1, String f2, String name) - throws IOException { + throws DataException, IOException { String file = f1 + File.separator + f2 + File.separator + name; - return new FileInputStream(new File(path, file)); + try { + return new FileInputStream(new File(path, file)); + } catch (FileNotFoundException e) { + throw new MissingChunkException(); + } } } diff --git a/src/com/sk89q/worldedit/data/Chunk.java b/src/com/sk89q/worldedit/data/Chunk.java index 9fa32f35e..a4e8972d2 100644 --- a/src/com/sk89q/worldedit/data/Chunk.java +++ b/src/com/sk89q/worldedit/data/Chunk.java @@ -57,12 +57,14 @@ public class Chunk { rootZ = ((IntTag)getChildTag( rootTag.getValue(), "zPos", IntTag.class)).getValue(); - if (blocks.length != 16384) { - throw new InvalidFormatException("Chunk blocks byte array expected to contain 16,384 blocks"); + if (blocks.length != 32768) { + throw new InvalidFormatException("Chunk blocks byte array expected " + + "to be 32,768 bytes; found " + blocks.length); } if (data.length != 16384) { - throw new InvalidFormatException("Chunk block data byte array expected to contain 16,384 blocks"); + throw new InvalidFormatException("Chunk block data byte array " + + "expected to be 16,384 bytes; found " + data.length); } } @@ -74,8 +76,10 @@ public class Chunk { * @throws DataException */ public int getBlockID(Vector pos) throws DataException { - int index = pos.getBlockY() * 16 * 16 - + (pos.getBlockZ() - rootZ) * 16 + (pos.getBlockX() - rootX); + int x = pos.getBlockX() - rootX * 16; + int y = pos.getBlockY(); + int z = pos.getBlockZ() - rootZ * 16; + int index = y + (z * 128 + (x * 128 * 16)); try { return blocks[index]; @@ -92,11 +96,19 @@ public class Chunk { * @throws DataException */ public int getBlockData(Vector pos) throws DataException { - int index = pos.getBlockY() * 16 * 16 - + (pos.getBlockZ() - rootZ) * 16 + (pos.getBlockX() - rootX); + int x = pos.getBlockX() - rootX * 16; + int y = pos.getBlockY(); + int z = pos.getBlockZ() - rootZ * 16; + int index = y + (z * 128 + (x * 128 * 16)); + boolean shift = index % 2 == 0; + index /= 2; try { - return data[index]; + if (!shift) { + return (data[index] & 0xF0) >> 4; + } else { + return data[index] & 0xF; + } } catch (IndexOutOfBoundsException e) { throw new DataException("Chunk does not contain position " + pos); } diff --git a/src/com/sk89q/worldedit/data/ChunkStore.java b/src/com/sk89q/worldedit/data/ChunkStore.java index cb51c92e8..b4f13a398 100644 --- a/src/com/sk89q/worldedit/data/ChunkStore.java +++ b/src/com/sk89q/worldedit/data/ChunkStore.java @@ -49,11 +49,11 @@ public abstract class ChunkStore { * * @param pos * @return tag - * @throws ChunkStoreException + * @throws DataException * @throws IOException */ public abstract CompoundTag getChunkTag(Vector2D pos) - throws ChunkStoreException, IOException; + throws DataException, IOException; /** * Get a chunk at a location. diff --git a/src/com/sk89q/worldedit/data/ChunkStoreException.java b/src/com/sk89q/worldedit/data/ChunkStoreException.java index b3d490645..a251fe7be 100644 --- a/src/com/sk89q/worldedit/data/ChunkStoreException.java +++ b/src/com/sk89q/worldedit/data/ChunkStoreException.java @@ -27,4 +27,8 @@ public class ChunkStoreException extends DataException { public ChunkStoreException(String msg) { super(msg); } + + public ChunkStoreException() { + super(); + } } diff --git a/src/com/sk89q/worldedit/data/DataException.java b/src/com/sk89q/worldedit/data/DataException.java index cd71d60c8..fd7c70a52 100644 --- a/src/com/sk89q/worldedit/data/DataException.java +++ b/src/com/sk89q/worldedit/data/DataException.java @@ -28,4 +28,8 @@ public class DataException extends Exception { public DataException(String msg) { super(msg); } + + public DataException() { + super(); + } } diff --git a/src/com/sk89q/worldedit/data/MissingChunkException.java b/src/com/sk89q/worldedit/data/MissingChunkException.java new file mode 100644 index 000000000..99bb5092f --- /dev/null +++ b/src/com/sk89q/worldedit/data/MissingChunkException.java @@ -0,0 +1,48 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.data; + +import com.sk89q.worldedit.Vector2D; + +/** + * + * @author sk89q + */ +public class MissingChunkException extends ChunkStoreException { + private Vector2D pos; + + public MissingChunkException() { + super(); + } + + public MissingChunkException(Vector2D pos) { + super(); + this.pos = pos; + } + + /** + * Get chunk position in question. May be null if unknown. + * + * @return + */ + public Vector2D getChunkPosition() { + return pos; + } +} diff --git a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java b/src/com/sk89q/worldedit/data/NestedFileChunkStore.java index e8b1db4ac..8913d9eb2 100644 --- a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java +++ b/src/com/sk89q/worldedit/data/NestedFileChunkStore.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.data; import com.sk89q.worldedit.Vector2D; import java.io.*; +import java.util.*; import org.jnbt.*; /** @@ -36,9 +37,11 @@ public abstract class NestedFileChunkStore extends ChunkStore { * * @param pos * @return tag + * @throws DataException + * @throws IOException */ public CompoundTag getChunkTag(Vector2D pos) - throws ChunkStoreException, IOException { + throws DataException, IOException { int x = pos.getBlockX(); int z = pos.getBlockZ(); @@ -51,15 +54,37 @@ public abstract class NestedFileChunkStore extends ChunkStore { NBTInputStream nbt = new NBTInputStream(stream); Tag tag; - tag = nbt.readTag(); - if (!(tag instanceof CompoundTag)) { - throw new ChunkStoreException("CompoundTag expected for chunk; got " - + tag.getClass()); + try { + tag = nbt.readTag(); + if (!(tag instanceof CompoundTag)) { + throw new ChunkStoreException("CompoundTag expected for chunk; got " + + tag.getClass().getName()); + } + + Map children = (Map)((CompoundTag)tag).getValue(); + CompoundTag rootTag = null; + + // Find Level tag + for (Map.Entry entry : children.entrySet()) { + if (entry.getKey().equals("Level")) { + if (entry.getValue() instanceof CompoundTag) { + rootTag = (CompoundTag)entry.getValue(); + break; + } else { + throw new ChunkStoreException("CompoundTag expected for 'Level'; got " + + entry.getValue().getClass().getName()); + } + } + } + + if (rootTag == null) { + throw new ChunkStoreException("Missing root 'Level' tag"); + } + + return rootTag; + } finally { + stream.close(); } - - stream.close(); - - return (CompoundTag)tag; } /** @@ -83,5 +108,5 @@ public abstract class NestedFileChunkStore extends ChunkStore { * @throws IOException */ protected abstract InputStream getInputStream(String f1, String f2, String name) - throws IOException; + throws IOException, DataException; } diff --git a/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java b/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java index 5054fa585..e4a9dcd3d 100644 --- a/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java +++ b/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.data; import java.io.*; import java.util.zip.*; +import java.util.Enumeration; /** * Represents the chunk store used by Minecraft alpha but zipped. @@ -43,7 +44,8 @@ public class ZippedAlphaChunkStore extends NestedFileChunkStore { /** * Create an instance. The folder argument lets you choose a folder or - * path to look into in the ZIP for the files. + * path to look into in the ZIP for the files. Use a blank string for + * the folder to not look into a subdirectory. * * @param zipFile * @param folder @@ -59,7 +61,8 @@ public class ZippedAlphaChunkStore extends NestedFileChunkStore { } /** - * Create an instance. + * Create an instance. The subfolder containing the chunk data will + * be detected. * * @param zipFile * @param folder @@ -81,13 +84,46 @@ public class ZippedAlphaChunkStore extends NestedFileChunkStore { * @param name * @return * @throws IOException + * @throws DataException */ protected InputStream getInputStream(String f1, String f2, String name) - throws IOException { + throws IOException, DataException { String file = f1 + "/" + f2 + "/" + name; + + // Detect subfolder for the world's files + if (folder != null) { + if (!folder.equals("")) { + file = folder + "/" + file; + } + } else { + ZipEntry testEntry = zip.getEntry("level.dat"); + + // So, the data is not in the root directory + if (testEntry == null) { + // Let's try a world/ sub-directory + testEntry = zip.getEntry("world/level.dat"); + + // So not there either... + if (testEntry == null) { + for (Enumeration e = zip.entries(); e.hasMoreElements(); ) { + testEntry = (ZipEntry)e.nextElement(); + + // Whoo, found level.dat! + if (testEntry.getName().matches(".+/level\\.dat")) { + file = testEntry.getName().replaceAll("level\\.dat$", "") + + file; + break; + } + } + } else { + file = "world/" + file; + } + } + } + ZipEntry entry = zip.getEntry(file); if (entry == null) { - throw new IOException("ZIP doesn't contain chunk"); + throw new MissingChunkException(); } try { return zip.getInputStream(entry);