From 5e7d9c7f7d8e6ee2ab5b9954a331627389a5b81f Mon Sep 17 00:00:00 2001 From: sk89q Date: Wed, 9 Mar 2011 23:10:59 -0800 Subject: [PATCH] Added McRegion reader classes (incomplete), moved some com.sk89q.worldedit.data classes around and moved org.jnbt com.sk89q.jnbt to coincide with some modifications to make the JNBT library a bit more generic by accepting uncompressed input streams. --- src/{org => com/sk89q}/jnbt/ByteArrayTag.java | 4 +- src/{org => com/sk89q}/jnbt/ByteTag.java | 4 +- src/{org => com/sk89q}/jnbt/CompoundTag.java | 3 +- src/{org => com/sk89q}/jnbt/DoubleTag.java | 4 +- src/{org => com/sk89q}/jnbt/EndTag.java | 4 +- src/{org => com/sk89q}/jnbt/FloatTag.java | 4 +- src/{org => com/sk89q}/jnbt/IntTag.java | 4 +- src/{org => com/sk89q}/jnbt/ListTag.java | 4 +- src/{org => com/sk89q}/jnbt/LongTag.java | 4 +- src/{org => com/sk89q}/jnbt/NBTConstants.java | 2 +- .../sk89q}/jnbt/NBTInputStream.java | 19 +- .../sk89q}/jnbt/NBTOutputStream.java | 16 +- src/{org => com/sk89q}/jnbt/NBTUtils.java | 16 +- src/{org => com/sk89q}/jnbt/ShortTag.java | 4 +- src/{org => com/sk89q}/jnbt/StringTag.java | 4 +- src/{org => com/sk89q}/jnbt/Tag.java | 2 +- src/com/sk89q/worldedit/CuboidClipboard.java | 6 +- src/com/sk89q/worldedit/EditSession.java | 3 +- .../sk89q/worldedit/blocks/ChestBlock.java | 2 +- .../worldedit/blocks/DispenserBlock.java | 2 +- .../sk89q/worldedit/blocks/FurnaceBlock.java | 2 +- .../worldedit/blocks/MobSpawnerBlock.java | 2 +- src/com/sk89q/worldedit/blocks/NoteBlock.java | 2 +- src/com/sk89q/worldedit/blocks/SignBlock.java | 2 +- .../worldedit/blocks/TileEntityBlock.java | 2 +- .../worldedit/commands/ChunkCommands.java | 8 +- src/com/sk89q/worldedit/data/Chunk.java | 2 +- src/com/sk89q/worldedit/data/ChunkStore.java | 2 +- ...nkStore.java => FileLegacyChunkStore.java} | 4 +- .../data/FileMcRegionChunkStore.java | 57 +++ .../data/ForwardSeekableInputStream.java | 100 ++++++ ...eChunkStore.java => LegacyChunkStore.java} | 10 +- .../worldedit/data/McRegionChunkStore.java | 123 +++++++ .../sk89q/worldedit/data/McRegionReader.java | 197 +++++++++++ .../data/TrueZipAlphaChunkStore.java | 157 -------- ...tore.java => TrueZipLegacyChunkStore.java} | 334 +++++++++--------- .../worldedit/data/ZippedAlphaChunkStore.java | 2 +- .../sk89q/worldedit/snapshots/Snapshot.java | 9 +- 38 files changed, 759 insertions(+), 367 deletions(-) rename src/{org => com/sk89q}/jnbt/ByteArrayTag.java (94%) rename src/{org => com/sk89q}/jnbt/ByteTag.java (94%) rename src/{org => com/sk89q}/jnbt/CompoundTag.java (95%) rename src/{org => com/sk89q}/jnbt/DoubleTag.java (94%) rename src/{org => com/sk89q}/jnbt/EndTag.java (94%) rename src/{org => com/sk89q}/jnbt/FloatTag.java (94%) rename src/{org => com/sk89q}/jnbt/IntTag.java (94%) rename src/{org => com/sk89q}/jnbt/ListTag.java (94%) rename src/{org => com/sk89q}/jnbt/LongTag.java (94%) rename src/{org => com/sk89q}/jnbt/NBTConstants.java (95%) rename src/{org => com/sk89q}/jnbt/NBTInputStream.java (88%) rename src/{org => com/sk89q}/jnbt/NBTOutputStream.java (90%) rename src/{org => com/sk89q}/jnbt/NBTUtils.java (89%) rename src/{org => com/sk89q}/jnbt/ShortTag.java (94%) rename src/{org => com/sk89q}/jnbt/StringTag.java (94%) rename src/{org => com/sk89q}/jnbt/Tag.java (95%) rename src/com/sk89q/worldedit/data/{AlphaChunkStore.java => FileLegacyChunkStore.java} (94%) create mode 100644 src/com/sk89q/worldedit/data/FileMcRegionChunkStore.java create mode 100644 src/com/sk89q/worldedit/data/ForwardSeekableInputStream.java rename src/com/sk89q/worldedit/data/{NestedFileChunkStore.java => LegacyChunkStore.java} (93%) create mode 100644 src/com/sk89q/worldedit/data/McRegionChunkStore.java create mode 100644 src/com/sk89q/worldedit/data/McRegionReader.java delete mode 100644 src/com/sk89q/worldedit/data/TrueZipAlphaChunkStore.java rename src/com/sk89q/worldedit/data/{TrueZipLegacyAlphaChunkStore.java => TrueZipLegacyChunkStore.java} (92%) diff --git a/src/org/jnbt/ByteArrayTag.java b/src/com/sk89q/jnbt/ByteArrayTag.java similarity index 94% rename from src/org/jnbt/ByteArrayTag.java rename to src/com/sk89q/jnbt/ByteArrayTag.java index 3e84b294b..739203acc 100644 --- a/src/org/jnbt/ByteArrayTag.java +++ b/src/com/sk89q/jnbt/ByteArrayTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/ByteTag.java b/src/com/sk89q/jnbt/ByteTag.java similarity index 94% rename from src/org/jnbt/ByteTag.java rename to src/com/sk89q/jnbt/ByteTag.java index b255cee70..358ec889b 100644 --- a/src/org/jnbt/ByteTag.java +++ b/src/com/sk89q/jnbt/ByteTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/CompoundTag.java b/src/com/sk89q/jnbt/CompoundTag.java similarity index 95% rename from src/org/jnbt/CompoundTag.java rename to src/com/sk89q/jnbt/CompoundTag.java index ce9c07927..a7f6d833a 100644 --- a/src/org/jnbt/CompoundTag.java +++ b/src/com/sk89q/jnbt/CompoundTag.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; /* * JNBT License @@ -35,6 +35,7 @@ package org.jnbt; import java.util.Collections; import java.util.Map; +import com.sk89q.jnbt.Tag; /** * The TAG_Compound tag. diff --git a/src/org/jnbt/DoubleTag.java b/src/com/sk89q/jnbt/DoubleTag.java similarity index 94% rename from src/org/jnbt/DoubleTag.java rename to src/com/sk89q/jnbt/DoubleTag.java index 668e0fd02..ed74c14bc 100644 --- a/src/org/jnbt/DoubleTag.java +++ b/src/com/sk89q/jnbt/DoubleTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/EndTag.java b/src/com/sk89q/jnbt/EndTag.java similarity index 94% rename from src/org/jnbt/EndTag.java rename to src/com/sk89q/jnbt/EndTag.java index 1f6b7f313..176c86ffc 100644 --- a/src/org/jnbt/EndTag.java +++ b/src/com/sk89q/jnbt/EndTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/FloatTag.java b/src/com/sk89q/jnbt/FloatTag.java similarity index 94% rename from src/org/jnbt/FloatTag.java rename to src/com/sk89q/jnbt/FloatTag.java index 3921a6478..0d9055356 100644 --- a/src/org/jnbt/FloatTag.java +++ b/src/com/sk89q/jnbt/FloatTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/IntTag.java b/src/com/sk89q/jnbt/IntTag.java similarity index 94% rename from src/org/jnbt/IntTag.java rename to src/com/sk89q/jnbt/IntTag.java index ad2e43361..b5187b197 100644 --- a/src/org/jnbt/IntTag.java +++ b/src/com/sk89q/jnbt/IntTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/ListTag.java b/src/com/sk89q/jnbt/ListTag.java similarity index 94% rename from src/org/jnbt/ListTag.java rename to src/com/sk89q/jnbt/ListTag.java index 4d267033f..17e8096eb 100644 --- a/src/org/jnbt/ListTag.java +++ b/src/com/sk89q/jnbt/ListTag.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; /* * JNBT License @@ -35,6 +35,8 @@ package org.jnbt; import java.util.Collections; import java.util.List; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.Tag; /** * The TAG_List tag. diff --git a/src/org/jnbt/LongTag.java b/src/com/sk89q/jnbt/LongTag.java similarity index 94% rename from src/org/jnbt/LongTag.java rename to src/com/sk89q/jnbt/LongTag.java index c337de889..66390f350 100644 --- a/src/org/jnbt/LongTag.java +++ b/src/com/sk89q/jnbt/LongTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/NBTConstants.java b/src/com/sk89q/jnbt/NBTConstants.java similarity index 95% rename from src/org/jnbt/NBTConstants.java rename to src/com/sk89q/jnbt/NBTConstants.java index 5dced4fcb..7533d8574 100644 --- a/src/org/jnbt/NBTConstants.java +++ b/src/com/sk89q/jnbt/NBTConstants.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; import java.nio.charset.Charset; diff --git a/src/org/jnbt/NBTInputStream.java b/src/com/sk89q/jnbt/NBTInputStream.java similarity index 88% rename from src/org/jnbt/NBTInputStream.java rename to src/com/sk89q/jnbt/NBTInputStream.java index 4ba8c3c0a..69bd5abb6 100644 --- a/src/org/jnbt/NBTInputStream.java +++ b/src/com/sk89q/jnbt/NBTInputStream.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; /* * JNBT License @@ -41,7 +41,20 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.zip.GZIPInputStream; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; /** *

This class reads NBT, or @@ -68,7 +81,7 @@ public final class NBTInputStream implements Closeable { * @throws IOException if an I/O error occurs. */ public NBTInputStream(InputStream is) throws IOException { - this.is = new DataInputStream(new GZIPInputStream(is)); + this.is = new DataInputStream(is); } /** diff --git a/src/org/jnbt/NBTOutputStream.java b/src/com/sk89q/jnbt/NBTOutputStream.java similarity index 90% rename from src/org/jnbt/NBTOutputStream.java rename to src/com/sk89q/jnbt/NBTOutputStream.java index a9a3c8956..41bea562e 100644 --- a/src/org/jnbt/NBTOutputStream.java +++ b/src/com/sk89q/jnbt/NBTOutputStream.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; import java.io.Closeable; import java.io.DataOutputStream; @@ -6,6 +6,20 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.zip.GZIPOutputStream; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/NBTUtils.java b/src/com/sk89q/jnbt/NBTUtils.java similarity index 89% rename from src/org/jnbt/NBTUtils.java rename to src/com/sk89q/jnbt/NBTUtils.java index 823b04ae6..1410453a4 100644 --- a/src/org/jnbt/NBTUtils.java +++ b/src/com/sk89q/jnbt/NBTUtils.java @@ -1,4 +1,18 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/ShortTag.java b/src/com/sk89q/jnbt/ShortTag.java similarity index 94% rename from src/org/jnbt/ShortTag.java rename to src/com/sk89q/jnbt/ShortTag.java index 11e2f3afd..559fd093f 100644 --- a/src/org/jnbt/ShortTag.java +++ b/src/com/sk89q/jnbt/ShortTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/StringTag.java b/src/com/sk89q/jnbt/StringTag.java similarity index 94% rename from src/org/jnbt/StringTag.java rename to src/com/sk89q/jnbt/StringTag.java index 3bcb09265..57ee7d85d 100644 --- a/src/org/jnbt/StringTag.java +++ b/src/com/sk89q/jnbt/StringTag.java @@ -1,4 +1,6 @@ -package org.jnbt; +package com.sk89q.jnbt; + +import com.sk89q.jnbt.Tag; /* * JNBT License diff --git a/src/org/jnbt/Tag.java b/src/com/sk89q/jnbt/Tag.java similarity index 95% rename from src/org/jnbt/Tag.java rename to src/com/sk89q/jnbt/Tag.java index 384784697..79fa0ed20 100644 --- a/src/org/jnbt/Tag.java +++ b/src/com/sk89q/jnbt/Tag.java @@ -1,4 +1,4 @@ -package org.jnbt; +package com.sk89q.jnbt; /* * JNBT License diff --git a/src/com/sk89q/worldedit/CuboidClipboard.java b/src/com/sk89q/worldedit/CuboidClipboard.java index 28f2f4cca..476436a7c 100644 --- a/src/com/sk89q/worldedit/CuboidClipboard.java +++ b/src/com/sk89q/worldedit/CuboidClipboard.java @@ -19,14 +19,15 @@ package com.sk89q.worldedit; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.blocks.*; import com.sk89q.worldedit.data.*; -import org.jnbt.*; import java.io.*; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; import java.util.List; +import java.util.zip.GZIPInputStream; /** * The clipboard remembers the state of a cuboid region. @@ -381,7 +382,8 @@ public class CuboidClipboard { public static CuboidClipboard loadSchematic(File path) throws DataException, IOException { FileInputStream stream = new FileInputStream(path); - NBTInputStream nbtStream = new NBTInputStream(stream); + NBTInputStream nbtStream = new NBTInputStream( + new GZIPInputStream(stream)); Vector origin = new Vector(); Vector offset = new Vector(); diff --git a/src/com/sk89q/worldedit/EditSession.java b/src/com/sk89q/worldedit/EditSession.java index 95a1bf4aa..c47e3fc53 100755 --- a/src/com/sk89q/worldedit/EditSession.java +++ b/src/com/sk89q/worldedit/EditSession.java @@ -2109,7 +2109,7 @@ public class EditSession { /** * Returns the highest solid 'terrain' block which can occur naturally. - * Looks at: 1, 2, 3, 7, 12, 13, 14, 15, 16, 56, 73, 74, 87, 88, 89 + * Looks at: 1, 2, 3, 7, 12, 13, 14, 15, 16, 56, 73, 74, 87, 88, 89, 82 * * @param x * @param z @@ -2131,6 +2131,7 @@ public class EditSession { || id == 7 // bedrock || id == 12 // sand || id == 13 // gravel + || id == 82 // clay // hell || id == 87 // netherstone || id == 88 // slowsand diff --git a/src/com/sk89q/worldedit/blocks/ChestBlock.java b/src/com/sk89q/worldedit/blocks/ChestBlock.java index 678bc618d..54e109ec6 100644 --- a/src/com/sk89q/worldedit/blocks/ChestBlock.java +++ b/src/com/sk89q/worldedit/blocks/ChestBlock.java @@ -19,12 +19,12 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; -import org.jnbt.*; /** * Represents chests. diff --git a/src/com/sk89q/worldedit/blocks/DispenserBlock.java b/src/com/sk89q/worldedit/blocks/DispenserBlock.java index f887c1edc..4504219b3 100644 --- a/src/com/sk89q/worldedit/blocks/DispenserBlock.java +++ b/src/com/sk89q/worldedit/blocks/DispenserBlock.java @@ -19,12 +19,12 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; -import org.jnbt.*; /** * Represents dispensers. diff --git a/src/com/sk89q/worldedit/blocks/FurnaceBlock.java b/src/com/sk89q/worldedit/blocks/FurnaceBlock.java index d72066407..c67180528 100644 --- a/src/com/sk89q/worldedit/blocks/FurnaceBlock.java +++ b/src/com/sk89q/worldedit/blocks/FurnaceBlock.java @@ -19,12 +19,12 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; -import org.jnbt.*; /** * Represents furnaces. diff --git a/src/com/sk89q/worldedit/blocks/MobSpawnerBlock.java b/src/com/sk89q/worldedit/blocks/MobSpawnerBlock.java index 64d2e673f..5e2e2fab1 100644 --- a/src/com/sk89q/worldedit/blocks/MobSpawnerBlock.java +++ b/src/com/sk89q/worldedit/blocks/MobSpawnerBlock.java @@ -19,10 +19,10 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; -import org.jnbt.*; /** * Represents chests. diff --git a/src/com/sk89q/worldedit/blocks/NoteBlock.java b/src/com/sk89q/worldedit/blocks/NoteBlock.java index 5a186127f..e25a86f36 100644 --- a/src/com/sk89q/worldedit/blocks/NoteBlock.java +++ b/src/com/sk89q/worldedit/blocks/NoteBlock.java @@ -19,10 +19,10 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; -import org.jnbt.*; /** * diff --git a/src/com/sk89q/worldedit/blocks/SignBlock.java b/src/com/sk89q/worldedit/blocks/SignBlock.java index d36146a13..4ae5a563e 100644 --- a/src/com/sk89q/worldedit/blocks/SignBlock.java +++ b/src/com/sk89q/worldedit/blocks/SignBlock.java @@ -19,10 +19,10 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.data.*; import java.util.Map; import java.util.HashMap; -import org.jnbt.*; /** * diff --git a/src/com/sk89q/worldedit/blocks/TileEntityBlock.java b/src/com/sk89q/worldedit/blocks/TileEntityBlock.java index 4cf06ee84..f36b57fc2 100644 --- a/src/com/sk89q/worldedit/blocks/TileEntityBlock.java +++ b/src/com/sk89q/worldedit/blocks/TileEntityBlock.java @@ -19,9 +19,9 @@ package com.sk89q.worldedit.blocks; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.data.*; import java.util.Map; -import org.jnbt.Tag; /** * A class implementing this interface has extra TileEntityBlock data to store. diff --git a/src/com/sk89q/worldedit/commands/ChunkCommands.java b/src/com/sk89q/worldedit/commands/ChunkCommands.java index 8354850a1..3dac26653 100644 --- a/src/com/sk89q/worldedit/commands/ChunkCommands.java +++ b/src/com/sk89q/worldedit/commands/ChunkCommands.java @@ -25,7 +25,7 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.data.NestedFileChunkStore; +import com.sk89q.worldedit.data.LegacyChunkStore; /** * Chunk tools. @@ -73,7 +73,7 @@ public class ChunkCommands { Set chunks = session.getSelection(player.getWorld()).getChunks(); for (Vector2D chunk : chunks) { - player.print(NestedFileChunkStore.getFilename(chunk)); + player.print(LegacyChunkStore.getFilename(chunk)); } } @@ -109,7 +109,7 @@ public class ChunkCommands { writer.write("PAUSE\r\n"); for (Vector2D chunk : chunks) { - String filename = NestedFileChunkStore.getFilename(chunk); + String filename = LegacyChunkStore.getFilename(chunk); writer.write("ECHO " + filename + "\r\n"); writer.write("DEL \"world/" + filename + "\"\r\n"); } @@ -138,7 +138,7 @@ public class ChunkCommands { writer.write("read -p \"Press any key to continue...\"\n"); for (Vector2D chunk : chunks) { - String filename = NestedFileChunkStore.getFilename(chunk); + String filename = LegacyChunkStore.getFilename(chunk); writer.write("echo " + filename + "\n"); writer.write("rm \"world/" + filename + "\"\n"); } diff --git a/src/com/sk89q/worldedit/data/Chunk.java b/src/com/sk89q/worldedit/data/Chunk.java index ae05e4f92..3c05cd4f7 100644 --- a/src/com/sk89q/worldedit/data/Chunk.java +++ b/src/com/sk89q/worldedit/data/Chunk.java @@ -23,9 +23,9 @@ package com.sk89q.worldedit.data; import java.util.List; import java.util.Map; import java.util.HashMap; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.*; -import org.jnbt.*; /** * Represents a chunk. diff --git a/src/com/sk89q/worldedit/data/ChunkStore.java b/src/com/sk89q/worldedit/data/ChunkStore.java index 99cca9949..ef0432b3b 100644 --- a/src/com/sk89q/worldedit/data/ChunkStore.java +++ b/src/com/sk89q/worldedit/data/ChunkStore.java @@ -20,8 +20,8 @@ package com.sk89q.worldedit.data; import java.io.*; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.*; -import org.jnbt.*; /** * Represents chunk storage mechanisms. diff --git a/src/com/sk89q/worldedit/data/AlphaChunkStore.java b/src/com/sk89q/worldedit/data/FileLegacyChunkStore.java similarity index 94% rename from src/com/sk89q/worldedit/data/AlphaChunkStore.java rename to src/com/sk89q/worldedit/data/FileLegacyChunkStore.java index 38da78f99..10c03c8f4 100644 --- a/src/com/sk89q/worldedit/data/AlphaChunkStore.java +++ b/src/com/sk89q/worldedit/data/FileLegacyChunkStore.java @@ -26,7 +26,7 @@ import java.io.*; * * @author sk89q */ -public class AlphaChunkStore extends NestedFileChunkStore { +public class FileLegacyChunkStore extends LegacyChunkStore { /** * Folder to read from. */ @@ -38,7 +38,7 @@ public class AlphaChunkStore extends NestedFileChunkStore { * * @param path */ - public AlphaChunkStore(File path) { + public FileLegacyChunkStore(File path) { this.path = path; } diff --git a/src/com/sk89q/worldedit/data/FileMcRegionChunkStore.java b/src/com/sk89q/worldedit/data/FileMcRegionChunkStore.java new file mode 100644 index 000000000..f9dabee69 --- /dev/null +++ b/src/com/sk89q/worldedit/data/FileMcRegionChunkStore.java @@ -0,0 +1,57 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 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 java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class FileMcRegionChunkStore extends McRegionChunkStore { + /** + * Folder to read from. + */ + private File path; + + /** + * Create an instance. The passed path is the folder to read the + * chunk files from. + * + * @param path + */ + public FileMcRegionChunkStore(File path) { + this.path = path; + } + + @Override + protected InputStream getInputStream(String name) throws IOException, + DataException { + + String file = "region" + File.separator + name; + + try { + return new FileInputStream(new File(path, file)); + } catch (FileNotFoundException e) { + throw new MissingChunkException(); + } + } + +} diff --git a/src/com/sk89q/worldedit/data/ForwardSeekableInputStream.java b/src/com/sk89q/worldedit/data/ForwardSeekableInputStream.java new file mode 100644 index 000000000..1ed26595b --- /dev/null +++ b/src/com/sk89q/worldedit/data/ForwardSeekableInputStream.java @@ -0,0 +1,100 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 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 java.io.IOException; +import java.io.InputStream; + +public class ForwardSeekableInputStream extends InputStream { + + protected InputStream parent; + protected long position = 0; + + public ForwardSeekableInputStream(InputStream parent) { + this.parent = parent; + } + + @Override + public int read() throws IOException { + int ret = parent.read(); + position++; + return ret; + } + + @Override + public int available() throws IOException { + return parent.available(); + } + + @Override + public void close() throws IOException { + parent.close(); + } + + @Override + public synchronized void mark(int readlimit) { + parent.mark(readlimit); + } + + @Override + public boolean markSupported() { + return parent.markSupported(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + throw new IOException("Offset not supported for read()"); + } + + @Override + public int read(byte[] b) throws IOException { + int read = parent.read(b); + position += read; + return read; + } + + @Override + public synchronized void reset() throws IOException { + parent.reset(); + } + + @Override + public long skip(long n) throws IOException { + long skipped = parent.skip(n); + position += skipped; + return skipped; + } + + public void seek(long n) throws IOException { + long diff = n - position; + + if (diff < 0) { + throw new IOException("Can't seek backwards"); + } + + if (diff == 0) { + return; + } + + if (skip(diff) < diff) { + throw new IOException("Failed to seek " + diff + " bytes"); + } + } +} diff --git a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java b/src/com/sk89q/worldedit/data/LegacyChunkStore.java similarity index 93% rename from src/com/sk89q/worldedit/data/NestedFileChunkStore.java rename to src/com/sk89q/worldedit/data/LegacyChunkStore.java index 9cd08fde2..7ae24c515 100644 --- a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java +++ b/src/com/sk89q/worldedit/data/LegacyChunkStore.java @@ -19,10 +19,13 @@ package com.sk89q.worldedit.data; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.*; import java.io.*; import java.util.Map; -import org.jnbt.*; +import java.util.zip.GZIPInputStream; /** * Represents chunk stores that use Alpha's file format for storing chunks. @@ -31,7 +34,7 @@ import org.jnbt.*; * * @author sk89q */ -public abstract class NestedFileChunkStore extends ChunkStore { +public abstract class LegacyChunkStore extends ChunkStore { /** * Get the filename of a chunk. * @@ -82,7 +85,8 @@ public abstract class NestedFileChunkStore extends ChunkStore { + "." + Integer.toString(z, 36) + ".dat"; InputStream stream = getInputStream(folder1, folder2, filename); - NBTInputStream nbt = new NBTInputStream(stream); + NBTInputStream nbt = new NBTInputStream( + new GZIPInputStream(stream)); Tag tag; try { diff --git a/src/com/sk89q/worldedit/data/McRegionChunkStore.java b/src/com/sk89q/worldedit/data/McRegionChunkStore.java new file mode 100644 index 000000000..69b0d5b8a --- /dev/null +++ b/src/com/sk89q/worldedit/data/McRegionChunkStore.java @@ -0,0 +1,123 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 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 java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector2D; + +public abstract class McRegionChunkStore extends ChunkStore { + protected String curFilename = null; + protected McRegionReader cachedReader = null; + + /** + * Get the filename of a region file. + * + * @param pos + * @return + */ + public static String getFilename(Vector2D pos) { + int x = pos.getBlockX(); + int z = pos.getBlockZ(); + + String filename = "r." + (x >> 5) + "." + (z >> 5) + ".mcr"; + + return filename; + } + + protected McRegionReader getReader(Vector2D pos) throws DataException, IOException { + String filename = getFilename(pos); + if (curFilename.equals(filename)) { + return cachedReader; + } + InputStream stream = getInputStream(filename); + cachedReader = new McRegionReader(stream); + return cachedReader; + } + + @Override + public CompoundTag getChunkTag(Vector2D pos) throws DataException, + IOException { + + McRegionReader reader = getReader(pos); + InputStream stream = reader.getChunkInputStream(pos); + NBTInputStream nbt = new NBTInputStream(stream); + Tag tag; + + 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(); + } + } + + /** + * Get the input stream for a chunk file. + * + * @param name + * @return + * @throws IOException + */ + protected abstract InputStream getInputStream(String name) + throws IOException, DataException; + + + /** + * Close resources. + * + * @throws IOException + */ + @Override + public void close() throws IOException { + if (cachedReader != null) { + cachedReader.close(); + } + } +} diff --git a/src/com/sk89q/worldedit/data/McRegionReader.java b/src/com/sk89q/worldedit/data/McRegionReader.java new file mode 100644 index 000000000..d77e484e2 --- /dev/null +++ b/src/com/sk89q/worldedit/data/McRegionReader.java @@ -0,0 +1,197 @@ +// $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 . +*/ + +/* + + Region File Format + + Concept: The minimum unit of storage on hard drives is 4KB. 90% of Minecraft + chunks are smaller than 4KB. 99% are smaller than 8KB. Write a simple + container to store chunks in single files in runs of 4KB sectors. + + Each region file represents a 32x32 group of chunks. The conversion from + chunk number to region number is floor(coord / 32): a chunk at (30, -3) + would be in region (0, -1), and one at (70, -30) would be at (3, -1). + Region files are named "r.x.z.data", where x and z are the region coordinates. + + A region file begins with a 4KB header that describes where chunks are stored + in the file. A 4-byte big-endian integer represents sector offsets and sector + counts. The chunk offset for a chunk (x, z) begins at byte 4*(x+z*32) in the + file. The bottom byte of the chunk offset indicates the number of sectors the + chunk takes up, and the top 3 bytes represent the sector number of the chunk. + Given a chunk offset o, the chunk data begins at byte 4096*(o/256) and takes up + at most 4096*(o%256) bytes. A chunk cannot exceed 1MB in size. If a chunk + offset is 0, the corresponding chunk is not stored in the region file. + + Chunk data begins with a 4-byte big-endian integer representing the chunk data + length in bytes, not counting the length field. The length must be smaller than + 4096 times the number of sectors. The next byte is a version field, to allow + backwards-compatible updates to how chunks are encoded. + + A version of 1 represents a gzipped NBT file. The gzipped data is the chunk + length - 1. + + A version of 2 represents a deflated (zlib compressed) NBT file. The deflated + data is the chunk length - 1. + + */ + +package com.sk89q.worldedit.data; + +import java.io.*; +import java.util.zip.*; +import com.sk89q.worldedit.Vector2D; + +/** + * Reader for a MCRegion file. This reader works on input streams, meaning + * that it can be used to read files from non-file based sources. + * + * @author sk89q + */ +public class McRegionReader { + + protected static final int VERSION_GZIP = 1; + protected static final int VERSION_DEFLATE = 2; + protected static final int SECTOR_BYTES = 4096; + protected static final int SECTOR_INTS = SECTOR_BYTES / 4; + public static final int CHUNK_HEADER_SIZE = 5; + + protected ForwardSeekableInputStream stream; + protected DataInputStream dataStream; + + protected int offsets[]; + + /** + * Construct the reader. + * + * @param stream + * @throws DataException + * @throws IOException + */ + public McRegionReader(InputStream stream) throws DataException, IOException { + this.stream = new ForwardSeekableInputStream(stream); + this.dataStream = new DataInputStream(this.stream); + + readHeader(); + } + + /** + * Read the header. + * + * @throws DataException + * @throws IOException + */ + private void readHeader() throws DataException, IOException { + offsets = new int[SECTOR_INTS]; + + for (int i = 0; i < SECTOR_INTS; ++i) { + int offset = dataStream.readInt(); + offsets[i] = offset; + } + } + + /** + * Gets the uncompressed data input stream for a chunk. + * + * @param pos + * @return + * @throws IOException + * @throws DataException + */ + public synchronized InputStream getChunkInputStream(Vector2D pos) + throws IOException, DataException { + + int x = pos.getBlockX(); + int z = pos.getBlockZ(); + + if (x < 0 || x >= 32 || z < 0 || z >= 32) { + throw new DataException("MCRegion file does not contain " + x + "," + z); + } + + int offset = getOffset(x, z); + + // The chunk hasn't been generated + if (offset == 0) { + return null; + } + + int sectorNumber = offset >> 8; + int numSectors = offset & 0xFF; + + stream.seek(sectorNumber * SECTOR_BYTES); + int length = dataStream.readInt(); + + if (length > SECTOR_BYTES * numSectors) { + throw new DataException("MCRegion chunk at " + + x + "," + z + " has an invalid length of " + length); + } + + byte version = dataStream.readByte(); + + if (version == VERSION_GZIP) { + byte[] data = new byte[length - 1]; + if (dataStream.read(data) < length - 1) { + throw new DataException("MCRegion file does not contain " + + x + "," + z + " in full"); + } + return new GZIPInputStream(new ByteArrayInputStream(data)); + } else if (version == VERSION_DEFLATE) { + byte[] data = new byte[length - 1]; + if (dataStream.read(data) < length - 1) { + throw new DataException("MCRegion file does not contain " + + x + "," + z + " in full"); + } + return new InflaterInputStream(new ByteArrayInputStream(data)); + } else { + throw new DataException("MCRegion chunk at " + + x + "," + z + " has an unsupported version of " + version); + } + } + + /** + * Get the offset for a chunk. May return 0 if it doesn't exist. + * + * @param x + * @param z + * @return + */ + private int getOffset(int x, int z) { + return offsets[x + z * 32]; + } + + /** + * Returns whether the file contains a chunk. + * + * @param x + * @param z + * @return + */ + public boolean hasChunk(int x, int z) { + return getOffset(x, z) != 0; + } + + /** + * Close the stream. + * + * @throws IOException + */ + public void close() throws IOException { + stream.close(); + } +} \ No newline at end of file diff --git a/src/com/sk89q/worldedit/data/TrueZipAlphaChunkStore.java b/src/com/sk89q/worldedit/data/TrueZipAlphaChunkStore.java deleted file mode 100644 index de521698b..000000000 --- a/src/com/sk89q/worldedit/data/TrueZipAlphaChunkStore.java +++ /dev/null @@ -1,157 +0,0 @@ -// $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 de.schlichtherle.io.*; -import de.schlichtherle.io.File; -import de.schlichtherle.io.FileInputStream; -import java.io.*; -import java.util.regex.Pattern; - -/** - * Represents the chunk store used by Minecraft alpha. This driver uses - * TrueZip to read zip files, althoguh it currently does not work well. - * - * @author sk89q - */ -public class TrueZipAlphaChunkStore extends NestedFileChunkStore { - /** - * Folder to read from. - */ - private File path; - - /** - * Create an instance. The passed path is the folder to read the - * chunk files from. - * - * @param path - */ - public TrueZipAlphaChunkStore(java.io.File path) { - this.path = new File(path); - File root = findWorldPath(this.path); - if (root != null) { - this.path = root; - } - } - - /** - * Create an instance. The passed path is the folder to read the - * chunk files from. - * - * @param path - */ - public TrueZipAlphaChunkStore(File path) { - this.path = path; - File root = findWorldPath(path); - if (root != null) { - this.path = root; - } - } - - /** - * Create an instance. The passed path is the folder to read the - * chunk files from. - * - * @param path - */ - public TrueZipAlphaChunkStore(String path) { - this.path = new File(path); - File root = findWorldPath(this.path); - if (root != null) { - this.path = root; - } - } - - /** - * Get the input stream for a chunk file. - * - * @param f1 - * @param f2 - * @param name - * @return - * @throws DataException - * @throws IOException - */ - @Override - protected InputStream getInputStream(String f1, String f2, String name) - throws DataException, IOException { - String file = f1 + File.separator + f2 + File.separator + name; - File f = new File(path, file); - try { - return new FileInputStream(f.getAbsolutePath()); - } catch (FileNotFoundException e) { - throw new MissingChunkException(); - } - } - - /** - * Find the root directory for the chunk files. - * - * @param path - * @return - */ - private File findWorldPath(File path) { - File f = new File(path, "world"); - - if (path.contains(f)) { - return f; - } - - return searchForPath(path); - } - - /** - * Find the root directory for the chunk files. - * - * @param path - * @return - */ - private File searchForPath(File path) { - String[] children = path.list(); - - Pattern pattern = Pattern.compile(".*[\\\\/]level\\.dat$"); - - if (children == null) { - return null; - } else { - for (String f : children) { - if (pattern.matcher(f).matches()) { - return (File)(new File(path, f)).getParentFile(); - } - } - } - - return null; - } - - /** - * Close the archive. - * - * @throws IOException - */ - @Override - public void close() throws IOException { - try { - File.umount(new File(path)); - } catch (ArchiveException e) { - throw new IOException(e); - } - } -} diff --git a/src/com/sk89q/worldedit/data/TrueZipLegacyAlphaChunkStore.java b/src/com/sk89q/worldedit/data/TrueZipLegacyChunkStore.java similarity index 92% rename from src/com/sk89q/worldedit/data/TrueZipLegacyAlphaChunkStore.java rename to src/com/sk89q/worldedit/data/TrueZipLegacyChunkStore.java index e5372f147..736169fbe 100644 --- a/src/com/sk89q/worldedit/data/TrueZipLegacyAlphaChunkStore.java +++ b/src/com/sk89q/worldedit/data/TrueZipLegacyChunkStore.java @@ -1,167 +1,167 @@ -// $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 java.io.*; -import java.util.regex.Pattern; -import java.util.zip.ZipException; -import java.util.Enumeration; -import de.schlichtherle.util.zip.*; - -/** - * Represents the chunk store used by Minecraft alpha but zipped. Uses - * the replacement classes for java.util.zip.* from TrueZip. - * - * @author sk89q - */ -public class TrueZipLegacyAlphaChunkStore extends NestedFileChunkStore { - /** - * ZIP file. - */ - @SuppressWarnings("unused") - private File zipFile; - /** - * Actual ZIP. - */ - private ZipFile zip; - /** - * Folder inside the ZIP file to read from, if any. - */ - private String folder; - - /** - * Create an instance. The folder argument lets you choose a folder or - * 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 - * @throws IOException - * @throws ZipException - */ - public TrueZipLegacyAlphaChunkStore(File zipFile, String folder) - throws IOException, ZipException { - this.zipFile = zipFile; - this.folder = folder; - - zip = new ZipFile(zipFile); - } - - /** - * Create an instance. The subfolder containing the chunk data will - * be detected. - * - * @param zipFile - * @throws IOException - * @throws ZipException - */ - public TrueZipLegacyAlphaChunkStore(File zipFile) - throws IOException, ZipException { - this.zipFile = zipFile; - - zip = new ZipFile(zipFile); - } - - /** - * Get the input stream for a chunk file. - * - * @param f1 - * @param f2 - * @param name - * @return - * @throws IOException - * @throws DataException - */ - @Override - @SuppressWarnings("unchecked") - protected InputStream getInputStream(String f1, String f2, String name) - 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 = getEntry("world/level.dat"); - - Pattern pattern = Pattern.compile(".*[\\\\/]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 (pattern.matcher(testEntry.getName()).matches()) { - file = testEntry.getName().replaceAll("level\\.dat$", "") - + file; - break; - } - } - } else { - file = "world/" + file; - } - } - } - - ZipEntry entry = getEntry(file); - if (entry == null) { - throw new MissingChunkException(); - } - try { - return zip.getInputStream(entry); - } catch (ZipException e) { - throw new IOException("Failed to read " + file + " in ZIP"); - } - } - - /** - * Get an entry from the ZIP, trying both types of slashes. - * - * @param file - * @return - */ - private ZipEntry getEntry(String file) { - ZipEntry entry = zip.getEntry(file); - if (entry != null) { - return entry; - } - return zip.getEntry(file.replace("/", "\\")); - } - - /** - * Close resources. - * - * @throws IOException - */ - @Override - public void close() throws IOException { - zip.close(); - } -} +// $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 java.io.*; +import java.util.regex.Pattern; +import java.util.zip.ZipException; +import java.util.Enumeration; +import de.schlichtherle.util.zip.*; + +/** + * Represents the chunk store used by Minecraft alpha but zipped. Uses + * the replacement classes for java.util.zip.* from TrueZip. + * + * @author sk89q + */ +public class TrueZipLegacyChunkStore extends LegacyChunkStore { + /** + * ZIP file. + */ + @SuppressWarnings("unused") + private File zipFile; + /** + * Actual ZIP. + */ + private ZipFile zip; + /** + * Folder inside the ZIP file to read from, if any. + */ + private String folder; + + /** + * Create an instance. The folder argument lets you choose a folder or + * 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 + * @throws IOException + * @throws ZipException + */ + public TrueZipLegacyChunkStore(File zipFile, String folder) + throws IOException, ZipException { + this.zipFile = zipFile; + this.folder = folder; + + zip = new ZipFile(zipFile); + } + + /** + * Create an instance. The subfolder containing the chunk data will + * be detected. + * + * @param zipFile + * @throws IOException + * @throws ZipException + */ + public TrueZipLegacyChunkStore(File zipFile) + throws IOException, ZipException { + this.zipFile = zipFile; + + zip = new ZipFile(zipFile); + } + + /** + * Get the input stream for a chunk file. + * + * @param f1 + * @param f2 + * @param name + * @return + * @throws IOException + * @throws DataException + */ + @Override + @SuppressWarnings("unchecked") + protected InputStream getInputStream(String f1, String f2, String name) + 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 = getEntry("world/level.dat"); + + Pattern pattern = Pattern.compile(".*[\\\\/]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 (pattern.matcher(testEntry.getName()).matches()) { + file = testEntry.getName().replaceAll("level\\.dat$", "") + + file; + break; + } + } + } else { + file = "world/" + file; + } + } + } + + ZipEntry entry = getEntry(file); + if (entry == null) { + throw new MissingChunkException(); + } + try { + return zip.getInputStream(entry); + } catch (ZipException e) { + throw new IOException("Failed to read " + file + " in ZIP"); + } + } + + /** + * Get an entry from the ZIP, trying both types of slashes. + * + * @param file + * @return + */ + private ZipEntry getEntry(String file) { + ZipEntry entry = zip.getEntry(file); + if (entry != null) { + return entry; + } + return zip.getEntry(file.replace("/", "\\")); + } + + /** + * Close resources. + * + * @throws IOException + */ + @Override + public void close() throws IOException { + zip.close(); + } +} diff --git a/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java b/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java index 65b783cfc..8823d6117 100644 --- a/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java +++ b/src/com/sk89q/worldedit/data/ZippedAlphaChunkStore.java @@ -29,7 +29,7 @@ import java.util.Enumeration; * * @author sk89q */ -public class ZippedAlphaChunkStore extends NestedFileChunkStore { +public class ZippedAlphaChunkStore extends LegacyChunkStore { /** * ZIP file. */ diff --git a/src/com/sk89q/worldedit/snapshots/Snapshot.java b/src/com/sk89q/worldedit/snapshots/Snapshot.java index f653ea890..356ff353b 100644 --- a/src/com/sk89q/worldedit/snapshots/Snapshot.java +++ b/src/com/sk89q/worldedit/snapshots/Snapshot.java @@ -21,12 +21,15 @@ package com.sk89q.worldedit.snapshots; import com.sk89q.worldedit.data.*; import java.io.*; +import java.util.logging.Logger; /** * * @author sk89q */ public class Snapshot { + protected static Logger logger = Logger.getLogger("Minecraft.WorldEdit"); + /** * Stores snapshot file. */ @@ -57,7 +60,7 @@ public class Snapshot { public ChunkStore getChunkStore() throws IOException, DataException { if (file.getName().toLowerCase().endsWith(".zip")) { try { - return new TrueZipLegacyAlphaChunkStore(file); + return new TrueZipLegacyChunkStore(file); } catch (NoClassDefFoundError e) { return new ZippedAlphaChunkStore(file); } @@ -65,12 +68,12 @@ public class Snapshot { || file.getName().toLowerCase().endsWith(".tar.gz") || file.getName().toLowerCase().endsWith(".tar")) { try { - return new TrueZipAlphaChunkStore(file); + return new TrueZipLegacyChunkStore(file); } catch (NoClassDefFoundError e) { throw new DataException("TrueZIP is required for .tar support"); } } else { - return new AlphaChunkStore(file); + return new FileLegacyChunkStore(file); } }