const std = @import("std"); const testing = std.testing; const ZipFile = struct { // [local file header 1] // [encryption header 1] // [file data 1] // [data descriptor 1] // . // . // . // [local file header n] // [encryption header n] // [file data n] // [data descriptor n] // [archive decryption header] // [archive extra data record] // [central directory header 1] // . // . // . // [central directory header n] // [zip64 end of central directory record] // [zip64 end of central directory locator] // [end of central directory record] }; const LocalFileHeader = packed struct { const GPBF = packed struct(u16) { encrypted: bool = false, }; const SIG: u32 = 0x04034b50; sig: u32 = SIG, // version needed to extract 2 bytes general_purpose_bit_flag: GPBF, // compression method 2 bytes // last mod file time 2 bytes // last mod file date 2 bytes // crc-32 4 bytes // compressed size 4 bytes // uncompressed size 4 bytes // file name length 2 bytes // extra field length 2 bytes // file name (variable size) // extra field (variable size) }; const DataDescriptor = struct { const SIG: u32 = 0x08074b50; sig: u32 = SIG, // crc-32 4 bytes // compressed size 4 bytes // uncompressed size 4 bytes }; const ArchiveExtraDataRecord = struct { const SIG: u32 = 0x08064b50; sig: u32 = SIG, // extra field length 4 bytes // extra field data (variable size) }; const CentralDirectoryHeader = packed struct { const SIG: u32 = 0x02014b50; // central file header signature 4 bytes () sig: u32 = SIG, // version made by 2 bytes version_made_by: u16, // version needed to extract 2 bytes version_needed_to_extract: u16, // general purpose bit flag 2 bytes general_purpose_bit_flag: u16, // compression method 2 bytes compression_method: u16, // last mod file time 2 bytes last_mod_file_time: u16, // last mod file date 2 bytes last_mod_file_date: u16, // crc-32 4 bytes crc32: u32, // compressed size 4 bytes compressed_size: u32, // uncompressed size 4 bytes uncompressed_size: u32, // file name length 2 bytes file_name_length: u16, // extra field length 2 bytes extra_field_length: u16, // file comment length 2 bytes file_comment_length: u16, // disk number start 2 bytes disk_number_start: u16, // internal file attributes 2 bytes internal_file_attributes: u16, // external file attributes 4 bytes external_file_attributes: u32, // relative offset of local header 4 bytes relative_offset_of_local_header: u16, // file name (variable size) // extra field (variable size) // file comment (variable size) }; const DigitalSignature = struct { const SIG: u32 = 0x05054b50; sig: u32 = SIG, // size of data 2 bytes // signature data (variable size) }; const Zip64EndOfCentralDirectoryRecord = struct { const SIG: u32 = 0x06064b50; sig: u32 = SIG, // size of zip64 end of central // directory record 8 bytes // version made by 2 bytes // version needed to extract 2 bytes // number of this disk 4 bytes // number of the disk with the // start of the central directory 4 bytes // total number of entries in the // central directory on this disk 8 bytes // total number of entries in the // central directory 8 bytes // size of the central directory 8 bytes // offset of start of central // directory with respect to // the starting disk number 8 bytes // zip64 extensible data sector (variable size) }; const Zip64EndOfCentralDirectoryLocator = struct { const SIG: u32 = 0x07064b50; sig: u32 = SIG, // number of the disk with the // start of the zip64 end of // central directory 4 bytes // relative offset of the zip64 // end of central directory record 8 bytes // total number of disks 4 bytes }; const EndOfCentralDirectoryRecord = packed struct { const SIG: u32 = 0x06054b50; // end of central dir signature 4 bytes (0x06054b50) sig: u32 = SIG, // number of this disk 2 bytes disk_number_this: u16, // number of the disk with the // start of the central directory 2 bytes disk_number_central_dir_start: u16, // total number of entries in the // central directory on this disk 2 bytes total_central_dir_entries_on_this_disk: u16, // total number of entries in // the central directory 2 bytes total_central_dir_entries: u16, // size of the central directory 4 bytes size_of_central_dir: u32, // offset of start of central // directory with respect to // the starting disk number 4 bytes central_dir_offset: u32, // .ZIP file comment length 2 bytes comment_length: u16, // .ZIP file comment (variable size) // comment: [*]u8, // fn comment_slice(self: EndOfCentralDirectoryRecord) []u8 { // return self.comment[0..self.comment_length]; // } }; test "foo" { var mapped_mem: ?[]align(std.mem.page_size) u8 = null; { var file = try std.fs.cwd().openFile("src/hello.zip", .{}); errdefer file.close(); const file_len = std.math.cast(usize, try file.getEndPos()) orelse std.math.maxInt(usize); mapped_mem = try std.os.mmap( null, file_len, std.os.PROT.READ, std.os.MAP.SHARED, file.handle, 0, ); file.close(); } var mm = mapped_mem orelse return error.MMapFailed; defer std.os.munmap(mm); const cdr_search_start = if (mm.len < 64_000) 0 else mm.len - 64_000; const needle: [4]u8 = @bitCast(CentralDirectoryHeader.SIG); const eocdr_start = std.mem.indexOf(u8, mm[cdr_search_start..], &needle) orelse return error.EocdrNotFound; const eocdr: *EndOfCentralDirectoryRecord = @ptrCast(@alignCast(mm[eocdr_start .. eocdr_start + @sizeOf(EndOfCentralDirectoryRecord)])); std.log.err("needle: {} eocdr_start {} comment_length {}", .{ std.fmt.fmtSliceHexLower(&needle), eocdr_start, eocdr.comment_length }); const hh_p: *u32 = @ptrCast(mm[0..4].ptr); const hh = hh_p.*; try std.testing.expectEqual(LocalFileHeader.SIG, hh); // std.log.err("mapped_mem len {} first 5 bytes {}", .{mm.len, std.fmt.fmtSliceHexUpper(mm[0..5])}); }