private static @NotNull ZipFile populateFromCentralDirectory(ByteBuffer buffer, int fileSize, boolean forceNonIkv) throws IOException {
    // https://en.wikipedia.org/wiki/ZIP_(file_format)
    int offset = fileSize - MIN_EOCD_SIZE;

    boolean finished = false;

    // first, EOCD
    for (; offset >= 0; offset--) {
      if (buffer.getInt(offset) == EOCD) {
        finished = true;
        break;
      }
    }
    if (!finished) {
      throw new ZipException("Archive is not a ZIP archive");
    }

    if (offset == 0) {
      // empty file
      return new EmptyZipFile();
    }

    boolean isZip64 = true;
    if (buffer.getInt(offset - 20) == 0x07064b50) {
      offset = (int)buffer.getLong(offset - (20 - 8));
      assert buffer.getInt(offset) == 0x06064b50;
    }
    else {
      isZip64 = false;
    }

    int entryCount;
    int centralDirSize;
    int centralDirPosition;
    int commentSize;
    int commentVersion;
    int indexDataEnd = -1;
    if (isZip64) {
      entryCount = (int)buffer.getLong(offset + 32);
      centralDirSize = (int)buffer.getLong(offset + 40);
      centralDirPosition = (int)buffer.getLong(offset + 48);

      commentSize = (int)(buffer.getLong(offset + 4) + 12) - 56;
      commentVersion = commentSize == COMMENT_SIZE ? buffer.get(offset + 56) : 0;
      if (commentVersion == INDEX_FORMAT_VERSION) {
        indexDataEnd = buffer.getInt(offset + 56 + 1);
      }
    }
    else {
      entryCount = buffer.getShort(offset + 10) & 0xffff;
      centralDirSize = buffer.getInt(offset + 12);
      centralDirPosition = buffer.getInt(offset + 16);

      commentSize = buffer.getShort(offset + 20);
      commentVersion = commentSize == COMMENT_SIZE ? buffer.get(offset + 22) : 0;
      if (commentVersion == INDEX_FORMAT_VERSION) {
        indexDataEnd = buffer.getInt(offset + 22 + Byte.BYTES /* index format version size */);
      }
    }

    if (forceNonIkv || commentVersion != INDEX_FORMAT_VERSION || entryCount == 0) {
      return HashMapZipFile.createHashMapZipFile(buffer, fileSize, entryCount, centralDirSize, centralDirPosition);
    }

    buffer.position(indexDataEnd);
    Ikv.SizeAwareIkv ikv = (Ikv.SizeAwareIkv)Ikv.loadIkv(buffer, indexDataEnd);
    buffer.position(indexDataEnd);
    // read package class and resource hashes
    long[] classPackages = new long[buffer.getInt()];
    long[] resourcePackages = new long[buffer.getInt()];
    LongBuffer longBuffer = buffer.asLongBuffer();
    longBuffer.get(classPackages);
    longBuffer.get(resourcePackages);

    int nameDataPosition = indexDataEnd + Long.BYTES + (longBuffer.position() * Long.BYTES);
    buffer.clear();
    return new ImmutableZipFile(ikv, classPackages, resourcePackages, nameDataPosition);
  }
private static @NotNull ZipFile populateFromCentralDirectory(ByteBuffer buffer, int fileSize, boolean forceNonIkv)