public synchronized DataInputStream getChunkDataInputStream(final int x,
      final int z) {
    if(outOfBounds(x, z)) {
      debugln("READ", x, z, "out of bounds");
      return null;
    }

    try {
      final int offset = getOffset(x, z);
      if(offset == 0) // debugln("READ", x, z, "miss");
      return null;

      final int sectorNumber = offset >> 8;
      final int numSectors = offset & 0xFF;

      if(sectorNumber + numSectors > sectorFree.size()) {
        debugln("READ", x, z, "invalid sector");
        return null;
      }

      file.seek(sectorNumber * SECTOR_BYTES);
      final int length = file.readInt();

      if(length > SECTOR_BYTES * numSectors) {
        debugln("READ", x, z, "invalid length: " + length
            + " > 4096 * " + numSectors);
        return null;
      }

      final byte version = file.readByte();
      if(version == VERSION_GZIP) {
        final byte[] data = new byte[length - 1];
        file.read(data);
        final DataInputStream ret = new DataInputStream(
            new BufferedInputStream(new GZIPInputStream(
                new ByteArrayInputStream(data))));
        // debug("READ", x, z, " = found");
        return ret;
      } else if(version == VERSION_DEFLATE) {
        final byte[] data = new byte[length - 1];
        file.read(data);
        final DataInputStream ret = new DataInputStream(
            new BufferedInputStream(new InflaterInputStream(
                new ByteArrayInputStream(data))));
        // debug("READ", x, z, " = found");
        return ret;
      }

      debugln("READ", x, z, "unknown version " + version);
      return null;
    } catch(final IOException e) {
      debugln("READ", x, z, "exception");
      return null;
    }
  }