srctools.vpk
This module reads and writes VPK archives, used to store content for Source games. These archives are uncompressed, and consist of two sorts of files:
The “directory” (usually named
archive_dir.vpk). The directory contains the list of all files, and optionally part or whole sections of files.One or more data files (named like
archive_032.vpk).This has no structure, and simply consists of file data concatenated together.
Alternatively, a “singular” VPK archive is also possible, where all contents must be stored in the directory file. Note that removing data from a data file is not possible - changing a file’s contents or deleting it will change the directory, but leave the old data. This does have the advantage that updating a shipped game only requires downloading new data files for the additional content, but it means old data stays around.
Opening and closing
VPKs can be opened in three modes, like regular files. Changes to the directory are only applied
once write_dirfile() is called, but writes to data files occur immediately.
The VPK can be used like a context manager to automatically save, if no exception was raised.
- class srctools.vpk.OpenModes
Bases:
EnumModes for opening VPK files.
- READ = 'r'
- WRITE = 'w'
- APPEND = 'a'
- class srctools.vpk.VPK(
- dir_file: str | PathLike[str],
- *,
- mode: OpenModes | str = 'r',
- dir_data_limit: int | None = 1024,
- version: int = 1,
Represents a VPK archive.
VPKs can either be a singular file, or a main
archive_dir.vpkwith associated numericarchive_XXX.vpkfiles containing the data.
Attributes
- VPK.filename
The filename of the directory VPK file, or the single file.
This can be assigned to set
file_prefix.
- VPK.path
The full path of the directory VPK file, or the single file.
- VPK.mode: OpenModes
How the file was opened.
Read mode, the file will not be modified and it must already exist.
Write mode will create the directory if needed.
Append mode will also create the directory, but not wipe the file.
- VPK.file_prefix
The VPK filename, without
.vpk, or_dir.vpkif a directory.
- VPK.dir_limit: int | None
The maximum amount of data for files saved to the dir file.
None: No limit.
0: Save all to a data file.
The block of data after the header, which contains the file data for files stored in the
_dirfile, not numeric files.
File Objects
- class srctools.vpk.FileInfo
Represents a file stored inside a VPK.
Do not call the constructor, it is only meant for VPK’s use. Attributes should not be assigned to - use
write()to change the contents.These have no public constructor, and represent a file in a VPK. All attributes should be considered readonly - call methods instead.
- name
- filename
The full filename for this file.
- size
The total size of this file.
Locating Files
The structure of a VPK organises files by folder and extension, making iteration efficient. To locate a specific file, the simplest way is to index the VPK in one of three ways:
vpk['folders/name.ext']
vpk['folders', 'name.ext']
vpk['folders', 'name', 'ext']
If the extension or folders are known, this avoids needing to split the filename.
in, and len() checks also work as expected.
If the filename is not known, iterating the VPK gives each file in turn. Alternatively, the following methods can be used:
- for filename in VPK.filenames(ext: str = '', folder: str = '') Iterator[str]
Yield filenames from this VPK.
If an extension or folder is specified, only files with this extension or in this folder are returned.
- for folder in VPK.folders(*, ext: str | None = None) Iterator[str]
Yield the names of folders present in this VPK.
If an extension is specified, only folders containing files with that extension are returned.
- for file in VPK.fileinfos( ) Iterator[FileInfo]
Yield file info objects from this VPK.
If an extension or folder is specified, only files with this extension or in this folder are returned.
Once found, call read to read the contents:
Writing
To write to a VPK, create a new file info entry, then call write:
- VPK.new_file( ) FileInfo
Create the given file, making it empty by default.
If root is set, files are treated as relative to there, otherwise the filename must be relative.
FileExistsError will be raised if the file is already present.
- FileInfo.write(data: bytes, arch_index: int | None = None) None
Replace this file with the given byte data.
- Parameters:
data – The contents of the file to write.
arch_index – This is the
pak01_000.vpkfile to put data into (orNonefor :file`_dir.vpk`). This is ignored if the VPK is singular.
Data written to the directory file is not immediately saved,
VPK.write_dirfile()must subsequently be called to do so.If this file already exists in the VPK, the old data is not removed from numeric files. For this reason VPK writes should be done once per file if possible.
Alternatively, call these methods to do both at once.
- VPK.add_file(
- filename: str | tuple[str, str] | tuple[str, str, str],
- data: bytes,
- root: str = '',
- arch_index: int | None = 0,
Add the given data to the VPK.
If root is set, files are treated as relative to there, otherwise the filename must be relative. arch_index is the pak01_xxx file to copy this to, if the length is larger than self.dir_limit. If None it’s written to the _dir file.
FileExistsError will be raised if the file is already present.
- VPK.add_folder(folder: str, prefix: str = '') None
Write all files in a folder to the VPK.
If prefix is set, the folders will be written to that subfolder.
You can also do del vpk['some/filename'] to remove a file.