Current section

Files

Jump to
packmatic lib packmatic field local file_header.ex
Raw

lib/packmatic/field/local/file_header.ex

defmodule Packmatic.Field.Local.FileHeader do
@moduledoc """
Represents the Local File Header, which is emitted before the content of each file is
incorporated into the Zip stream.
## Structure
### Local File Header
Size | Content
-------- | -
4 bytes | Signature
2 bytes | Version needed to extract
2 bytes | General Purpose Flag
2 bytes | Compression Method (0 = No Compression; 8 = Deflated)
2 bytes | Modification Time (DOS Format)
2 bytes | Modification Date (DOS Format)
4 bytes | Checksum (CRC-32; 0 since Data Descriptor is used)
4 bytes | Compressed Size (Bytes; 0 since Data Descriptor is used)
4 bytes | Original Size (Bytes; 0 since Data Descriptor is used)
2 bytes | File Path Length (Bytes)
2 bytes | Extra Fields Length (Bytes)
Variable | File Path
Variable | Extra Fields
#### Notes
1. The General Purpose Flag has the following bits set.
- Bit 3: Indicating a Streaming Archive; Data Descriptor is used, and the Local File Header
has no Size or CRC information.
- Bit 11: Language encoding flag, indicating that the Filename and Comment are both already
in UTF-8. As per APPNOTE, the presence of this flag obviates the need to emit a separate
Info-ZIP Unicode Path Extra Field.
2. The Checksum, Compressed Size and Original Size fields are set to 0, since when the Local
File Header is written, no further data has been read and so this information is not
available. When the file has been read fully, a Data Descriptor will be written, which
contains relevant information.
3. The following Extra Field is emitted:
- Extended Timestamp, see `Packmatic.Field.Shared.ExtendedTimestamp`
"""
@type t :: %__MODULE__{path: Path.t(), timestamp: DateTime.t()}
defstruct path: nil, timestamp: nil
end
defimpl Packmatic.Field, for: Packmatic.Field.Local.FileHeader do
import Packmatic.Field.Helpers
alias Packmatic.Field
def encode(%{timestamp: %DateTime{time_zone: "Etc/UTC"}} = target) do
entry_timestamp = encode_timestamp(target)
entry_extra_timestamp = encode_extended_timestamp(target)
entry_extras = [entry_extra_timestamp]
[
<<0x50, 0x4B, 0x03, 0x04>>,
encode_16(45),
encode_16(2056),
encode_16(8),
entry_timestamp,
encode_32(0),
encode_32(0),
encode_32(0),
encode_16(:erlang.iolist_size(target.path)),
encode_16(:erlang.iolist_size(entry_extras)),
target.path,
entry_extras
]
end
defp encode_timestamp(target) do
Field.encode(%Field.Shared.Timestamp{
timestamp: target.timestamp
})
end
defp encode_extended_timestamp(target) do
Field.encode(%Field.Shared.ExtendedTimestamp{
timestamp: target.timestamp
})
end
end