/**
   * Writes the central file header entry.
   *
   * @param ze the entry to write
   * @throws IOException on error
   * @throws Zip64RequiredException if the archive's size exceeds 4 GByte and {@link Zip64Mode
   *     #setUseZip64} is {@link Zip64Mode#Never}.
   */
  protected void writeCentralFileHeader(ZipArchiveEntry ze) throws IOException {
    writeOut(CFH_SIG);
    written += WORD;

    final long lfhOffset = offsets.get(ze).longValue();
    final boolean needsZip64Extra =
        hasZip64Extra(ze)
            || ze.getCompressedSize() >= ZIP64_MAGIC
            || ze.getSize() >= ZIP64_MAGIC
            || lfhOffset >= ZIP64_MAGIC;

    if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
      // must be the offset that is too big, otherwise an
      // exception would have been throw in putArchiveEntry or
      // closeArchiveEntry
      throw new Zip64RequiredException(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE);
    }

    handleZip64Extra(ze, lfhOffset, needsZip64Extra);

    // version made by
    // CheckStyle:MagicNumber OFF
    writeOut(
        ZipShort.getBytes(
            (ze.getPlatform() << 8)
                | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION)));
    written += SHORT;

    final int zipMethod = ze.getMethod();
    final boolean encodable = zipEncoding.canEncode(ze.getName());
    writeVersionNeededToExtractAndGeneralPurposeBits(
        zipMethod, !encodable && fallbackToUTF8, needsZip64Extra);
    written += WORD;

    // compression method
    writeOut(ZipShort.getBytes(zipMethod));
    written += SHORT;

    // last mod. time and date
    writeOut(ZipUtil.toDosTime(ze.getTime()));
    written += WORD;

    // CRC
    // compressed length
    // uncompressed length
    writeOut(ZipLong.getBytes(ze.getCrc()));
    if (ze.getCompressedSize() >= ZIP64_MAGIC || ze.getSize() >= ZIP64_MAGIC) {
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
    } else {
      writeOut(ZipLong.getBytes(ze.getCompressedSize()));
      writeOut(ZipLong.getBytes(ze.getSize()));
    }
    // CheckStyle:MagicNumber OFF
    written += 12;
    // CheckStyle:MagicNumber ON

    ByteBuffer name = getName(ze);

    writeOut(ZipShort.getBytes(name.limit()));
    written += SHORT;

    // extra field length
    byte[] extra = ze.getCentralDirectoryExtra();
    writeOut(ZipShort.getBytes(extra.length));
    written += SHORT;

    // file comment length
    String comm = ze.getComment();
    if (comm == null) {
      comm = "";
    }

    ByteBuffer commentB = getEntryEncoding(ze).encode(comm);

    writeOut(ZipShort.getBytes(commentB.limit()));
    written += SHORT;

    // disk number start
    writeOut(ZERO);
    written += SHORT;

    // internal file attributes
    writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
    written += SHORT;

    // external file attributes
    writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
    written += WORD;

    // relative offset of LFH
    writeOut(ZipLong.getBytes(Math.min(lfhOffset, ZIP64_MAGIC)));
    written += WORD;

    // file name
    writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
    written += name.limit();

    // extra field
    writeOut(extra);
    written += extra.length;

    // file comment
    writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position());
    written += commentB.limit();
  }