vpk
VPK ("Valve Pak") is the archive format used by the Source Engine, Valve's game engine (see the Valve Developer Wiki article). This vpk is a Ruby library for reading and writing those VPK files — it can both extract a VPK file and pack a directory into one.
Published as a gem, with a command-line tool bundled in.
gem install vpk
require 'vpk'
# Extract a VPK file
VPK::VPKFile.new("./path_to.vpk").extract_to("./")
# Pack a directory into a VPK
VPK::VPKFile.archive("./path_to_dir").write_to("./archive.vpk")
From the command line it works like this: -c creates a .vpk with the same name as the directory, and -x extracts into the current directory.
vpk -c some_dir # pack some_dir into some_dir.vpk
vpk -x some_file.vpk # extract into the current directory
Internals
What it implements is version 1 of the VPK format. A file consists of three layers: a "12-byte header", a "directory section holding the file listing", and a "data section of concatenated file contents".
- The directory section is a 3-level tree: "extension → path → filename". Each element is a NUL-terminated string, with an empty string terminating that level. Files directly under the root use a single ASCII space as their path
Directory section layout (example):
"vtf" <- level 1: extension
"materials/vgui" <- level 2: path
"logo" + entry info (18 bytes) <- level 3: filename
"" <- end of filenames
"" <- end of paths
"" <- end of extensions = end of the directory section
- The per-file entry information is a fixed 18 bytes, holding the CRC, offset, length, and so on. Positions inside the data section are referenced via
entry_offset(relative to the end of the directory section). The data section is, as the name suggests, the payloads of the files concatenated as-is
The read and write flows look like this.
- VPKFile (
lib/vpk.rb) — on read, it first validates the header signature (0x55AA1234), parses the directory section with a triple loop, then loads every entry's payload into memory and verifies it against the CRC32 (Zlib). A signature mismatch raisesVPKFileFormatError, a CRC mismatch raisesVPKFileInvalidCRCError - VPKDirectoryEntry — the class holding one file's metadata (CRC, offset, length, etc.) and payload. Conversion to and from full paths and CRC verification also live here
- The write side (
VPKFile.archive) walks the directory recursively withFind.findto build entries, then groups them by extension and path withto_file_struct_tree. It computes the directory section's length up front (string lengths plus 18 bytes per entry) and then writes the header, directory section, and data section in order - The VPK it writes is a single-file layout with no data split into separate files;
archive_indexis fixed at0x7FFFand preload data is unused (always 0) - VPKUtil — shortcuts for calling
extract/archiveas a single method - Logging can be plugged in via
VPKFile.logger=(no output by default)
🤖 This English version is machine-translated by AI.