ValvePak 7.2.0.221
dotnet add package ValvePak --version 7.2.0.221
NuGet\Install-Package ValvePak -Version 7.2.0.221
<PackageReference Include="ValvePak" Version="7.2.0.221" />
<PackageVersion Include="ValvePak" Version="7.2.0.221" />
<PackageReference Include="ValvePak" />
paket add ValvePak --version 7.2.0.221
#r "nuget: ValvePak, 7.2.0.221"
#:package ValvePak@7.2.0.221
#addin nuget:?package=ValvePak&version=7.2.0.221
#tool nuget:?package=ValvePak&version=7.2.0.221
<h1 align="center"> <img src="./Misc/logo.png" alt="Logo" width="128"> <br>Valve Pak for .NET </h1>
<p align="center"> Read, extract, verify, and create VPK files, the archive format that packages game content in Source and Source 2 games. <br /> <a href="https://www.nuget.org/packages/ValvePak/">NuGet</a> · <a href="#usage">Usage</a> · <a href="#create-a-vpk">Creating a VPK</a> · <a href="https://app.codecov.io/gh/ValveResourceFormat/ValvePak">Coverage</a> </p>
Usage
using ValvePak;
using var package = new Package();
// Open a vpk file
package.Read("pak01_dir.vpk");
// Find a file, this returns a PackageEntry
var file = package.FindEntry("path/to/file.txt");
if (file != null)
{
// Read a file to a byte array
package.ReadEntry(file, out byte[] fileContents);
// Inspect entry metadata
Console.WriteLine(file.GetFullPath()); // "path/to/file.txt"
Console.WriteLine(file.GetFileName()); // "file.txt"
Console.WriteLine(file.TotalLength); // file size in bytes
Console.WriteLine(file.CRC32); // CRC32 checksum
}
Do note that files such as pak01_001.vpk are just data files, you have to open pak01_dir.vpk.
FindEntry normalizes the path like added files are: \ is a directory separator and ASCII letters are lowercased.
To read from a stream instead of a path, set the file name first so that the chunk files can be located:
using var package = new Package();
package.SetFileName("pak01_dir.vpk");
package.Read(File.OpenRead("pak01_dir.vpk"));
Extract all files
using var package = new Package();
package.Read("pak01_dir.vpk");
var outputDirectory = "extracted";
foreach (var group in package.Entries!)
{
foreach (var entry in group.Value)
{
var filePath = Path.Combine(outputDirectory, entry.GetFullPath());
package.ReadEntry(entry, out byte[] data);
// Create the directory if needed, then write the file
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
File.WriteAllBytes(filePath, data);
}
}
Read into a user-provided buffer
var entry = package.FindEntry("path/to/file.txt");
// Allocate your own buffer (must be at least entry.TotalLength bytes)
var buffer = new byte[entry.TotalLength];
package.ReadEntry(entry, buffer, validateCrc: true);
Using ArrayPool to avoid allocations when reading many files:
var entry = package.FindEntry("path/to/file.txt");
var buffer = ArrayPool<byte>.Shared.Rent((int)entry.TotalLength);
try
{
package.ReadEntry(entry, buffer, validateCrc: true);
// Use buffer[..(int)entry.TotalLength] here
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
Stream-based access
GetMemoryMappedStreamIfPossible returns a memory-mapped stream for large files (over 4 KiB) and a MemoryStream for smaller ones. This avoids reading the entire file into a byte array.
var entry = package.FindEntry("path/to/file.txt");
using var stream = package.GetMemoryMappedStreamIfPossible(entry);
OpenEntryStream returns a seekable stream that reads an entry in place. A VPK stored in an entry of another VPK can be read from it without extracting it:
using var inner = new Package();
inner.SetFileName(entry.GetFullPath());
inner.Read(package.OpenEntryStream(entry));
Optimize for many lookups
By default, FindEntry performs a linear scan. If you need to look up many files, call OptimizeEntriesForBinarySearch() before Read() to sort entries and use binary search instead. You can also pass StringComparison.OrdinalIgnoreCase for case-insensitive lookups.
using var package = new Package();
// Call before Read() to enable binary search for FindEntry
package.OptimizeEntriesForBinarySearch();
package.Read("pak01_dir.vpk");
// FindEntry calls are now significantly faster
var file = package.FindEntry("path/to/file.txt");
Package.Open("pak01_dir.vpk") does both in one call.
Verification
using var package = new Package();
package.Read("pak01_dir.vpk");
// Verify the signature, all hashes, and the CRC32 checksums of files no hash covers in one call
package.Verify();
// Verify MD5 hashes of the directory tree and whole file (version 1 packages have none)
package.VerifyHashes();
// Verify MD5/Blake3 hashes of individual chunk files (pak01_000.vpk, pak01_001.vpk, ...)
package.VerifyChunkHashes();
// Verify CRC32 checksums of every file in the package
package.VerifyFileChecksums();
// Verify the RSA signature if the package is signed
bool valid = package.IsSignatureValid();
Create a VPK
using var package = new Package();
// Add files to the package
package.AddFile("path/to/file.txt", File.ReadAllBytes("file.txt"));
package.AddFile("models/example.vmdl", File.ReadAllBytes("example.vmdl"));
// Remove a file from the package
package.RemoveFile(package.FindEntry("path/to/file.txt")!);
// Write the package to disk
package.Write("pak01.vpk");
All file data is stored in the single written file, which is limited to 2 GiB. To go beyond that, split the package into chunk files as described below.
Modify an existing VPK
A package that was read can have files added, removed or renamed and then be written to a new file, or back over the file it was read from, including a _dir.vpk with chunk files.
using var package = new Package();
package.Read("pak01.vpk");
package.AddFile("new/file.txt", File.ReadAllBytes("file.txt"));
package.RemoveFile(package.FindEntry("old/file.txt")!);
package.Write("pak01_modified.vpk");
Chunk files are named after the directory file, so a package with chunk files can only be written to another path with repack: true, which RequiresRepack(path) tells. Repacking a package without chunk files into a _dir.vpk moves its data into chunk files.
Split into chunk files
Files added with multiChunk are written right away into numbered chunk files (pak01_000.vpk, pak01_001.vpk, ...) next to the directory file, so their data is not kept in memory. A new chunk file is started once the current one is larger than WriteChunkSize (100 MiB by default). Write then only writes the directory file, which contains Blake3 hashes of the chunk files that can be verified with VerifyChunkHashes.
using var package = new Package();
// Chunk files are created next to the directory file, so its path must be set first
package.SetFileName("pak01_dir.vpk");
// Optionally change the chunk file size, in bytes
package.WriteChunkSize = 50 * 1024 * 1024;
// Data can be streamed from a file instead of being read into memory
using (var stream = File.OpenRead("example.vmdl"))
{
package.AddFile("models/example.vmdl", stream, multiChunk: true);
}
// Files added without multiChunk still go into the directory file
package.AddFile("manifest.txt", File.ReadAllBytes("manifest.txt"));
// The directory file must be written to the same path
package.Write("pak01_dir.vpk");
Removing a file that is already in a chunk file leaves its data in place until the package is written with repack: true. Chunk files stay on disk if the package is disposed without being written.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Blake3 (>= 3.0.2)
- System.IO.Hashing (>= 10.0.10)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on ValvePak:
| Package | Downloads |
|---|---|
|
ValveResourceFormat
Parser, decompiler, and exporter for Valve's Source 2 resource file formats. Supports models, textures, materials, maps, particles, and more. |
|
|
VPKTools
Utilities to parse Valve's PAK files for metadata purposes. |
|
|
CS2WorkshopManager
Packs a compiled addon from "game/csgo_addons" and publishes it to Steam. |
|
|
CS2CalloutExtractor
A library for extracting callouts from Counter-Strike 2 `.vpk` files. |
GitHub repositories (2)
Showing the top 2 popular GitHub repositories that depend on ValvePak:
| Repository | Stars |
|---|---|
|
ValveResourceFormat/ValveResourceFormat
Source 2 Viewer is an all-in-one tool to browse VPK archives, view, extract, and decompile Source 2 assets, including maps, models, materials, textures, sounds, and more.
|
|
|
ktxiaok/FireAxe
A Left 4 Dead 2 addon manager that supports hierarchical organization, workshop items and collections download, addon enablement management, etc.
|
| Version | Downloads | Last Updated |
|---|---|---|
| 7.2.0.221 | 126 | 10/21/2026 |
| 7.1.0.211 | 424 | 10/19/2026 |
| 7.0.0.190 | 263 | 10/17/2026 |
| 6.0.0.182 | 4,106 | 9/7/2026 |
| 5.0.2.177 | 8,944 | 8/8/2026 |
| 4.0.0.142 | 25,695 | 11/11/2025 |
| 3.0.3.132 | 2,798 | 9/22/2025 |
| 2.0.1.107 | 15,950 | 5/9/2024 |
| 2.0.0.101 | 536 | 3/22/2024 |
| 1.8.0.93 | 1,121 | 2/13/2024 |
| 1.7.0.88 | 2,250 | 1/7/2024 |
| 1.6.2.76 | 7,629 | 11/23/2023 |
| 1.6.1.71 | 3,681 | 9/8/2023 |
| 1.6.0.67 | 394 | 9/5/2023 |
| 1.5.0.59 | 2,014 | 7/14/2023 |
| 1.4.0.53 | 2,328 | 3/22/2023 |
| 1.3.0.33 | 3,837 | 4/29/2022 |
| 1.2.0.24 | 1,078 | 1/15/2022 |
| 1.1.0.1 | 5,566 | 11/23/2021 |
| 1.0.2.35 | 3,077 | 6/23/2020 |
View release notes at https://github.com/ValveResourceFormat/ValvePak/releases