ZIP書庫ファイル フォーマット

書庫(アーカイブ)とは、たくさんのファイルをまとめて1つのファイルとして扱えるようにした物のことで、たとえば写真(ファイル)をアルバム(写真より大きな単位のファイル)に収納したような状態のことです。 関連するファイルを書庫にすることで管理を容易にし紛失等のリスクを低減することができます。
ZIP形式は世界中で使用されている最もポピュラーなフォーマットで、各種のデータ圧縮・パスワード保護・誤り検出などの機能も備えています。 また、ファイル展開器(解凍ソフト)に多様な情報を提供しハードウェアやOS間を越えて異なるシステム上でのデータ授受を容易にする側面も持っています。
ファイル管理にZIP形式を使用しているアプリもあります。『○ffice』『○ava』『○か。』といったアプリでは、ひとつの保存ファイルの中に複数のデータが格納されています。このファイルに独自の拡張子を用いて運用することがあります。
メニュー(ページ内リンク)
このページは仕様書(2007年9月28日付Version:6.3.2)をもとに、広く利用されるZIP(ver.2程)の範囲で記述しています。
表記ルールはHSPというスクリプト言語を基準にし、仕様書および他の解説サイト様にはない用語・表現・矛盾・罠・勘違い・嘘・未検証項目・憶測を含んでいます。
その仕様書は古いです
どうやらこのページ作成時は5年ぶりのモデルチェンジの直前だったようです。ですのでこのページには最新の仕様が十分に反映されていない可能性があります。 と言っても、もとより大した(ry

全体の構造

一般的なZIPファイルは大きく3つのエリアに分けることができます。
エリア要素シグネチャ
格納エリア File 1 Local file header $04034B50
File data(なし)
File 2 〜(格納するファイルの数だけ繰り返す)
情報エリア File 1 Central directory header $02014B50
File 2 〜(格納するファイルの数だけ繰り返す)
ZIP情報エリア - End of central directory record $06054B50
Local file header
ファイルを展開するのに必要なファイル名やタイムスタンプなどの情報が格納されます。
File data
ファイルの実内容が格納されます。この内容は圧縮やパスワードで保護されることがあります。
Central directory header
Local file header+αの情報が格納されますが省略や矛盾はできません。
End of central directory record
ZIPファイルの末尾に1つだけあり、そのZIPファイルの基礎情報が書き込まれています。
Data descriptor
ファイル出力が順方向に限定されるシステムで生じる問題を解決するため File data の直後に付加される場合があります。
数値情報を保持するフィールドには『符号なし整数値』が格納されます。ちなみにあたしはリドルエイリアンですの値はリトルエンディアンですので取得には wpeek や lpeek 関数の使用がおすすめです。
文字列情報を保持するフィールドに格納される文字列は末尾の終端コード(null)を含みません。また、文字数のフィールドも同様に null を含まないバイト数を格納します。

Local file header

ファイルの外枠部分の情報を格納します。Central directory header との重複がかなりあります。
Sizeフィールド
4 local file header signature
Local file header であることを示す固定値
$04034B50 = $50, $4B, $03, $04
2 version needed to extract
展開に必要なZIPのバージョン補足
10 (ver.1.0) ⇒無圧縮ファイル
20 (ver.2.0) ⇒フォルダ、デフレート、パスワード保護
45 (ver.4.5) ⇒ZIP64
2 general purpose bit flag
オプションフラグ
ビットフラグ
%0000000000000001 (パスワード保護)
%0000000000001000 (Data descriptor を使用)
%0000100000000000 (ファイル名やコメントが UTF-8文字)
2 compression method
ファイル圧縮に用いたアルゴリズム補足
0 (無圧縮ファイル)
8 (デフレート形式)
2 last mod file time
タイムスタンプ(時刻)
ビット割り当て
%1111100000000000 = 0〜23 [時]
%0000011111100000 = 0〜59 [分]
%0000000000011111 = 0〜29 [×2秒] ⇒1で2秒分を表す
2 last mod file date
タイムスタンプ(日付)
ビット割り当て
%1111111000000000 = 0〜 [+1980年] ⇒西暦1980年からの経過年
%0000000111100000 = 1〜12 [月]
%0000000000011111 = 1〜31 [日]
4 crc-32
ファイルのデータから算出した CRC-32 の値補足
n (CRC-32)
0 (Data descriptor 使用の場合)
4 compressed size
圧縮後のデータ量、File data のサイズ
無圧縮なら uncompressed size フィールドと同値
n [byte]
0 [byte] (データ無し) ⇒フォルダ、空ファイルなど
0 (Data descriptor 使用の場合)
$FFFFFFFF (ZIP64)
4 uncompressed size
圧縮前のデータ量、つまりファイルサイズ
n [byte]
0 [byte] (データ無し) ⇒フォルダ、空ファイルなど
0 (Data descriptor 使用の場合)
$FFFFFFFF (ZIP64)
2 file name length
file name フィールドのサイズ
n [byte]
0 [byte] (名前なし) ⇒標準入力からの流入
2 extra field length
extra field フィールドのサイズ
n [byte]
0 [byte] (extra field を使用しない)
s file name
ファイル名を格納する領域
"ファイル名"
※ドライブ・デバイスレター・先頭のスラッシュは無し
※フォルダ区切りは普通のスラッシュ"/"
s extra field
拡張データを格納する領域補足
任意データ

File data

ファイルの実データを格納します。
このデータは圧縮することができます。ZIPというとファイル圧縮をイメージする人も多いですが、圧縮は必須ではありません。
また、general purpose bit flag フィールドの当該ビットがOnの場合、このデータはパスワード保護されていることになります。
Sizeフィールド
s File data
ファイルデータ
無圧縮の場合、ファイルの内容がそのまま格納される
圧縮した場合、圧縮後のデータが格納される
パスワード保護(従来の暗号化)
入力したパスワードから算出したキーを使ってこのデータを暗号・復号化する仕組みが提供されています。ZIP独自のこの方法は仕様書に記されています。
しかし現代においては、この方法は隠蔽強度の弱い簡易なものとされています。 まず暗号化の対象が File Data に限定されていること、そして暗号化前のデータの一部が知れている場合に暗号キーの部分が割れてしまうのです。 このことはファイル名(特に拡張子)などからデータ形式の見当が付く様な場合、重大な結果につながります。
この問題を解決するためZIPではより強固な暗号化が使用できるようになりましたが、権利的な制約が有り普及していません。

Data descriptor

Data descriptor の使用は任意です。と言うか、ランダムアクセス主流の現代ではまったく見かけない都市伝説のような存在です。
Local file header にある3種のフィールドに格納する値は File data の格納処理と同時に求めることができますが、状況によってはストリームをさかのぼって追記することが困難な場合もあります。 Data descriptor はこの3種の値を File data の後ろに後付けする仕組みです。
シーケンシャルとランダム
シーケンシャルアクセスは、データ列に対して一方向に順番にアクセスしていく方法です。 帯状の媒体?ビデオテープ(VHS)って言えばまだ記憶にとどめている人もいるかと思いますがデータのスキップに相応の時間を要したり、ストリーミング配信のように "後から編集する" ことが叶わない場合もあります。
対してランダムアクセスは、データ列の任意位置へ即時アクセスが可能な方法です。半導体メモリーのようにモーターを必要としない媒体の登場は斬新でしたよ(え?。
閑話休題。識別子を持つタイプと持たないタイプがあります。
識別子を持つタイプ (推奨)
Sizeフィールド
4 data descriptor header signature (?)
Data descriptor であることを示す固定値
$08074B50 = $50, $4B, $07, $08
4 crc-32
ファイルのデータから算出した CRC-32 の値
n (CRC-32)
4 compressed size
圧縮後のデータ量、無圧縮なら uncompressed size と同値
n [byte]
4 uncompressed size
圧縮前のデータ量、つまりファイルサイズ
n [byte]
識別子を持たないタイプ
Sizeフィールド
4 crc-32
ファイルのデータから算出した CRC-32 の値
n (CRC-32)
4 compressed size
圧縮後のデータ量、無圧縮なら uncompressed size と同値
n [byte]
4 uncompressed size
圧縮前のデータ量、つまりファイルサイズ
n [byte]
なぜ 2種類あるかというと、、、
もともとは識別子を定めておらず識別子フィールドを持っていなかった(後者を使用)。 しかし一般的に $08074B50 という値が識別子として使われるようになったため現在では識別子を付加する(前者の使用)ことを推奨している。 よって展開器は両方の場合を考慮しなさい。
……みたいなことが仕様書に書いてあるorz。

Central directory header

ファイルの情報やZIP化したときの状況を格納します。Local file header との重複が多いですが省略や矛盾は厳禁です。
Sizeフィールド
4 central file header signature
Central directory header であることを示す固定値
$02014B50 = $50, $4B, $01, $02
1 version made by
ZIPファイル製作を行ったOSとアプリの情報
lower byte:アプリがサポートするZIPのバージョン
upper byte:OSの種類補足
10 (ver.1.0) ⇒無圧縮ファイル
20 (ver.2.0) ⇒フォルダ、デフレート、パスワード保護
45 (ver.4.5) ⇒ZIP64
1
00 (MS-DOS)
03 (UNIX)
07 (Macintosh)
2 version needed to extract
展開に必要なZIPのバージョン補足
10 (ver.1.0) ⇒無圧縮ファイル
20 (ver.2.0) ⇒フォルダ、デフレート、パスワード保護
45 (ver.4.5) ⇒ZIP64
2 general purpose bit flag
オプションフラグ
ビットフラグ
%0000000000000001 (パスワード保護)
%0000000000001000 (Data descriptor を使用)
%0000100000000000 (ファイル名やコメントが UTF-8文字)
2 compression method
ファイル圧縮に用いたアルゴリズム補足
0 (無圧縮ファイル)
8 (デフレート形式)
2 last mod file time
タイムスタンプ(時刻)
ビット割り当て
%1111100000000000 = 0〜23 [時]
%0000011111100000 = 0〜59 [分]
%0000000000011111 = 0〜29 [×2秒] ⇒1で2秒分を表す
2 last mod file date
タイムスタンプ(日付)
ビット割り当て
%1111111000000000 = 0〜 [+1980年] ⇒西暦1980年からの経過年
%0000000111100000 = 1〜12 [月]
%0000000000011111 = 1〜31 [日]
4 crc-32
ファイルのデータから算出した CRC-32 の値補足
n (CRC-32)
4 compressed size
圧縮後のデータ量、File data のサイズ
無圧縮なら uncompressed size フィールドと同値
n [byte]
0 [byte] (データ無し) ⇒フォルダ、空ファイルなど
$FFFFFFFF (ZIP64)
4 uncompressed size
圧縮前のデータ量、つまりファイルサイズ
n [byte]
0 [byte] (データ無し) ⇒フォルダ、空ファイルなど
$FFFFFFFF (ZIP64)
2 file name length
file name フィールドのサイズ
n [byte]
0 [byte] (名前なし) ⇒標準入力からの流入
2 extra field length
extra field フィールドのサイズ
n [byte]
0 [byte] (extra field を使用しない)
2 file comment length
file comment フィールドのサイズ
n [byte]
0 [byte] (file comment を使用しない)
2 disk number start
対応する Local file header が格納されているディスクの番号補足
0 (ディスク分割はしない)
n [番目] (ディスクの識別番号)
$FFFF (ZIP64)
2 internal file attributes
データの属性(性質を示す)ビットフラグ
0 (バイナリデータ)
1 (ASCII、テキストファイル)
※確かな場合にビットOnする
※展開器によっては改行コードなどを変換
4 external file attributes
ファイルの属性(読み込み専用とか隠しファイルとか)
※この値はOSに依存
※展開器は属性を再現したりしなかったり… ⇒隠しファイルが丸見えになった事例有り
4 relative offset of local header
対応する Local file header までのオフセット
ZIPファイル先頭から(ディスク分割時はそのディスクの先頭から)のオフセット補足
n [byte] (オフセット)
$FFFFFFFF (ZIP64)
s file name
ファイル名を格納する領域
"ファイル名"
※ドライブ・デバイスレター・先頭のスラッシュは無し
※フォルダ区切りは普通のスラッシュ"/"
s extra field
拡張データを格納する領域補足
任意データ
s file comment
当該ファイルに対してのコメントを格納する領域
"コメント" (コメント文字列)

End of central directory record

1つのZIPファイルに1つだけ存在しそのZIPファイル全体の情報を格納します。 アプリは最初にこの End of central directory record を読み込み、オフセット情報を順にたどっていくことですべての格納ファイルにアクセスすることができます。 …のはずなんですが不確定長のフィールドがあります。。。
Sizeフィールド
4 end of central dir signature
End of central directory record であることを示す固定値
$06054B50 = $50, $4B, $05, $06
2 number of this disk
このディスク(End of central directory record の有る)の番号
ディスクの総数補足
0 (ディスク分割はしない)
n [番目] (ディスクの識別番号)
$FFFF (ZIP64)
2 number of the disk with the start of the central directory
最初の Central directory header が有るディスクの番号
0 (ディスク分割はしない)
n [番目] (ディスクの識別番号)
$FFFF (ZIP64)
2 total number of entries in the central directory on this disk
基本的にはファイル数のこと
ディスク分割において、同じディスクから取得できる Central directory header の数
n [個]
$FFFF (ZIP64)
2 total number of entries in the central directory
基本的にはファイル数のこと
ZIPファイルに格納してある Central directory header の総数
n [個]
$FFFF (ZIP64)
4 size of the central directory
全ての Central directory header サイズの合計値
n [byte]
$FFFFFFFF (ZIP64)
4 offset of start of central directory with respect to the starting disk number
最初の Central directory header までのオフセット
ZIPファイル先頭から(ディスク分割時はそのディスクの先頭から)のオフセット
n [byte] (オフセット)
$FFFFFFFF (ZIP64)
2 .ZIP file comment length
.ZIP file comment フィールドのサイズ
n [byte]
0 [byte] (.ZIP file comment を使用しない)
s .ZIP file comment
ZIPファイルに対してのコメントを格納する領域
文字列? (安全性は保証されない)

補足事項とフィールドの解説

補足とフィールドの詳細です。

ファイル格納順

仕様書では Local file header と Central directory header の並び順が異なる可能性についてふれています。つまりZIP構造上『ファイルA(格納)→ファイルB(格納)→ファイルB(情報)→ファイルA(情報)→ZIP情報』ということがおこりえます。

ZIP64

ZIPフォーマットには各所に16/32bitの制限があり、この制限を64bitに拡張したものがZIP64と呼ばれ無拡張ZIPと区別されます。
ZIP64は通常のZIP構造を必要に応じて拡張したものです。Local file header と Central directory header はそれぞれの extra field を使用し、End of central directory record は拡張要素を用意します。
※ZIP64はPKWARE社の商標(tm)である記載があります(1.3.1)

ディスク分割

巨大なZIPファイルを小分けにして保存する機能があります。
かつて一世風靡(?)したフロッピーのような少容量媒体にZIPファイルを保存する目的のようです。フロッピー(容量:0.0014Gbyte)は20世紀までは重宝されていた媒体で、今ではその地位をフラッシュメモリにあけ渡しました。
近年、メールに添付したりデータの大容量化に伴う超巨大ファイルが出現し再注目な機能かもです。
ディスク番号はこの小分けファイルに付けられた通し番号で、外部要因(ファイル名など)で管理されるみたいです。 また、ファイル内容自体はほぼブツ切り状態で格納されるようですが、新たに という識別子が登場するらしいです。
ちなみにこの $08074B50 って Data descriptor とどっかぶりしてるから、わはーさっしろf( ̄△ ̄メ)ゞムー
だってさ...
ディスク分割側としての登場の場合、直後に $04034B50 (Local file header の識別子)が続くようです。。。
ディスク番号フィールドにはこの通し番号(1 から始まる)が格納され、分割しないのであれば 0 です。
オフセットフィールドに格納されるオフセット値は、そのディスクの先頭からのバイト数です。つまり分割しないのであれば、ZIPファイルの先頭からのバイト数となります。……紛らわしいですよね→relative.

ZIPバージョン

ZIPの対応状況を示すバージョンはメジャーver.とマイナーver.の2値で表します。フィールドの格納値は『メジャーver.×10+マイナーver.』の形式になります。
version needed to extract フィールドは、そのファイルを取り出すために必要な演算能力を示す訳ですから複数の場合は最大のものになります。
version made by フィールドのOS情報は、ファイル属性・データ属性を再現・変換するために展開器から参照されるかもしれません。
ZIPのバージョン(4.4.3.2)
1.0 - Default value
1.1 - File is a volume label
2.0 - File is a folder (directory)
2.0 - File is compressed using Deflate compression
2.0 - File is encrypted using traditional PKWARE encryption
2.1 - File is compressed using Deflate64(tm)
2.5 - File is compressed using PKWARE DCL Implode
2.7 - File is a patch data set
4.5 - File uses ZIP64 format extensions
4.6 - File is compressed using BZIP2 compression*
5.0 - File is encrypted using DES
5.0 - File is encrypted using 3DES
5.0 - File is encrypted using original RC2 encryption
5.0 - File is encrypted using RC4 encryption
5.1 - File is encrypted using AES encryption
5.1 - File is encrypted using corrected RC2 encryption**
5.2 - File is encrypted using corrected RC2-64 encryption**
6.1 - File is encrypted using non-OAEP key wrapping***
6.2 - Central directory encryption
6.3 - File is compressed using LZMA
6.3 - File is compressed using PPMd+
6.3 - File is encrypted using Blowfish
6.3 - File is encrypted using Twofish
※compress:圧縮 encrypt:暗号化 *,**,***,+:補足付事項
OSの種類(4.4.2.2)
0 - MS-DOS and OS/2
(FAT / VFAT / FAT32 file systems)
1 - Amiga
2 - OpenVMS
3 - UNIX
4 - VM/CMS
5 - Atari ST
6 - OS/2 H.P.F.S.
7 - Macintosh
8 - Z-System
9 - CP/M
10 - Windows NTFS
11 - MVS (OS/390 - Z/OS)
12 - VSE
13 - Acorn Risc
14 - VFAT
15 - alternate MVS
16 - BeOS
17 - Tandem
18 - OS/400
19 - OS/X (Darwin)
20 thru 255 - unused

extra field

拡張データ領域は Local file header や Central directory header に専用のフィールドが無い多種の情報を格納するための領域です。 ちなみに同じファイルであっても Local file header と Central directory header のこのフィールドが同一であるとは限りません。
1レコード『HeaderID(2byte)→DataSize(2byte)→Data(DataSize byte)』を必要分連結したものです。
このフィールドの内容は、全てが仕様として確定されているわけではなく第三者への解放部分もあり衝突をおこす可能性もあります。
$0001 - Zip64 extended information extra field
Local file header や Central directory header でZIP64を使用するためのレコードです。サイズやオフセットのフィールドで格納する値が容量を超えている場合にその値を格納します。
Sizeフィールド
2 HeaderID
レコードの種類を示す固定値
$0001 (Zip64 extended information extra field)
2 DataSize
レコードの大きさ
以下の合計値
8 [byte] (Original Size を持つ場合)
8 [byte] (Compressed Size を持つ場合)
8 [byte] (Relative Header Offset を持つ場合)
4 [byte] (Disk Start Number を持つ場合)
(8) Original Size
圧縮前のデータ量
n [byte]
(8) Compressed Size
圧縮後のデータ量
n [byte]
(8) Relative Header Offset
当該ファイルの Local file header までのオフセット
n [byte]
(4) Disk Start Number
格納しているディスクの番号
n [番目]
$5455 - extended timestamp
ファイルのタイムスタンプです…たぶん。仕様書では主なHeaderIDとして紹介していますが、詳細が見当たりませんorz。
Sizeフィールド
2 HeaderID
レコードの種類を示す固定値
$5455 (extended timestamp)
2 DataSize
レコードの大きさ
以下の合計値
1 [byte] (ビットフラグ分)
4 [byte] (更新日時を持つ場合)
4 [byte] (アクセス日時を持つ場合)
4 [byte] (作成日時を持つ場合)
1 ビットフラグ
%00000001 (更新日時を持つ)
%00000010 (アクセス日時を持つ)
%00000100 (作成日時を持つ)
(4) 更新日時
1970年1月1日 0時00分00秒からの経過秒
(4)アクセス日時
(4)作成日時

crc-32

加工前(圧縮前・ZIPに格納する前)のファイルデータから 4byte のキーを計算します。展開後(ZIP解凍後)のファイルデータから同じキーが算出できればデータに誤り(変質)がないことが確認できます(恣意的な改ざんや偶然の一致という可能性もあります)。
マジックナンバーは $DEBB20E3、算出は以下のソースで。
    dim CRCTable, 256   ;CRCテーブル(演算用固定値テーブル)
    repeat 256
        ib = cnt
        repeat 8
            ib = (ib >> 1 & $7FFFFFFF) ^ ((ib & 1) * $EDB88320)
        loop
        CRCTable(cnt) = ib
    loop

    dialog "*", 16  : if stat == 0  : stop
    FileName = refstr
    exist FileName  : FileSize = strsize
    sdim FileData, FileSize
    bload FileName, FileData, FileSize

    CRC32 = $FFFFFFFF   ;初期値
    repeat FileSize
        CRC32 = (CRC32 >> 8 & $00FFFFFF) ^ CRCTable(peek(FileData, cnt) ^ (CRC32 & $FF))
    loop
    CRC32 = CRC32 ^ $FFFFFFFF   ;ファイル分割ロードのときは、この値を初期値に代入する
    mes CRC32
; http://www.tvg.ne.jp/menyukko/ ; Copyright(C) 2011 衣日和 All rights reserved.
※HSPの都合上"符号付き"で処理しています。
※ソースはPNG画像仕様書にあったものをHSP用に、、、えっ?

compression method

一般的にZIPファイルで使用されるのはデフレートアルゴリズムのみですが、ZIPでは他にもさまざまな圧縮法をサポートいています。アルゴリズムの詳細は仕様書に記されています。
デフレート(deflate)
ZIP書庫ファイルやPNG画像ファイルなどで使われている圧縮アルゴリズムです。
以下の記載はかなりいい加減です。m(_ _)m
デフレート圧縮はスライド辞書とハフマン符号化という二つの圧縮を組み合わせた方法です。
スライド辞書は、繰り返しの文を簡易な記号で置き換えることで全体を小さくする方法で、歌詞カードなどでよく見かけるアレに似ています。原文『こんにちはこんにちはこんにちはこんばんは。』に対して『こんにちは[5,12]ばんは。』な感じです([5,12]は[5文字前から12文字複製]の意味)。
ハフマン符号化は、一覧表と参照順を組み合わせます。先の原文に対して一覧表『[1:こんにちは][2:こんばんは。]』参照順『[1][1][1][2]』みたいな。
圧縮の詳細は、仕様書・RFC1950・RFC1951・RFC1952あたり。利用するにはDeflateStream(MSDN)・zlib(C言語)・各種DLLなどが良さそうです。