A Rust library and command-line tool for reading, writing, and manipulating Valve Pak (VPK) files used by Valve's Source engine games.
- Full VPK Format Support: Both VPK v1 and v2 formats
- Unified API: Single struct handles both reading and writing operations
- Checksum Verification: MD5 checksum verification for VPK v2 files
- High Performance: Efficient I/O with buffered readers/writers and streaming
- Memory Safe: Written in Rust with comprehensive error handling
- CLI Tool: Complete command-line interface for common operations
- File-like Access: VPKFile implements standard Read/Seek traits
git clone <repository-url>
cd valve-pak-rs
cargo build --release
The compiled binary will be available at target/release/valve_pak
.
Add to your Cargo.toml
:
cargo add valve_pak
valve_pak pack <directory> <output.vpk> [--verbose]
Example:
valve_pak pack my_mod/ my_mod.vpk --verbose
valve_pak unpack <input.vpk> <output_directory> [--verbose]
Example:
valve_pak unpack game_assets.vpk extracted/ --verbose
valve_pak list <input.vpk> [--detailed]
Examples:
valve_pak list game_assets.vpk
valve_pak list game_assets.vpk --detailed # Shows file sizes and CRC32
valve_pak verify <input.vpk>
Example:
valve_pak verify game_assets.vpk
valve_pak extract <input.vpk> <file_path> <output_file>
Example:
valve_pak extract game_assets.vpk scripts/game.txt extracted_game.txt
use valve_pak::{VPK, Result};
fn main() -> Result<()> {
// Open an existing VPK file
let vpk = VPK::open("game_assets.vpk")?;
// List all files
for file_path in vpk.file_paths() {
println!("{}", file_path);
}
// Get a specific file
let mut file = vpk.get_file("scripts/game.txt")?;
// Read file content
let content = file.read_all_string()?;
println!("File content: {}", content);
// Save file to disk
file.save("extracted_game.txt")?;
Ok(())
}
use valve_pak::{VPK, Result};
fn main() -> Result<()> {
// Create VPK from directory
let vpk = VPK::from_directory("my_mod/")?;
// Save to file
vpk.save("my_mod.vpk")?;
println!("Packed {} files", vpk.file_count());
Ok(())
}
use valve_pak::{VPK, Result};
use std::io::{Read, Seek, SeekFrom};
fn main() -> Result<()> {
let vpk = VPK::open("game_assets.vpk")?;
let mut file = vpk.get_file("textures/logo.png")?;
// VPKFile implements Read and Seek traits
let mut buffer = [0u8; 1024];
let bytes_read = file.read(&mut buffer)?;
// Seek to position
file.seek(SeekFrom::Start(100))?;
// Verify file integrity
if file.verify()? {
println!("File checksum is valid");
}
Ok(())
}
use valve_pak::{VPK, Result};
fn main() -> Result<()> {
match VPK::open("nonexistent.vpk") {
Ok(vpk) => {
println!("Opened VPK with {} files", vpk.file_count());
}
Err(e) => {
eprintln!("Failed to open VPK: {}", e);
// Error context is preserved through the chain
for cause in e.chain() {
eprintln!(" Caused by: {}", cause);
}
}
}
Ok(())
}
- Basic file tree structure
- CRC32 checksums for individual files
- No global checksums
- Extended header with metadata
- MD5 checksums for entire archive
- Support for chunk hashes
- Backward compatible with V1
The library is optimized for performance:
- Streaming I/O: Uses buffered readers/writers for efficient file operations
- Lazy Loading: File tree is loaded on demand when needed
- Memory Efficient: Large files are streamed rather than loaded entirely into memory
- Zero-Copy: Minimal data copying during read operations
All operations return Result<T>
types with descriptive error messages using the anyhow
crate. Errors include full context chains to help with debugging.
Run the test suite:
cargo test
Run tests with output:
cargo test -- --nocapture
anyhow
- Error handling with contextclap
- Command line argument parsingmd5
- MD5 checksum calculation (VPK v2)crc32fast
- Fast CRC32 calculationwalkdir
- Recursive directory traversal
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Submit a pull request