Search code examples
c++zipzlibunzip

Why does unzip ignore my zip64 end of central directory record?


My goal is create a zip64 supported archive in c++ with only zlib.

The current implementation works without zip64, but when I assign the zip64 EOCD and EOCL they are unused by unzip and 7zip. Though I have verified that my unzip does support zip64.

What could be the cause of unzip (and similairs) ignoring my zip64 EOCD? I believe the corresponding headers are correct?

I am aware that this example is not fully reproducible but the code is too large to include everything. The File class can not be included but it is just a simple wrapper for FILE*, SICE is an alias for static inline constexpr and String is very similair to std::string. All of the code that is not included is thoroughly tested and is not the cause of the issue.

These are the header structs:

// File header.
struct FHeader {
    
    // Default headers.
    SICE uint32_t   signature = 0x04034b50;
    uint16_t        version = 45;
    uint16_t        general_flag = 0;
    uint16_t        compression_method = 8; // deflated.
    uint16_t        mod_time = 0;
    uint16_t        mod_date = 0;
    uint32_t        crc = 0;                    // TO ASSIGN.
    uint32_t        compressed_len = 0xFFFFFFFF;
    uint32_t        uncompressed_len = 0xFFFFFFFF;
    uint16_t        name_len = 0;               // TO ASSIGN.
    uint16_t        extra_field_len = 24 + 4 + 14 + 4;
    
    // File name.
    String          name;                       // TO ASSIGN.
    
    // Zip64 extended info extra field.
    SICE uint16_t   extf_zip64_signature = 0x0001;
    SICE uint16_t   extf_zip64_size = 24;
    uint64_t        zip64_compressed_len = 0;   // TO ASSIGN.
    uint64_t        zip64_uncompressed_len = 0; // TO ASSIGN.
    uint64_t        zip64_offset = 0;           // TO ASSIGN.
    
    // ASi UNIX extra field.
    SICE uint16_t   extf_unix_signature = 0x756e;
    SICE uint16_t   extf_unix_size = 14;
    uint32_t        extf_unix_crc = 0;
    uint16_t        permission = 0777;          // TO ASSIGN.
    uint32_t        sizdev = 0;
    uint16_t        uid = 0;                    // TO ASSIGN.
    uint16_t        gid = 0;                    // TO ASSIGN.
    
    // File data.
    String          data;                       // TO ASSIGN.
    
};

// Central dir header.
struct CDHeader {
    
    // Default headers.
    SICE uint32_t   signature = 0x02014b50;
    uint16_t        made_version = 45;
    uint16_t        version = 45;
    uint16_t        general_flag = 0;
    uint16_t        compression_method = 8; // deflated.
    uint16_t        mod_time = 0;
    uint16_t        mod_date = 0;
    uint32_t        crc = 0;                    // TO ASSIGN.
    uint32_t        compressed_len = 0xFFFFFFFF;
    uint32_t        uncompressed_len = 0xFFFFFFFF;
    uint16_t        name_len = 0;               // TO ASSIGN.
    uint16_t        extra_field_len = 24 + 4 + 14 + 4;
    uint16_t        comment_len = 0;
    uint16_t        disk = 0;
    uint16_t        internal_file_attr = 0;
    uint32_t        external_file_attr = 0;
    uint32_t        offset = 0xFFFFFFFF;
    
    // File name.
    String          name;                       // TO ASSIGN.
    
    // Zip64 extended info extra field.
    SICE uint16_t   extf_zip64_signature = 0x0001;
    SICE uint16_t   extf_zip64_size = 24;
    uint64_t        zip64_compressed_len = 0;   // TO ASSIGN.
    uint64_t        zip64_uncompressed_len = 0; // TO ASSIGN.
    uint64_t        zip64_offset = 0;           // TO ASSIGN.
    
    // ASi UNIX extra field.
    SICE uint16_t   extf_unix_signature = 0x756e;
    SICE uint16_t   extf_unix_size = 14;
    uint32_t        extf_unix_crc = 0;
    uint16_t        permission = 0777;          // TO ASSIGN.
    uint32_t        sizdev = 0;
    uint16_t        uid = 0;                    // TO ASSIGN.
    uint16_t        gid = 0;                    // TO ASSIGN.
    
};

// End of central directory record for ZIP64.
struct EOCD64 {
    SICE uint32_t   signature = 0x06064b50;
    uint64_t        remaining_size = 44;
    uint16_t        made_version = 45;
    uint16_t        version = 45;
    uint32_t        disk = 0;
    uint32_t        start_disk = 0;
    uint64_t        disk_entries = 0;   // TO ASSIGN.
    uint64_t        entries = 0;        // TO ASSIGN.
    uint64_t        size = 0;           // TO ASSIGN.
    uint64_t        offset = 0;         // TO ASSIGN.
};

// End of central directory locator for ZIP64.
struct EOCL64 {
    SICE uint32_t   signature = 0x07064b50;
    uint16_t        disk = 0;
    uint64_t        offset = 0;         // TO ASSIGN.
    uint32_t        disks = 1;
};

// End of central directory record.
struct EOCD {
    SICE uint32_t   signature = 0x06054b50;
    uint16_t        disk = 0xFFFF;
    uint16_t        start_central_disk = 0xFFFF;
    uint16_t        disk_entries = 0xFFFF;
    uint16_t        entries = 0xFFFF;
    uint32_t        size = 0xFFFFFFFF;
    uint32_t        offset = 0xFFFFFFFF;
    uint16_t        comment_len = 0;
};

These are the functions used to write the headers to the archive file:

// Compute CRC-32.
static
uint32_t compute_crc32(const char* data, const ullong& len) {
    uLong crc = crc32(0L, Z_NULL, 0);
    crc = crc32(crc, (uchar*) data, len);
    return (uint32_t) crc;
}
static
uint32_t compute_crc32(
    const uint16_t permission,
    const uint32_t sizdev,
    const uint16_t uid,
    const uint16_t gid
) {
    uLong crc = crc32(0, nullptr, 0);  // Initialize CRC-32 value to 0
    crc = crc32_combine(crc, permission, sizeof(permission));
    crc = crc32_combine(crc, sizdev, sizeof(sizdev));
    crc = crc32_combine(crc, uid, sizeof(uid));
    crc = crc32_combine(crc, gid, sizeof(gid));
    return (uint32_t) crc;
}

// Write local file header for a file entry
void    write_fheader(File& archive, const FHeader& header) const {
    
    // Write header.
    archive.write((char*) &header.signature, sizeof(header.signature));
    archive.write((char*) &header.version, sizeof(header.version));
    archive.write((char*) &header.general_flag, sizeof(header.general_flag));
    archive.write((char*) &header.compression_method, sizeof(header.compression_method));
    archive.write((char*) &header.mod_time, sizeof(header.mod_time));
    archive.write((char*) &header.mod_date, sizeof(header.mod_date));
    archive.write((char*) &header.crc, sizeof(header.crc));
    archive.write((char*) &header.compressed_len, sizeof(header.compressed_len));
    archive.write((char*) &header.uncompressed_len, sizeof(header.uncompressed_len));
    archive.write((char*) &header.name_len, sizeof(header.name_len));
    archive.write((char*) &header.extra_field_len, sizeof(header.extra_field_len));
    
    // File name
    archive.write(header.name.c_str(), header.name.len());
    
    // ASi UNIX extra field.
    archive.write((char*) &header.extf_unix_signature, sizeof(header.extf_unix_signature));
    archive.write((char*) &header.extf_unix_size, sizeof(header.extf_unix_size));
    archive.write((char*) &header.crc, sizeof(header.crc));
    archive.write((char*) &header.permission, sizeof(header.permission));
    archive.write((char*) &header.sizdev, sizeof(header.sizdev));
    archive.write((char*) &header.uid, sizeof(header.uid));
    archive.write((char*) &header.gid, sizeof(header.gid));
    
    // Zip64 extented info.
    // Should be last.
    archive.write((char*) &header.extf_zip64_signature, sizeof(header.extf_zip64_signature));
    archive.write((char*) &header.extf_zip64_size, sizeof(header.extf_zip64_size));
    archive.write((char*) &header.zip64_uncompressed_len, sizeof(header.zip64_uncompressed_len));
    archive.write((char*) &header.zip64_compressed_len, sizeof(header.zip64_compressed_len));
    archive.write((char*) &header.zip64_offset, sizeof(header.zip64_offset));
    
    // File data.
    archive.write(header.data.data(), header.data.len());
    
}

// Write central directory file header for a file entry
void    write_cdheader(File& archive, const CDHeader& header) const {
    
    // Write header.
    archive.write((char*) &header.signature, sizeof(header.signature));
    archive.write((char*) &header.made_version, sizeof(header.made_version));
    archive.write((char*) &header.version, sizeof(header.version));
    archive.write((char*) &header.general_flag, sizeof(header.general_flag));
    archive.write((char*) &header.compression_method, sizeof(header.compression_method));
    archive.write((char*) &header.mod_time, sizeof(header.mod_time));
    archive.write((char*) &header.mod_date, sizeof(header.mod_date));
    archive.write((char*) &header.crc, sizeof(header.crc));
    archive.write((char*) &header.compressed_len, sizeof(header.compressed_len));
    archive.write((char*) &header.uncompressed_len, sizeof(header.uncompressed_len));
    archive.write((char*) &header.name_len, sizeof(header.name_len));
    archive.write((char*) &header.extra_field_len, sizeof(header.extra_field_len));
    archive.write((char*) &header.comment_len, sizeof(header.comment_len));
    archive.write((char*) &header.disk, sizeof(header.disk));
    archive.write((char*) &header.internal_file_attr, sizeof(header.internal_file_attr));
    archive.write((char*) &header.external_file_attr, sizeof(header.external_file_attr));
    archive.write((char*) &header.offset, sizeof(header.offset));

    // File name
    archive.write(header.name.c_str(), header.name.len());
    
    // ASi UNIX extra field.
    archive.write((char*) &header.extf_unix_signature, sizeof(header.extf_unix_signature));
    archive.write((char*) &header.extf_unix_size, sizeof(header.extf_unix_size));
    archive.write((char*) &header.crc, sizeof(header.crc));
    archive.write((char*) &header.permission, sizeof(header.permission));
    archive.write((char*) &header.sizdev, sizeof(header.sizdev));
    archive.write((char*) &header.uid, sizeof(header.uid));
    archive.write((char*) &header.gid, sizeof(header.gid));
    
    // Zip64 extented info.
    // Should be last.
    archive.write((char*) &header.extf_zip64_signature, sizeof(header.extf_zip64_signature));
    archive.write((char*) &header.extf_zip64_size, sizeof(header.extf_zip64_size));
    archive.write((char*) &header.zip64_uncompressed_len, sizeof(header.zip64_uncompressed_len));
    archive.write((char*) &header.zip64_compressed_len, sizeof(header.zip64_compressed_len));
    archive.write((char*) &header.zip64_offset, sizeof(header.zip64_offset));
    
}

// Write end of central directory record
void    write_eocd(File& archive, const EOCD& header) const {
    
    // Write header.
    archive.write((char*) &header.signature, sizeof(header.signature));
    archive.write((char*) &header.disk, sizeof(header.disk));
    archive.write((char*) &header.start_central_disk, sizeof(header.start_central_disk));
    archive.write((char*) &header.disk_entries, sizeof(header.disk_entries));
    archive.write((char*) &header.entries, sizeof(header.entries));
    archive.write((char*) &header.size, sizeof(header.size));
    archive.write((char*) &header.offset, sizeof(header.offset));
    archive.write((char*) &header.comment_len, sizeof(header.comment_len));
    
}

// Write end of central directory recod.
void    write_eocd64(File& archive, const EOCD64& header) const {
    archive.write((char*) &header.signature, sizeof(header.signature));
    archive.write((char*) &header.remaining_size, sizeof(header.remaining_size));
    archive.write((char*) &header.made_version, sizeof(header.made_version));
    archive.write((char*) &header.version, sizeof(header.version));
    archive.write((char*) &header.disk, sizeof(header.disk));
    archive.write((char*) &header.start_disk, sizeof(header.start_disk));
    archive.write((char*) &header.disk_entries, sizeof(header.disk_entries));
    archive.write((char*) &header.entries, sizeof(header.entries));
    archive.write((char*) &header.size, sizeof(header.size));
    archive.write((char*) &header.offset, sizeof(header.offset));
}

// Write end of central directory locator.
void    write_eocl64(File& archive, const EOCL64& header) const {
    archive.write((char*) &header.signature, sizeof(header.signature));
    archive.write((char*) &header.disk, sizeof(header.disk));
    archive.write((char*) &header.offset, sizeof(header.offset));
    archive.write((char*) &header.disks, sizeof(header.disks));
}

And this is how i write the headers:

...

// Write files.
for (auto& index: file_headers.indexes()) {
    file_headers[index].zip64_offset = ftell(output.file());
    central_dir_headers[index].zip64_offset = file_headers[index].zip64_offset;
    // central_dir_headers[index].offset = ftell(output.file());
    write_fheader(output, file_headers[index]);
}

// Write central dir.
ullong cd_offset = ftell(output.file());
for (auto& header: central_dir_headers) {
    write_cdheader(output, header);
}
ullong eocd64_offset = ftell(output.file());

// Write zip64 EOCD.
eocd64.offset = cd_offset;
eocd64.disk_entries = file_headers.len();
eocd64.entries = file_headers.len();
eocd64.size = eocd64_offset - cd_offset;
write_eocd64(output, eocd64);

// Write zip64 EOCL.
eocl64.offset = eocd64_offset;
write_eocl64(output, eocl64);

// Write EOCD.
write_eocd(output, eocd);

But when trying to extract the archive with unzip these error logs are encountered.

$ unzip -t ../archive.zip                          
Archive:  ../archive.zip
warning [../archive.zip]:  zipfile claims to be last disk of a multi-part archive;
  attempting to process anyway, assuming all parts have been concatenated
  together in order.  Expect "errors" and warnings...true multi-part support
  doesn't exist yet (coming soon).
error [../archive.zip]:  missing 8589933224 bytes in zipfile
  (attempting to process anyway)
error [../archive.zip]:  attempt to seek before beginning of zipfile
  (please check that you have transferred or created the zipfile in the
  appropriate BINARY mode and that you have compiled UnZip properly)

And zipinfo:

$ zipinfo -v ../archive.zip
Archive:  ../archive.zip
There is no zipfile comment.

End-of-central-directory record:
-------------------------------

  Zip archive file size:                      1388 (000000000000056Ch)
  Actual end-cent-dir record offset:          1366 (0000000000000556h)
  Expected end-cent-dir record offset:  8589934590 (00000001FFFFFFFEh)
  (based on the length of the central directory and its expected offset)

  This zipfile constitutes the sole disk of a single-part archive; its
  central directory contains 65535 entries.
  The central directory is 4294967295 (00000000FFFFFFFFh) bytes long,
  and its (expected) offset in bytes from the beginning of the zipfile
  is 4294967295 (00000000FFFFFFFFh).

error [../archive.zip]:  missing 8589933224 bytes in zipfile
  (attempting to process anyway)
error [../archive.zip]:  attempt to seek before beginning of zipfile
  (please check that you have transferred or created the zipfile in the
  appropriate BINARY mode and that you have compiled UnZip properly)

I really appreciate any help given!

Edit

An example archive file created by this code can be found at: https://www.dropbox.com/s/14mu7q6hpi93cyc/dir.zip?dl=0

Edit The ignoring of the zip64 is resolved but the ASi UNIX extra field is also ignored.

The uint32_t permission is initialized with.

struct stat stat_info;
if (::lstat(path.c_str(), &stat_info) == -1) {
    ...
}
uint16_t permission = stat_info.st_mode;
// uint16_t permission = stat_info.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); // does also not work.

This is the code to create the ASi UNIX extra field. Structure:

// Central dir header.
struct ... {
    ...
    
    // ASi UNIX extra field.
    SICE uint16_t   extf_unix_signature = 0x756e;
    SICE uint16_t   extf_unix_size = 14;
    uint32_t        extf_unix_crc = 0;          // TO ASSIGN
    uint16_t        permission = 0777;          // TO ASSIGN.
    uint32_t        sizdev = 0;
    uint16_t        uid = 0;                    // TO ASSIGN.
    uint16_t        gid = 0;                    // TO ASSIGN.
    
};

CRC32:

static
uint32_t compute_crc32(
    const uint16_t permission,
    const uint32_t sizdev,
    const uint16_t uid,
    const uint16_t gid
) {
    uLong crc = crc32(0, nullptr, 0);  // Initialize CRC-32 value to 0
    crc = crc32_combine(crc, permission, sizeof(permission));
    crc = crc32_combine(crc, sizdev, sizeof(sizdev));
    crc = crc32_combine(crc, uid, sizeof(uid));
    crc = crc32_combine(crc, gid, sizeof(gid));
    return (uint32_t) crc;
}

Writing the header:

// ASi UNIX extra field.
archive.write((char*) &header.extf_unix_signature, sizeof(header.extf_unix_signature));
archive.write((char*) &header.extf_unix_size, sizeof(header.extf_unix_size));
archive.write((char*) &header.extf_unix_crc, sizeof(header.extf_unix_crc,));
archive.write((char*) &header.permission, sizeof(header.permission));
archive.write((char*) &header.sizdev, sizeof(header.sizdev));
archive.write((char*) &header.uid, sizeof(header.uid));
archive.write((char*) &header.gid, sizeof(header.gid));

All variables are initialized but not posted here.


Solution

  • Below is a dump of your zip file created by running zipdetails. Note I've had to enable a special scanning mode to get it to find the zip64 records.

    Note the crazy values for all the fields in the ZIP64 END CENTRAL DIR LOCATOR. Also the last two bytes (for the Total no of Disks) at offset 0546 overlap with the END CENTRAL HEADER

    $ zipdetails  -v archive.zip
    Cannot find signature for 'Zip64 end of central directory locator': 0x07064b50
    Try running with --scan option.
    
    $ zipdetails --scan -v archive.zip
    
    0000 0004 50 4B 03 04 LOCAL HEADER #1       04034B50
    0004 0001 2D          Extract Zip Spec      2D '4.5'
    0005 0001 00          Extract OS            00 'MS-DOS'
    0006 0002 00 00       General Purpose Flag  0000
                          [Bits 1-2]            0 'Normal Compression'
    0008 0002 08 00       Compression Method    0008 'Deflated'
    000A 0004 00 00 00 00 Last Mod Time         00000000 'No Date/Time'
    000E 0004 02 F5 0C 44 CRC                   440CF502
    0012 0004 FF FF FF FF Compressed Length     FFFFFFFF
    0016 0004 FF FF FF FF Uncompressed Length   FFFFFFFF
    001A 0002 07 00       Filename Length       0007
    001C 0002 2E 00       Extra Length          002E
    001E 0007 69 6E 73 74 Filename              'install'
              61 6C 6C
    0025 0002 6E 75       Extra ID #0001        756E 'nu: ASi Unix'
    0027 0002 0E 00         Length              000E
    0029 000E 02 F5 0C 44   Extra Payload       02 F5 0C 44 ED 01 00 00 00 00 F5 01
              ED 01 00 00                       00 00
              00 00 F5 01
              00 00
    0037 0002 01 00       Extra ID #0002        0001 'ZIP64'
    0039 0002 18 00         Length              0018
    003B 0008 DA 04 00 00   Uncompressed Size   00000000000004DA
              00 00 00 00
    0043 0008 A2 01 00 00   Compressed Size     00000000000001A2
              00 00 00 00
    004B 01A2 ...         PAYLOAD
    
    01F5 0004 50 4B 03 04 LOCAL HEADER #2       04034B50
    01F9 0001 2D          Extract Zip Spec      2D '4.5'
    01FA 0001 00          Extract OS            00 'MS-DOS'
    01FB 0002 00 00       General Purpose Flag  0000
                          [Bits 1-2]            0 'Normal Compression'
    01FD 0002 08 00       Compression Method    0008 'Deflated'
    01FF 0004 00 00 00 00 Last Mod Time         00000000 'No Date/Time'
    0203 0004 1A D9 B2 1D CRC                   1DB2D91A
    0207 0004 FF FF FF FF Compressed Length     FFFFFFFF
    020B 0004 FF FF FF FF Uncompressed Length   FFFFFFFF
    020F 0002 09 00       Filename Length       0009
    0211 0002 2E 00       Extra Length          002E
    0213 0009 52 45 41 44 Filename              'README.md'
              4D 45 2E 6D
              64
    021C 0002 6E 75       Extra ID #0001        756E 'nu: ASi Unix'
    021E 0002 0E 00         Length              000E
    0220 000E 1A D9 B2 1D   Extra Payload       1A D9 B2 1D ED 01 00 00 00 00 F5 01
              ED 01 00 00                       00 00
              00 00 F5 01
              00 00
    022E 0002 01 00       Extra ID #0002        0001 'ZIP64'
    0230 0002 18 00         Length              0018
    0232 0008 95 03 00 00   Uncompressed Size   0000000000000395
              00 00 00 00
    023A 0008 FA 01 00 00   Compressed Size     00000000000001FA
              00 00 00 00
    0242 01FA ...         PAYLOAD
    
    0444 0004 50 4B 01 02 CENTRAL HEADER #1     02014B50
    0448 0001 2D          Created Zip Spec      2D '4.5'
    0449 0001 00          Created OS            00 'MS-DOS'
    044A 0001 2D          Extract Zip Spec      2D '4.5'
    044B 0001 00          Extract OS            00 'MS-DOS'
    044C 0002 00 00       General Purpose Flag  0000
                          [Bits 1-2]            0 'Normal Compression'
    044E 0002 08 00       Compression Method    0008 'Deflated'
    0450 0004 00 00 00 00 Last Mod Time         00000000 'No Date/Time'
    0454 0004 02 F5 0C 44 CRC                   440CF502
    0458 0004 FF FF FF FF Compressed Length     FFFFFFFF
    045C 0004 FF FF FF FF Uncompressed Length   FFFFFFFF
    0460 0002 07 00       Filename Length       0007
    0462 0002 2E 00       Extra Length          002E
    0464 0002 00 00       Comment Length        0000
    0466 0002 00 00       Disk Start            0000
    0468 0002 00 00       Int File Attributes   0000
                          [Bit 0]               0 'Binary Data'
    046A 0004 00 00 00 00 Ext File Attributes   00000000
    046E 0004 FF FF FF FF Local Header Offset   FFFFFFFF
    0472 0007 69 6E 73 74 Filename              'install'
              61 6C 6C
    0479 0002 6E 75       Extra ID #0001        756E 'nu: ASi Unix'
    047B 0002 0E 00         Length              000E
    047D 000E 02 F5 0C 44   Extra Payload       02 F5 0C 44 ED 01 00 00 00 00 F5 01
              ED 01 00 00                       00 00
              00 00 F5 01
              00 00
    048B 0002 01 00       Extra ID #0002        0001 'ZIP64'
    048D 0002 18 00         Length              0018
    048F 0008 DA 04 00 00   Uncompressed Size   00000000000004DA
              00 00 00 00
    0497 0008 A2 01 00 00   Compressed Size     00000000000001A2
              00 00 00 00
    049F 0008 00 00 00 00   Offset to Local Dir 0000000000000000
              00 00 00 00
    
    04A7 0004 50 4B 01 02 CENTRAL HEADER #2     02014B50
    04AB 0001 2D          Created Zip Spec      2D '4.5'
    04AC 0001 00          Created OS            00 'MS-DOS'
    04AD 0001 2D          Extract Zip Spec      2D '4.5'
    04AE 0001 00          Extract OS            00 'MS-DOS'
    04AF 0002 00 00       General Purpose Flag  0000
                          [Bits 1-2]            0 'Normal Compression'
    04B1 0002 08 00       Compression Method    0008 'Deflated'
    04B3 0004 00 00 00 00 Last Mod Time         00000000 'No Date/Time'
    04B7 0004 1A D9 B2 1D CRC                   1DB2D91A
    04BB 0004 FF FF FF FF Compressed Length     FFFFFFFF
    04BF 0004 FF FF FF FF Uncompressed Length   FFFFFFFF
    04C3 0002 09 00       Filename Length       0009
    04C5 0002 2E 00       Extra Length          002E
    04C7 0002 00 00       Comment Length        0000
    04C9 0002 00 00       Disk Start            0000
    04CB 0002 00 00       Int File Attributes   0000
                          [Bit 0]               0 'Binary Data'
    04CD 0004 00 00 00 00 Ext File Attributes   00000000
    04D1 0004 FF FF FF FF Local Header Offset   FFFFFFFF
    04D5 0009 52 45 41 44 Filename              'README.md'
              4D 45 2E 6D
              64
    04DE 0002 6E 75       Extra ID #0001        756E 'nu: ASi Unix'
    04E0 0002 0E 00         Length              000E
    04E2 000E 1A D9 B2 1D   Extra Payload       1A D9 B2 1D ED 01 00 00 00 00 F5 01
              ED 01 00 00                       00 00
              00 00 F5 01
              00 00
    04F0 0002 01 00       Extra ID #0002        0001 'ZIP64'
    04F2 0002 18 00         Length              0018
    04F4 0008 95 03 00 00   Uncompressed Size   0000000000000395
              00 00 00 00
    04FC 0008 FA 01 00 00   Compressed Size     00000000000001FA
              00 00 00 00
    0504 0008 F5 01 00 00   Offset to Local Dir 00000000000001F5
              00 00 00 00
    
    050C 0004 50 4B 06 06 ZIP64 END CENTRAL DIR 06064B50
                          RECORD
    0510 0008 2C 00 00 00 Size of record        000000000000002C
              00 00 00 00
    0518 0001 2D          Created Zip Spec      2D '4.5'
    0519 0001 00          Created OS            00 'MS-DOS'
    051A 0001 2D          Extract Zip Spec      2D '4.5'
    051B 0001 00          Extract OS            00 'MS-DOS'
    051C 0004 00 00 00 00 Number of this disk   00000000
    0520 0004 00 00 00 00 Central Dir Disk no   00000000
    0524 0008 02 00 00 00 Entries in this disk  0000000000000002
              00 00 00 00
    052C 0008 02 00 00 00 Total Entries         0000000000000002
              00 00 00 00
    0534 0008 C8 00 00 00 Size of Central Dir   00000000000000C8
              00 00 00 00
    053C 0008 44 04 00 00 Offset to Central dir 0000000000000444
              00 00 00 00
    
    0544 0004 50 4B 06 07 ZIP64 END CENTRAL DIR 07064B50
                          LOCATOR
    0548 0004 00 00 0C 05 Central Dir Disk no   050C0000
    054C 0008 00 00 00 00 Offset to Central dir 0001000000000000
              00 00 01 00
    0554 0004 00 00 50 4B Total no of Disks     4B500000
    
    0556 0004 50 4B 05 06 END CENTRAL HEADER    06054B50
    055A 0002 FF FF       Number of this disk   FFFF
    055C 0002 FF FF       Central Dir Disk no   FFFF
    055E 0002 FF FF       Entries in this disk  FFFF
    0560 0002 FF FF       Total Entries         FFFF
    0562 0004 FF FF FF FF Size of Central Dir   FFFFFFFF
    0566 0004 FF FF FF FF Offset to Central Dir FFFFFFFF
    056A 0002 00 00       Comment Length        0000
    Done
    
    

    Looking at your code, the disk should be a 32-bit value

    // End of central directory locator for ZIP64.
    struct EOCL64 {
        SICE uint32_t   signature = 0x07064b50;
        uint16_t        disk = 0;
        uint64_t        offset = 0;         // TO ASSIGN.
        uint32_t        disks = 1;
    };
    

    [EDIT]

    Next thing to note is the gaps between entries. Local Header 1 ends at 0x4A + 0x1A2 payload bytes = 0x1ED. But Local header 2 starts at 0x1F5. You have a gap with nothing in it.

    Same goes for local header 2.

    The zip spec doesn't explicitly say you shouldn't have gaps in the zip file, but is is implied in section 4.3.6 Overall .ZIP file format. For maximum interoperability with unzip clients I advise against leaving spaces between entries.