Method: ZipTricks::ZipWriter#write_central_directory_file_header

Defined in:
lib/zip_tricks/zip_writer.rb

#write_central_directory_file_header(io:, local_file_header_location:, gp_flags:, storage_mode:, compressed_size:, uncompressed_size:, mtime:, crc32:, filename:) ⇒ void

This method returns an undefined value.

Writes the file header for the central directory, for a particular file in the archive. When writing out this data, ensure that the CRC32 and both sizes (compressed/uncompressed) are correct for the entry in question.

Parameters:

  • io (#<<)

    the buffer to write the local file header to

  • filename (String)

    the name of the file in the archive

  • compressed_size (Fixnum)

    The size of the compressed (or stored) data - how much space it uses in the ZIP

  • uncompressed_size (Fixnum)

    The size of the file once extracted

  • crc32 (Fixnum)

    The CRC32 checksum of the file

  • mtime (Time)

    the modification time to be recorded in the ZIP

  • gp_flags (Fixnum)

    bit-packed general purpose flags



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/zip_tricks/zip_writer.rb', line 140

def write_central_directory_file_header(io:,
                                        local_file_header_location:,
                                        gp_flags:,
                                        storage_mode:,
                                        compressed_size:,
                                        uncompressed_size:,
                                        mtime:,
                                        crc32:,
                                        filename:)
  # At this point if the header begins somewhere beyound 0xFFFFFFFF we _have_ to record the offset
  # of the local file header as a zip64 extra field, so we give up, give in, you loose, love will always win...
  add_zip64 = (local_file_header_location > FOUR_BYTE_MAX_UINT) ||
              (compressed_size > FOUR_BYTE_MAX_UINT) || (uncompressed_size > FOUR_BYTE_MAX_UINT)

  io << [0x02014b50].pack(C_UINT4)                        # central file header signature   4 bytes  (0x02014b50)
  io << MADE_BY_SIGNATURE                             # version made by                 2 bytes
  io << if add_zip64
    [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2) # version needed to extract       2 bytes
  else
    [VERSION_NEEDED_TO_EXTRACT].pack(C_UINT2)       # version needed to extract       2 bytes
  end

  io << [gp_flags].pack(C_UINT2)                          # general purpose bit flag        2 bytes
  io << [storage_mode].pack(C_UINT2)                      # compression method              2 bytes
  io << [to_binary_dos_time(mtime)].pack(C_UINT2)         # last mod file time              2 bytes
  io << [to_binary_dos_date(mtime)].pack(C_UINT2)         # last mod file date              2 bytes
  io << [crc32].pack(C_UINT4)                             # crc-32                          4 bytes

  if add_zip64
    io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)              # compressed size              4 bytes
    io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)              # uncompressed size            4 bytes
  else
    io << [compressed_size].pack(C_UINT4)                 # compressed size              4 bytes
    io << [uncompressed_size].pack(C_UINT4)               # uncompressed size            4 bytes
  end

  # Filename should not be longer than 0xFFFF otherwise this wont fit here
  io << [filename.bytesize].pack(C_UINT2)                 # file name length                2 bytes

  extra_fields = StringIO.new
  if add_zip64
    extra_fields << zip_64_extra_for_central_directory_file_header(local_file_header_location: local_file_header_location,
                                                                   compressed_size: compressed_size,
                                                                   uncompressed_size: uncompressed_size)
  end
  extra_fields << timestamp_extra_for_central_directory_entry(mtime)

  io << [extra_fields.size].pack(C_UINT2)                 # extra field length              2 bytes

  io << [0].pack(C_UINT2)                                 # file comment length             2 bytes

  # For The Unarchiver < 3.11.1 this field has to be set to the overflow value if zip64 is used
  # because otherwise it does not properly advance the pointer when reading the Zip64 extra field
  # https://bitbucket.org/WAHa_06x36/theunarchiver/pull-requests/2/bug-fix-for-zip64-extra-field-parser/diff
  io << if add_zip64                                        # disk number start               2 bytes
    [TWO_BYTE_MAX_UINT].pack(C_UINT2)
  else
    [0].pack(C_UINT2)
  end
  io << [0].pack(C_UINT2)                                # internal file attributes        2 bytes

  # Because the add_empty_directory method will create a directory with a trailing "/",
  # this check can be used to assign proper permissions to the created directory.
  io << if filename.end_with?('/')
    [EMPTY_DIRECTORY_EXTERNAL_ATTRS].pack(C_UINT4)
  else
    [DEFAULT_EXTERNAL_ATTRS].pack(C_UINT4)           # external file attributes        4 bytes
  end

  io << if add_zip64                                 # relative offset of local header 4 bytes
    [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
  else
    [local_file_header_location].pack(C_UINT4)
  end

  io << filename                                     # file name (variable size)
  io << extra_fields.string                          # extra field (variable size)
  # (empty)                                          # file comment (variable size)
end