/* * Copyright 2011 Tyler Blair. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and contributors and should not be interpreted as representing official policies, * either expressed or implied, of anybody else. */ package com.griefcraft.io; import org.bukkit.inventory.ItemStack; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.EnumSet; import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class Backup { /** * The backup file's current revision */ public static final int CURRENT_REVISION = 1; /** * The operations the backup is allowed to perform */ public enum OperationMode { READ, WRITE } /** * The file this backup is located at */ private final File file; /** * The operationMode we are allowed to perform */ private final OperationMode operationMode; /** * The flags we are using */ private final EnumSet<BackupManager.Flag> flags; /** * This backup's revision number */ private int revision; /** * The time the backup was created it */ private long created; /** * The backup's input stream if we are reading */ private DataInputStream inputStream; /** * The backup's output stream if we are writing */ private DataOutputStream outputStream; public Backup(File file, OperationMode operationMode, EnumSet<BackupManager.Flag> flags) throws IOException { this.file = file; this.operationMode = operationMode; this.flags = flags; if (!file.exists()) { if (operationMode == OperationMode.READ) { throw new UnsupportedOperationException("The backup could not be read"); } else { file.createNewFile(); } } // Set some base data if we're writing if (operationMode == OperationMode.WRITE) { revision = CURRENT_REVISION; created = System.currentTimeMillis() / 1000; } // Are we using compression? boolean compression = flags.contains(BackupManager.Flag.COMPRESSION); // create the stream we need if (operationMode == OperationMode.READ) { FileInputStream fis = new FileInputStream(file); inputStream = new DataInputStream(compression ? new GZIPInputStream(fis) : fis); } else if (operationMode == OperationMode.WRITE) { FileOutputStream fos = new FileOutputStream(file); outputStream = new DataOutputStream(compression ? new GZIPOutputStream(fos) : fos); } } /** * Read an entity from the backup file * * @return */ protected Restorable readRestorable() throws IOException { if (operationMode != OperationMode.READ) { throw new UnsupportedOperationException("READ is not allowed on this backup."); } // The object type int type = (byte) inputStream.read(); // EOF if (type == -1) { return null; } // TODO enum that shit yo if (type == 0) { // Protection RestorableProtection rprotection = new RestorableProtection(); rprotection.setId(inputStream.readInt()); rprotection.setProtectionType(inputStream.readByte()); rprotection.setBlockId(inputStream.readShort()); rprotection.setOwner(inputStream.readUTF()); rprotection.setWorld(inputStream.readUTF()); rprotection.setX(inputStream.readInt()); rprotection.setY(inputStream.readShort()); rprotection.setZ(inputStream.readInt()); rprotection.setData(inputStream.readUTF()); rprotection.setCreated(inputStream.readLong()); rprotection.setUpdated(inputStream.readLong()); return rprotection; } else if (type == 1) { // Block RestorableBlock rblock = new RestorableBlock(); rblock.setId(inputStream.readShort()); rblock.setWorld(inputStream.readUTF()); rblock.setX(inputStream.readInt()); rblock.setY(inputStream.readShort()); rblock.setZ(inputStream.readInt()); rblock.setData(inputStream.read() & 0xFF); int itemCount = inputStream.readShort(); for (int i = 0; i < itemCount; i++) { // Read in us some RestorableItems int slot = inputStream.readShort(); int itemId = inputStream.readShort(); int amount = inputStream.readShort(); short damage = inputStream.readShort(); // Create the stack ItemStack itemStack = new ItemStack(itemId, amount, damage); // add it to the block rblock.setSlot(slot, itemStack); } // Woo! return rblock; } throw new UnsupportedOperationException("Read unknown type: " + type); } /** * Write an entity to the backup file * * @param restorable */ protected void writeRestorable(Restorable restorable) throws IOException { if (operationMode != OperationMode.WRITE) { throw new UnsupportedOperationException("WRITE is not allowed on this backup."); } // write the id outputStream.write((byte) restorable.getType()); // Write it if (restorable.getType() == 0) { // Protection, also TODO ENUMSSSSSSSSSSS RestorableProtection rprotection = (RestorableProtection) restorable; outputStream.writeInt(rprotection.getId()); outputStream.writeByte(rprotection.getType()); outputStream.writeShort(rprotection.getBlockId()); outputStream.writeUTF(rprotection.getOwner()); outputStream.writeUTF(rprotection.getWorld()); outputStream.writeInt(rprotection.getX()); outputStream.writeShort(rprotection.getY()); outputStream.writeInt(rprotection.getZ()); outputStream.writeUTF(rprotection.getData()); outputStream.writeLong(rprotection.getCreated()); outputStream.writeLong(rprotection.getUpdated()); } else if (restorable.getType() == 1) { // Block, TODO DID I SAY TO DO THE ENUM YET?? RestorableBlock rblock = (RestorableBlock) restorable; outputStream.writeShort(rblock.getId()); outputStream.writeUTF(rblock.getWorld()); outputStream.writeInt(rblock.getX()); outputStream.writeShort(rblock.getY()); outputStream.writeInt(rblock.getZ()); outputStream.write((byte) rblock.getData()); outputStream.writeShort(rblock.getItems().size()); // Write the items if there are any for (Map.Entry<Integer, ItemStack> entry : rblock.getItems().entrySet()) { int slot = entry.getKey(); ItemStack stack = entry.getValue(); outputStream.writeShort(slot); outputStream.writeShort(stack.getTypeId()); outputStream.writeShort(stack.getAmount()); outputStream.writeShort(stack.getDurability()); } } outputStream.flush(); } /** * Read the backup's header * * @throws IOException */ protected void readHeader() throws IOException { revision = inputStream.readShort(); created = inputStream.readLong(); inputStream.read(new byte[10]); // reserved space } /** * Write the backup's header * * @throws IOException */ protected void writeHeader() throws IOException { outputStream.writeShort(revision); outputStream.writeLong(created); outputStream.write(new byte[10]); // reserved space outputStream.flush(); } /** * Close the backup file * * @throws IOException */ protected void close() throws IOException { if (operationMode == OperationMode.READ) { inputStream.close(); } else if (operationMode == OperationMode.WRITE) { outputStream.close(); } } }