/* @see loci.formats.IFormatReader#isThisType(String, boolean) */
 public boolean isThisType(String name, boolean open) {
   if (!open) return false; // not allowed to touch the file system
   if (name.equals(METADATA)
       || name.endsWith(File.separator + METADATA)
       || name.equals(XML)
       || name.endsWith(File.separator + XML)) {
     final int blockSize = 1048576;
     try {
       RandomAccessInputStream stream = new RandomAccessInputStream(name);
       long length = stream.length();
       String data = stream.readString((int) Math.min(blockSize, length));
       stream.close();
       return length > 0
           && (data.indexOf("Micro-Manager") >= 0 || data.indexOf("micromanager") >= 0);
     } catch (IOException e) {
       return false;
     }
   }
   try {
     Location parent = new Location(name).getAbsoluteFile().getParentFile();
     Location metaFile = new Location(parent, METADATA);
     RandomAccessInputStream s = new RandomAccessInputStream(name);
     boolean validTIFF = isThisType(s);
     s.close();
     return validTIFF && isThisType(metaFile.getAbsolutePath(), open);
   } catch (NullPointerException e) {
   } catch (IOException e) {
   }
   return false;
 }
  /**
   * Constructor RawDataBlockList
   *
   * @param stream the InputStream from which the data will be read
   * @exception IOException on I/O errors, and if an incomplete block is read
   */
  public RawDataBlockList(final RandomAccessInputStream stream, int size) throws IOException {
    List blocks = new ArrayList();
    bigBlockSize = size;
    while (true) {
      RawDataBlock block = new RawDataBlock(stream, size);

      if (block.eof()) {
        break;
      }
      blocks.add(block);
      if (size + stream.getFilePointer() > stream.length()) {
        break;
      }
      stream.skipBytes(size);
    }
    setBlocks((RawDataBlock[]) blocks.toArray(new RawDataBlock[0]));
  }
  private void readTags(RandomAccessInputStream vsi) throws IOException {
    // read the VSI header
    long fp = vsi.getFilePointer();
    if (fp + 24 >= vsi.length()) {
      return;
    }
    int headerSize = vsi.readShort(); // should always be 24
    int version = vsi.readShort(); // always 21321
    int volumeVersion = vsi.readInt();
    long dataFieldOffset = vsi.readLong();
    int flags = vsi.readInt();
    vsi.skipBytes(4);

    int tagCount = flags & 0xfffffff;

    if (fp + dataFieldOffset < 0) {
      return;
    }

    vsi.seek(fp + dataFieldOffset);
    if (vsi.getFilePointer() >= vsi.length()) {
      return;
    }

    for (int i = 0; i < tagCount; i++) {
      if (vsi.getFilePointer() + 16 >= vsi.length()) {
        break;
      }

      // read the data field

      int fieldType = vsi.readInt();
      int tag = vsi.readInt();
      long nextField = vsi.readInt() & 0xffffffffL;
      int dataSize = vsi.readInt();

      boolean extraTag = ((fieldType & 0x8000000) >> 27) == 1;
      boolean extendedField = ((fieldType & 0x10000000) >> 28) == 1;
      boolean inlineData = ((fieldType & 0x40000000) >> 30) == 1;
      boolean array = (!inlineData && !extendedField) && ((fieldType & 0x20000000) >> 29) == 1;
      boolean newVolume = ((fieldType & 0x80000000) >> 31) == 1;

      int realType = fieldType & 0xffffff;
      int secondTag = -1;

      if (extraTag) {
        secondTag = vsi.readInt();
      }

      if (extendedField && realType == NEW_VOLUME_HEADER) {
        if (tag == 2007) {
          dimensionTag = secondTag;
          inDimensionProperties = true;
        }
        long endPointer = vsi.getFilePointer() + dataSize;
        while (vsi.getFilePointer() < endPointer && vsi.getFilePointer() < vsi.length()) {
          long start = vsi.getFilePointer();
          readTags(vsi);
          long end = vsi.getFilePointer();
          if (start == end) {
            break;
          }
        }
        if (tag == 2007) {
          inDimensionProperties = false;
          foundChannelTag = false;
        }
      }

      if (inDimensionProperties) {
        if (tag == 2012 && !dimensionOrdering.containsValue(dimensionTag)) {
          dimensionOrdering.put("Z", dimensionTag);
        } else if ((tag == 2100 || tag == 2027) && !dimensionOrdering.containsValue(dimensionTag)) {
          dimensionOrdering.put("T", dimensionTag);
        } else if (tag == 2039 && !dimensionOrdering.containsValue(dimensionTag)) {
          dimensionOrdering.put("L", dimensionTag);
        } else if (tag == 2008
            && foundChannelTag
            && !dimensionOrdering.containsValue(dimensionTag)) {
          dimensionOrdering.put("C", dimensionTag);
        } else if (tag == 2008) {
          foundChannelTag = true;
        }
      }

      if (nextField == 0) {
        return;
      }

      if (fp + nextField < vsi.length() && fp + nextField >= 0) {
        vsi.seek(fp + nextField);
      } else break;
    }
  }
  private byte[] decodeTile(int no, int row, int col) throws FormatException, IOException {
    if (tileMap[getCoreIndex()] == null) {
      return new byte[getTileSize()];
    }

    int[] zct = getZCTCoords(no);
    TileCoordinate t = new TileCoordinate(nDimensions[getCoreIndex()]);
    t.coordinate[0] = col;
    t.coordinate[1] = row;

    for (String dim : dimensionOrdering.keySet()) {
      int index = dimensionOrdering.get(dim) + 2;

      if (dim.equals("Z")) {
        t.coordinate[index] = zct[0];
      } else if (dim.equals("C")) {
        t.coordinate[index] = zct[1];
      } else if (dim.equals("T")) {
        t.coordinate[index] = zct[2];
      }
    }

    Integer index = (Integer) tileMap[getCoreIndex()].get(t);
    if (index == null) {
      return new byte[getTileSize()];
    }

    Long offset = tileOffsets[getCoreIndex()][index];
    RandomAccessInputStream ets = new RandomAccessInputStream(usedFiles[getCoreIndex()]);
    ets.seek(offset);

    CodecOptions options = new CodecOptions();
    options.interleaved = isInterleaved();
    options.littleEndian = isLittleEndian();
    int tileSize = getTileSize();
    if (tileSize == 0) {
      tileSize = tileX[getCoreIndex()] * tileY[getCoreIndex()] * 10;
    }
    options.maxBytes = (int) (offset + tileSize);

    byte[] buf = null;
    long end =
        index < tileOffsets[getCoreIndex()].length - 1
            ? tileOffsets[getCoreIndex()][index + 1]
            : ets.length();

    IFormatReader reader = null;
    String file = null;

    switch (compressionType[getCoreIndex()]) {
      case RAW:
        buf = new byte[tileSize];
        ets.read(buf);
        break;
      case JPEG:
        Codec codec = new JPEGCodec();
        buf = codec.decompress(ets, options);
        break;
      case JPEG_2000:
        codec = new JPEG2000Codec();
        buf = codec.decompress(ets, options);
        break;
      case PNG:
        file = "tile.png";
        reader = new APNGReader();
      case BMP:
        if (reader == null) {
          file = "tile.bmp";
          reader = new BMPReader();
        }

        byte[] b = new byte[(int) (end - offset)];
        ets.read(b);
        Location.mapFile(file, new ByteArrayHandle(b));
        reader.setId(file);
        buf = reader.openBytes(0);
        Location.mapFile(file, null);
        break;
    }

    if (reader != null) {
      reader.close();
    }

    ets.close();
    return buf;
  }
  /**
   * The CodecOptions parameter should have the following fields set: {@link CodecOptions#width
   * width} {@link CodecOptions#height height}
   *
   * @see Codec#decompress(RandomAccessInputStream, CodecOptions)
   */
  public byte[] decompress(RandomAccessInputStream in, CodecOptions options)
      throws FormatException, IOException {
    if (in == null) throw new IllegalArgumentException("No data to decompress.");
    if (options == null) options = CodecOptions.getDefaultOptions();

    in.skipBytes(8);

    int plane = options.width * options.height;

    stride = options.width;
    int rowInc = stride - 4;
    short opcode;
    int nBlocks;
    int colorA = 0, colorB;
    int[] color4 = new int[4];
    int index, idx;
    int ta, tb;
    int blockPtr = 0;
    rowPtr = pixelPtr = 0;
    int pixelX, pixelY;

    int[] pixels = new int[plane];
    byte[] rtn = new byte[plane * 3];

    while (in.read() != (byte) 0xe1) ;
    in.skipBytes(3);

    totalBlocks = ((options.width + 3) / 4) * ((options.height + 3) / 4);

    while (in.getFilePointer() + 2 < in.length()) {
      opcode = in.readByte();
      nBlocks = (opcode & 0x1f) + 1;

      if ((opcode & 0x80) == 0) {
        if (in.getFilePointer() >= in.length()) break;
        colorA = (opcode << 8) | in.read();
        opcode = 0;
        if (in.getFilePointer() >= in.length()) break;
        if ((in.read() & 0x80) != 0) {
          opcode = 0x20;
          nBlocks = 1;
        }
        in.seek(in.getFilePointer() - 1);
      }

      switch (opcode & 0xe0) {
        case 0x80:
          while (nBlocks-- > 0) {
            updateBlock(options.width);
          }
          break;
        case 0xa0:
          if (in.getFilePointer() + 2 >= in.length()) break;
          colorA = in.readShort();
          while (nBlocks-- > 0) {
            blockPtr = rowPtr + pixelPtr;
            for (pixelY = 0; pixelY < 4; pixelY++) {
              for (pixelX = 0; pixelX < 4; pixelX++) {
                if (blockPtr >= pixels.length) break;
                pixels[blockPtr] = colorA;

                short s = (short) (pixels[blockPtr] & 0x7fff);
                unpack(s, rtn, blockPtr, pixels.length);
                blockPtr++;
              }
              blockPtr += rowInc;
            }
            updateBlock(options.width);
          }
          break;
        case 0xc0:
        case 0x20:
          if (in.getFilePointer() + 2 >= in.length()) break;
          if ((opcode & 0xe0) == 0xc0) {
            colorA = in.readShort();
          }

          colorB = in.readShort();

          color4[0] = colorB;
          color4[1] = 0;
          color4[2] = 0;
          color4[3] = colorA;

          ta = (colorA >> 10) & 0x1f;
          tb = (colorB >> 10) & 0x1f;
          color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
          color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;

          ta = (colorA >> 5) & 0x1f;
          tb = (colorB >> 5) & 0x1f;
          color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
          color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;

          ta = colorA & 0x1f;
          tb = colorB & 0x1f;
          color4[1] |= (11 * ta + 21 * tb) >> 5;
          color4[2] |= (21 * ta + 11 * tb) >> 5;

          while (nBlocks-- > 0) {
            blockPtr = rowPtr + pixelPtr;
            for (pixelY = 0; pixelY < 4; pixelY++) {
              if (in.getFilePointer() >= in.length()) break;
              index = in.read();
              for (pixelX = 0; pixelX < 4; pixelX++) {
                idx = (index >> (2 * (3 - pixelX))) & 3;
                if (blockPtr >= pixels.length) break;
                pixels[blockPtr] = color4[idx];

                short s = (short) (pixels[blockPtr] & 0x7fff);
                unpack(s, rtn, blockPtr, pixels.length);
                blockPtr++;
              }
              blockPtr += rowInc;
            }
            updateBlock(options.width);
          }
          break;
        case 0x00:
          blockPtr = rowPtr + pixelPtr;
          for (pixelY = 0; pixelY < 4; pixelY++) {
            for (pixelX = 0; pixelX < 4; pixelX++) {
              if ((pixelY != 0) || (pixelX != 0)) {
                if (in.getFilePointer() + 2 >= in.length()) break;
                colorA = in.readShort();
              }
              if (blockPtr >= pixels.length) break;
              pixels[blockPtr] = colorA;

              short s = (short) (pixels[blockPtr] & 0x7fff);
              unpack(s, rtn, blockPtr, pixels.length);
              blockPtr++;
            }
            blockPtr += rowInc;
          }
          updateBlock(options.width);
          break;
      }
    }
    return rtn;
  }