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 void parseETSFile(String file, int s) throws FormatException, IOException {
    RandomAccessInputStream etsFile = new RandomAccessInputStream(file);
    etsFile.order(true);

    // read the volume header
    String magic = etsFile.readString(4).trim();
    if (!magic.equals("SIS")) {
      throw new FormatException("Unknown magic bytes: " + magic);
    }

    int headerSize = etsFile.readInt();
    int version = etsFile.readInt();
    nDimensions[s] = etsFile.readInt();
    long additionalHeaderOffset = etsFile.readLong();
    int additionalHeaderSize = etsFile.readInt();
    etsFile.skipBytes(4); // reserved
    long usedChunkOffset = etsFile.readLong();
    int nUsedChunks = etsFile.readInt();
    etsFile.skipBytes(4); // reserved

    // read the additional header
    etsFile.seek(additionalHeaderOffset);

    String moreMagic = etsFile.readString(4).trim();
    if (!moreMagic.equals("ETS")) {
      throw new FormatException("Unknown magic bytes: " + moreMagic);
    }

    etsFile.skipBytes(4); // extra version number

    int pixelType = etsFile.readInt();
    core[s].sizeC = etsFile.readInt();
    int colorspace = etsFile.readInt();
    compressionType[s] = etsFile.readInt();
    int compressionQuality = etsFile.readInt();
    tileX[s] = etsFile.readInt();
    tileY[s] = etsFile.readInt();
    int tileZ = etsFile.readInt();

    core[s].rgb = core[s].sizeC > 1;

    // read the used chunks

    etsFile.seek(usedChunkOffset);

    tileOffsets[s] = new Long[nUsedChunks];

    ArrayList<TileCoordinate> tmpTiles = new ArrayList<TileCoordinate>();

    for (int chunk = 0; chunk < nUsedChunks; chunk++) {
      etsFile.skipBytes(4);
      TileCoordinate t = new TileCoordinate(nDimensions[s]);
      for (int i = 0; i < nDimensions[s]; i++) {
        t.coordinate[i] = etsFile.readInt();
      }
      tileOffsets[s][chunk] = etsFile.readLong();
      int nBytes = etsFile.readInt();
      etsFile.skipBytes(4);

      tmpTiles.add(t);
    }

    int maxX = 0;
    int maxY = 0;
    int maxZ = 0;
    int maxC = 0;
    int maxT = 0;

    for (TileCoordinate t : tmpTiles) {
      Integer tv = dimensionOrdering.get("T");
      Integer zv = dimensionOrdering.get("Z");
      Integer cv = dimensionOrdering.get("C");

      int tIndex = tv == null ? -1 : tv + 2;
      int zIndex = zv == null ? -1 : zv + 2;
      int cIndex = cv == null ? -1 : cv + 2;

      if (tv == null && zv == null) {
        if (t.coordinate.length > 4 && cv == null) {
          cIndex = 2;
          dimensionOrdering.put("C", cIndex - 2);
        }

        if (t.coordinate.length > 4) {
          if (cv == null) {
            tIndex = 3;
          } else {
            tIndex = cIndex + 2;
          }
          if (tIndex < t.coordinate.length) {
            dimensionOrdering.put("T", tIndex - 2);
          } else {
            tIndex = -1;
          }
        }

        if (t.coordinate.length > 5) {
          if (cv == null) {
            zIndex = 4;
          } else {
            zIndex = cIndex + 1;
          }
          if (zIndex < t.coordinate.length) {
            dimensionOrdering.put("Z", zIndex - 2);
          } else {
            zIndex = -1;
          }
        }
      }

      if (t.coordinate[0] > maxX) {
        maxX = t.coordinate[0];
      }
      if (t.coordinate[1] > maxY) {
        maxY = t.coordinate[1];
      }

      if (tIndex >= 0 && t.coordinate[tIndex] > maxT) {
        maxT = t.coordinate[tIndex];
      }
      if (zIndex >= 0 && t.coordinate[zIndex] > maxZ) {
        maxZ = t.coordinate[zIndex];
      }
      if (cIndex >= 0 && t.coordinate[cIndex] > maxC) {
        maxC = t.coordinate[cIndex];
      }
    }

    if (maxX > 1) {
      core[s].sizeX = tileX[s] * (maxX + 1);
    } else {
      core[s].sizeX = tileX[s];
    }
    if (maxY > 1) {
      core[s].sizeY = tileY[s] * (maxY + 1);
    } else {
      core[s].sizeY = tileY[s];
    }
    core[s].sizeZ = maxZ + 1;
    if (maxC > 0) {
      core[s].sizeC *= (maxC + 1);
    }
    core[s].sizeT = maxT + 1;
    if (core[s].sizeZ == 0) {
      core[s].sizeZ = 1;
    }
    core[s].imageCount = core[s].sizeZ * core[s].sizeT;
    if (maxC > 0) {
      core[s].imageCount *= (maxC + 1);
    }

    if (maxY > 1) {
      rows[s] = maxY + 1;
    } else {
      rows[s] = 1;
    }
    if (maxX > 1) {
      cols[s] = maxX + 1;
    } else {
      cols[s] = 1;
    }

    for (int i = 0; i < tmpTiles.size(); i++) {
      tileMap[s].put(tmpTiles.get(i), i);
    }

    core[s].pixelType = convertPixelType(pixelType);
    etsFile.close();
  }