public CuboidClipboard load(InputStream stream) throws IOException, DataException {
    NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(stream));

    Vector origin = new Vector();
    Vector offset = new Vector();

    // Schematic tag
    NamedTag rootTag = nbtStream.readNamedTag();
    nbtStream.close();
    if (!rootTag.getName().equals("Schematic")) {
      throw new DataException("Tag \"Schematic\" does not exist or is not first");
    }

    CompoundTag schematicTag = (CompoundTag) rootTag.getTag();

    // Check
    Map<String, Tag> schematic = schematicTag.getValue();
    if (!schematic.containsKey("Blocks")) {
      throw new DataException("Schematic file is missing a \"Blocks\" tag");
    }

    // Get information
    short width = getChildTag(schematic, "Width", ShortTag.class).getValue();
    short length = getChildTag(schematic, "Length", ShortTag.class).getValue();
    short height = getChildTag(schematic, "Height", ShortTag.class).getValue();

    try {
      int originX = getChildTag(schematic, "WEOriginX", IntTag.class).getValue();
      int originY = getChildTag(schematic, "WEOriginY", IntTag.class).getValue();
      int originZ = getChildTag(schematic, "WEOriginZ", IntTag.class).getValue();
      origin = new Vector(originX, originY, originZ);
    } catch (DataException e) {
      // No origin data
    }

    try {
      int offsetX = getChildTag(schematic, "WEOffsetX", IntTag.class).getValue();
      int offsetY = getChildTag(schematic, "WEOffsetY", IntTag.class).getValue();
      int offsetZ = getChildTag(schematic, "WEOffsetZ", IntTag.class).getValue();
      offset = new Vector(offsetX, offsetY, offsetZ);
    } catch (DataException e) {
      // No offset data
    }

    // Check type of Schematic
    String materials = getChildTag(schematic, "Materials", StringTag.class).getValue();
    if (!materials.equals("Alpha")) {
      throw new DataException("Schematic file is not an Alpha schematic");
    }

    // Get blocks
    byte[] blockId = getChildTag(schematic, "Blocks", ByteArrayTag.class).getValue();
    byte[] blockData = getChildTag(schematic, "Data", ByteArrayTag.class).getValue();
    byte[] addId = new byte[0];
    short[] blocks = new short[blockId.length]; // Have to later combine IDs

    // We support 4096 block IDs using the same method as vanilla Minecraft, where
    // the highest 4 bits are stored in a separate byte array.
    if (schematic.containsKey("AddBlocks")) {
      addId = getChildTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
    }

    // Combine the AddBlocks data with the first 8-bit block ID
    for (int index = 0; index < blockId.length; index++) {
      if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
        blocks[index] = (short) (blockId[index] & 0xFF);
      } else {
        if ((index & 1) == 0) {
          blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
        } else {
          blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
        }
      }
    }

    // Need to pull out tile entities
    List<Tag> tileEntities = getChildTag(schematic, "TileEntities", ListTag.class).getValue();
    Map<BlockVector, Map<String, Tag>> tileEntitiesMap =
        new HashMap<BlockVector, Map<String, Tag>>();

    for (Tag tag : tileEntities) {
      if (!(tag instanceof CompoundTag)) continue;
      CompoundTag t = (CompoundTag) tag;

      int x = 0;
      int y = 0;
      int z = 0;

      Map<String, Tag> values = new HashMap<String, Tag>();

      for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
        if (entry.getKey().equals("x")) {
          if (entry.getValue() instanceof IntTag) {
            x = ((IntTag) entry.getValue()).getValue();
          }
        } else if (entry.getKey().equals("y")) {
          if (entry.getValue() instanceof IntTag) {
            y = ((IntTag) entry.getValue()).getValue();
          }
        } else if (entry.getKey().equals("z")) {
          if (entry.getValue() instanceof IntTag) {
            z = ((IntTag) entry.getValue()).getValue();
          }
        }

        values.put(entry.getKey(), entry.getValue());
      }

      BlockVector vec = new BlockVector(x, y, z);
      tileEntitiesMap.put(vec, values);
    }

    Vector size = new Vector(width, height, length);
    CuboidClipboard clipboard = new CuboidClipboard(size);
    clipboard.setOrigin(origin);
    clipboard.setOffset(offset);

    for (int x = 0; x < width; ++x) {
      for (int y = 0; y < height; ++y) {
        for (int z = 0; z < length; ++z) {
          int index = y * width * length + z * width + x;
          BlockVector pt = new BlockVector(x, y, z);
          BaseBlock block = getBlockForId(blocks[index], blockData[index]);

          if (tileEntitiesMap.containsKey(pt)) {
            block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt)));
          }
          clipboard.setBlock(pt, block);
        }
      }
    }

    return clipboard;
  }