vpk
VPK(“Valve Pak”)は、Source Engine(Valve のゲームエンジン)で使われるアーカイブ形式です(Valve Developer Wiki の解説)。この vpk は、その VPK ファイルを Ruby で読み書きするライブラリで、VPK ファイルの展開と、ディレクトリを VPK にまとめる圧縮の両方ができます。
gem として公開していて、コマンドラインツールも同梱しています。
gem install vpk
require 'vpk'
# VPK ファイルを展開する
VPK::VPKFile.new("./path_to.vpk").extract_to("./")
# ディレクトリを VPK にまとめる
VPK::VPKFile.archive("./path_to_dir").write_to("./archive.vpk")
コマンドラインからはこう使います。-c はディレクトリと同名の .vpk を作り、-x はカレントディレクトリに展開します。
vpk -c some_dir # some_dir を some_dir.vpk に圧縮
vpk -x some_file.vpk # カレントディレクトリに展開
内部設計
実装しているのは VPK フォーマットのバージョン 1 です。ファイルは「12 バイトのヘッダ」「ファイル一覧を持つディレクトリ部」「中身を連結したデータ部」の三層でできています。
- ディレクトリ部は「拡張子 → パス → ファイル名」の 3 段ツリーです。各要素は NUL 終端文字列で、空文字列がその段の終端。ルート直下のファイルはパスを半角スペース 1 文字で表します
ディレクトリ部の並び(例):
"vtf" ← 第 1 段: 拡張子
"materials/vgui" ← 第 2 段: パス
"logo" + エントリ情報(18 バイト) ← 第 3 段: ファイル名
"" ← ファイル名の終端
"" ← パスの終端
"" ← 拡張子の終端 = ディレクトリ部おわり
- ファイルごとのエントリ情報は 18 バイト固定で、CRC・オフセット・長さなどを持ちます。データ部内の位置は
entry_offset(ディレクトリ部の終端からの相対位置)で参照します。データ部はその名の通り、各ファイルのペイロードをそのまま連結したものです
読み書きの流れはこうなっています。
- VPKFile(
lib/vpk.rb)— 読み込み時はまずヘッダのシグネチャ(0x55AA1234)を検証し、ディレクトリ部を 3 重ループでパースしたあと、全エントリのペイロードをメモリに読み込んで CRC32(Zlib)を照合します。シグネチャ不一致はVPKFileFormatError、CRC 不一致はVPKFileInvalidCRCErrorを投げます - VPKDirectoryEntry — 1 ファイル分のメタデータ(CRC・オフセット・長さ等)とペイロードを持つクラスです。フルパスとの相互変換や CRC 検証もここにあります
- 書き出し側(
VPKFile.archive)はFind.findでディレクトリを再帰走査してエントリを作り、to_file_struct_treeで拡張子→パス別にグループ化します。ディレクトリ部の長さ(文字列長 + エントリごとに 18 バイト)を先に計算してから、ヘッダ・ディレクトリ部・データ部の順に書き出します - 書き出す VPK はデータを別ファイルに分割しない単一ファイル構成で、
archive_indexは0x7FFF固定、preload データは使いません(常に 0) - VPKUtil —
extract/archiveを 1 メソッドで呼べるショートカットです - ログは
VPKFile.logger=で Logger を差し込めます(デフォルトは出力なし)