// Returns error code and reason string
  private int deployShip(
      String fileName, int offsetX, int offsetY, int offsetZ, StringBuilder reason) {
    // Load schematic
    NBTTagCompound schematic = readNBTFromFile(WarpDriveConfig.G_SCHEMALOCATION + "/" + fileName);
    if (schematic == null) {
      reason.append("Schematic not found or unknow error reading it.");
      return -1;
    }

    // Compute geometry
    short width = schematic.getShort("Width");
    short height = schematic.getShort("Height");
    short length = schematic.getShort("Length");

    targetX = xCoord + offsetX;
    targetY = yCoord + offsetY;
    targetZ = zCoord + offsetZ;
    blocksToDeployCount = width * height * length;

    // Validate context
    {
      // Check distance
      double dX = xCoord - targetX;
      double dY = yCoord - targetY;
      double dZ = zCoord - targetZ;
      double distance = MathHelper.sqrt_double(dX * dX + dY * dY + dZ * dZ);

      if (distance > WarpDriveConfig.SS_MAX_DEPLOY_RADIUS_BLOCKS) {
        reason.append("Cannot deploy ship so far away from scanner.");
        return 5;
      }

      // Consume energy
      if (!consumeEnergy(getDeploymentEnergyCost(blocksToDeployCount), false)) {
        reason.append(
            "Insufficient energy (" + getDeploymentEnergyCost(blocksToDeployCount) + " required)");
        return 1;
      }

      // Check specified area for occupation by blocks
      // If specified area occupied, break deploying with error message
      int occupiedBlockCount = 0;
      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
          for (int z = 0; z < length; z++) {
            if (!worldObj.isAirBlock(targetX + x, targetY + y, targetZ + z)) occupiedBlockCount++;
          }
        }
      }
      if (occupiedBlockCount > 0) {
        reason.append(
            "Deploying area occupied with " + occupiedBlockCount + " blocks. Can't deploy ship.");
        return 2;
      }
    }

    // Set deployment variables
    blocksToDeploy = new JumpBlock[blocksToDeployCount];
    isDeploying = true;
    currentDeployIndex = 0;

    // Read blocks and TileEntities from NBT to internal storage array
    NBTTagList localBlocks = (NBTTagList) schematic.getTag("Blocks");
    byte localMetadata[] = schematic.getByteArray("Data");

    // Load Tile Entities
    NBTTagCompound[] tileEntities = new NBTTagCompound[blocksToDeployCount];
    NBTTagList tileEntitiesList =
        schematic.getTagList(
            "TileEntities", new NBTTagByteArray(new byte[0]).getId()); // TODO: 0 is not correct

    for (int i = 0; i < tileEntitiesList.tagCount(); i++) {
      NBTTagCompound teTag = tileEntitiesList.getCompoundTagAt(i);
      int teX = teTag.getInteger("x");
      int teY = teTag.getInteger("y");
      int teZ = teTag.getInteger("z");

      tileEntities[teX + (teY * length + teZ) * width] = teTag;
    }

    // Create list of blocks to deploy
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        for (int z = 0; z < length; z++) {
          int index = x + (y * length + z) * width;
          JumpBlock jb = new JumpBlock();

          jb.x = x;
          jb.y = y;
          jb.z = z;
          jb.block = Block.getBlockFromName(localBlocks.getStringTagAt(index));
          jb.blockMeta = (localMetadata[index]) & 0xFF;
          jb.blockNBT = tileEntities[index];

          if (jb.block != null) {

            if (WarpDriveConfig.LOGGING_BUILDING) {
              if (tileEntities[index] == null) {
                WarpDrive.logger.info(
                    "[ShipScanner] Adding block to deploy: "
                        + jb.block.getUnlocalizedName()
                        + " (no tile entity)");
              } else {
                WarpDrive.logger.info(
                    "[ShipScanner] Adding block to deploy: "
                        + jb.block.getUnlocalizedName()
                        + " with tile entity "
                        + tileEntities[index].getString("id"));
              }
            }

            blocksToDeploy[index] = jb;
          } else {
            jb = null;

            blocksToDeploy[index] = jb;
          }
        }
      }
    }

    setActive(true);
    reason.append("Ship deploying...");
    return 3;
  }