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.
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.