这篇条目有关 Source引擎。如需详情,点击这里。
这篇条目有关 Source 2引擎。如需详情,点击这里。

VPK (file format)

From Valve Developer Community
< Zh
Jump to navigation Jump to search
Under construction.png
This page is actively undergoing a major edit.
As a courtesy, please do not edit this while this message is displayed.
If this page has not been edited for at least several hours to a few days, please remove this template. This message is intended to help reduce edit conflicts; please remove it between editing sessions to allow others to edit the page.

The person who added this notice will be listed in its edit history should you wish to contact them.

Info content.png
This page needs to be translated.
This page either contains information that is only partially or incorrectly translated, or there isn't a translation yet.
If this page cannot be translated for some reason, or is left untranslated for an extended period of time after this notice is posted, the page should be requested to be deleted.
Also, please make sure the article tries to comply with the alternate languages guide.
English (en)中文 (zh)Translate (Translate)

VPK ("Valve PacK") 是一种包格式,后GCF(en) 起源 起源 引擎游戏和 起源2 起源2 引擎都使用它来存储游戏内容。它最早在 求生之路 引擎分支 中被引入。

Warning.png混淆的风险:Vampire: The Masquerade - Bloodlines Vampire: The Masquerade - Bloodlines 使用文件扩展名相同但不同的包格式, 称为Vampire PacK 文件。

Conception

求生之路 求生之路起源2013 起源2013 之前,以及在 SteamPipe(en)更新之后将 VPK 向后移植到较旧的 Source 引擎分支之前, 典型的 Source 引擎游戏将其内容存储在.gcf(en) 文件中. 可执行文件、可修改文件 (例如配置文件) 和自定义内容被复制并本地存储在用户的硬盘驱动器上. Possibly brought on by poor performance, the NCF file format was introduced and all game content was copied entirely to the hard drive. This, however, introduced a new problem. Source engine materials and models are stored in thousands of small files and it would be expensive to continuously open and close these files. The solution was the conception of the VPK file format which is used to store Left 4 Dead materials, models and particles in a handful of files which can be quickly accessed.

功能

预加载数据

为了高效地访问小型或关键文件,每个文件的开头可以选择性地存储在 VPK 目录中。 在实践中,这似乎仅限于 Source 引擎材质(VMT(en)文件)的前 1000 个字节,而这些文件通常只有几百字节大小。

多个存档

以前通过更高级的 GCF 文件格式分发的 Source 引擎游戏,可以方便地在内部对新的和更新的文件进行碎片化处理。 这意味着可以高效地下载和保存新的和更新的文件,同时最大限度地减少带宽和磁盘 I/O。 由于新的 VPK 文件与分发方式无关(《求生之路》通过 NCF 文件分发,而 Steam 对 VPK 文件格式一无所知),因此它们的内容被拆分到多个存档中,这些存档的大小似乎限制在约 32 MB 左右。 因此,当特定文件中的文件更新时,只需要更新包含该文件的 VPK 存档。 此外,新文件可以下载到它们各自独立的存档中。 这就是为什么大多数较新的存档体积都很小的原因;它们的内容仅限于在单个更新中添加的文件。

Versions

1
异形丛生 异形丛生
求生之路 求生之路
求生之路2 求生之路2
传送门2 传送门2
起源电影制作器 起源电影制作器
Postal III Postal III
2
反恐精英:全球攻势 反恐精英:全球攻势
反恐精英:起源 反恐精英:起源
胜利之日:起源 胜利之日:起源
Dota 2 Dota 2 (original release)
Garry's Mod Garry's Mod
半衰期:起源 半衰期:起源
半衰期2 半衰期2
半衰期2:死亡竞赛 半衰期2:死亡竞赛
传送门 传送门
军团要塞2 军团要塞2
Depot VPK files for 起源2006 起源2006, 起源2007 起源2007 and 起源2009 起源2009 games
All 起源2 起源2 games
Third-Party (unknown version)
Titanfall Titanfall
Titanfall 2 Titanfall 2

File Format

A VPK package is actually spread out over multiple files sharing the same extension. The directory is stored in a specific file called <name>_dir.vpk and the content is spread over several additional archive files called <name>_*.vpk (where * is the zero based archive index). Consequentially, there are two file formats:

Directory

Header

VPK 1

最初, VPK 文件没有文件头或标识符. 这种情况在 2009 年 6 月 25 日《求生之路》更新 发布时发生了改变,该更新增加了对第三方战役的支持。在此日期之后创建的 VPK 目录文件具有以下文件头:

struct VPKHeader_v1
{
	const unsigned int Signature = 0x55aa1234;
	const unsigned int Version = 1;

	// The size, in bytes, of the directory tree
	unsigned int TreeSize;
};

If the file data is stored in the same file as the directory, its offset is (sizeof(VPKHeader_v1) + TreeSize).

VPK 2
struct VPKHeader_v2
{
	const unsigned int Signature = 0x55aa1234;
	const unsigned int Version = 2;

	// The size, in bytes, of the directory tree
	unsigned int TreeSize;

	// How many bytes of file content are stored in this VPK file (0 in CSGO)
	unsigned int FileDataSectionSize;

	// The size, in bytes, of the section containing MD5 checksums for external archive content
	unsigned int ArchiveMD5SectionSize;

	// The size, in bytes, of the section containing MD5 checksums for content in this file (should always be 48)
	unsigned int OtherMD5SectionSize;

	// The size, in bytes, of the section containing the public key and signature. This is either 0 (CSGO & The Ship) or 296 (HL2, HL2:DM, HL2:EP1, HL2:EP2, HL2:LC, TF2, DOD:S & CS:S)
	unsigned int SignatureSectionSize;
};

If the file data is stored in the same file as the directory, its offset is (sizeof(VPKHeader_v2) + TreeSize).

Tree

The format of the directory tree is a little unorthodox. It consists of a tree three levels deep that seems to be structured for file size. The first level of the tree consists of file extensions (e.g. vmt, vtf and mdl), the second level consists of directory paths (e.g. materials/brick, materials/decals/asphalt and models/infected), and the third level consists of file names, file information and preload data. Each tree node begins with a null terminated ASCII string and empty strings are used to signify the end of a parent node. Pseudo-code to read the directory might look something like:

ReadString(file)
	string = ""
	while true
		char = ReadChar(file)
		if char = null
			return string
		string = string + char
ReadDirectory(file)
	while true
		extension = ReadString(file)
		if extension = ""
			break
		while true
			path = ReadString(file)
			if path = ""
				break
			while true
				filename = ReadString(file)
				if filename = ""
					break
				ReadFileInformationAndPreloadData(file)

A nonexistent extension (in example/file), path (in example.txt), or filename is represented by a single space.

Immediately following the null terminator for the filename is the following structure:

struct VPKDirectoryEntry
{
	unsigned int CRC; // A 32bit CRC of the file's data.
	unsigned short PreloadBytes; // The number of bytes contained in the index file.

	// A zero based index of the archive this file's data is contained in.
	// If 0x7fff, the data follows the directory.
	unsigned short ArchiveIndex;

	// If ArchiveIndex is 0x7fff, the offset of the file data relative to the end of the directory (see the header for more details).
	// Otherwise, the offset of the data from the start of the specified archive.
	unsigned int EntryOffset;

	// If zero, the entire file is stored in the preload data.
	// Otherwise, the number of bytes stored starting at EntryOffset.
	unsigned int EntryLength;

	const unsigned short Terminator = 0xffff;
};

If a file contains preload data, the preload data immediately follows the above structure. The entire size of a file is PreloadBytes + EntryLength.

Footer

VPK2 adds a footer section that contains extra CRC data for pure mode "so the dedicated servers do not need to compute them at startup but can be checked with the command sv_pure_checkvpk".

VPK 2 Sections

File data

This can be read like a separate VPK archive embedded in the directory file (see EntryOffset comment in VPKDirectoryEntry).

Archive MD5 checksums

This section is an array of these:

struct VPK_ArchiveMD5SectionEntry
{
	unsigned int ArchiveIndex;
	unsigned int StartingOffset; // where to start reading bytes
	unsigned int Count; // how many bytes to check
	char MD5Checksum[16]; // expected checksum
}

Since sizeof(VPK_MD5SectionEntry) is 28, the section size must be a multiple of 28.

Valve's VPK tool refers to these as cache line hashes. They can be checked against the file content with the checkhash command.

Other MD5 checksums
struct VPK_OtherMD5Section
{
	char TreeChecksum[16];
	char ArchiveMD5SectionChecksum[16];
	char WholeFileChecksum[16];
}
Public Key & Signature
struct VPK_SignatureSection
{
	unsigned int PublicKeySize; // always seen as 160 (0xA0) bytes
	char PublicKey[PublicKeySize];

	unsigned int SignatureSize; // always seen as 128 (0x80) bytes
	char Signature[SignatureSize];
}

This section can be viewed with Valve's VPK tool's dumpsig command.

Games with this section (296 bytes):

反恐精英:起源 反恐精英:起源
胜利之日:起源 胜利之日:起源
半衰期:起源 半衰期:起源
半衰期2 半衰期2
半衰期2:死亡竞赛 半衰期2:死亡竞赛
半衰期2:第一章 半衰期2:第一章
半衰期2:第二章 半衰期2:第二章
半衰期2:失落的海岸线 半衰期2:失落的海岸线
传送门 传送门
军团要塞2 军团要塞2

Games lacking this section:

反恐精英:全球攻势 反恐精英:全球攻势
The Ship: Murder Party The Ship: Murder Party (Multiplayer)
Depot VPK files (起源2006 起源2006, 起源2007 起源2007 and 起源2009 起源2009 games)

All VPKs tested were from the English Language

Archive

VPK Archives store raw file data. They have no identifying header and know nothing of their contents. Though not necessary, the raw file data is typically tightly packed.

Archives are named by the main VPK name with the archive number appended. (e.g tf2_misc_050.vpk).

待完善: Rewrite and merge Notes section to the format section

Notes

Broom icon.png
此物品需要清理以符合更高的质量标准
如需帮助,请参阅 VDC 编辑帮助维基百科清理过程。另外,请记住在本文的讨论页检查标记者留下的任何笔记。

Valve apparently added skipping to the specifications. Someone found out this when trying to write their VPK parser in C#. This should be merged with the format area later, and reworded.

Valve uses nulls to signify if skipping is used. On a normal entry, it uses 2 nulls, and is followed by the format above. However, there are cases where there are only one, or no nulls at the start, and this means that some level of skipping is used.

If there are 2 nulls, no skipping is used, and the extension, path, and name are read as usual. If there is 1 null, the extension is skipped (It's the same extension as the last read entry), and then the path and name are read as usual. If there are no nulls, the extension and path are the same as the last entry (Skipped), and only the name is read.

This system has only been observed in VPK1.

VPK readers

See also