BSP files can contain embedded files in an internal ZIP archive called the pakfile. BSPSource can extract these files with optional smart filtering to exclude VBSP-generated content.
Understanding Pakfiles
The pakfile is a ZIP archive embedded in the BSP’s LUMP_PAKFILE lump. It typically contains:
- Custom materials (.vmt, .vtf)
- Custom models (.mdl, .vvd, .vtx, .phy)
- Custom sounds (.wav, .mp3)
- Particle manifests
- VBSP-generated files (cubemaps, lighting data)
config.unpackEmbedded = true;
config.smartUnpack = false; // Extract everything
This extracts the entire pakfile contents to the output directory.
You can also extract the raw pakfile ZIP:
PakFile pakFile = new PakFile(bspFile);
pakFile.unpack(outputPath, true); // Direct ZIP extraction
Smart Unpacking
Smart unpacking filters out files automatically generated by VBSP, keeping only custom content:
config.unpackEmbedded = true;
config.smartUnpack = true; // Default: filter VBSP files
VBSP-Generated Files
BSPSource automatically detects and excludes these patterns:
VHV Files (HDR lighting data):
sp_0.vhv
sp_1.vhv
sp_hdr_0.vhv
sp_hdr_1.vhv
Cubemap VTF Files:
c-64_128_256.vtf
c-64_128_256.hdr.vtf
Default Cubemaps:
cubemapdefault.vtf
cubemapdefault.hdr.vtf
Smart unpacking uses regex patterns to identify VBSP-generated files, preserving only mapper-created custom content.
Custom File Filtering
You can provide custom extraction filters:
PakFile pakFile = new PakFile(bspFile);
// Extract only materials
pakFile.unpack(outputPath, fileName ->
fileName.endsWith(".vmt") || fileName.endsWith(".vtf")
);
// Extract only models
pakFile.unpack(outputPath, fileName ->
fileName.endsWith(".mdl") ||
fileName.endsWith(".vvd") ||
fileName.endsWith(".vtx") ||
fileName.endsWith(".phy")
);
// Exclude specific directories
pakFile.unpack(outputPath, fileName ->
!fileName.startsWith("maps/")
);
Path Traversal Protection
BSPSource prevents malicious pakfiles from writing outside the extraction directory:
// Blocked: ../../../etc/passwd
// Allowed: materials/custom/wall.vtf
Files attempting path traversal are automatically skipped with warnings.
Invalid Path Handling
Files with invalid characters in their paths are skipped:
// Skipped: "file\x00name.txt" (contains null byte)
Duplicate File Protection
Existing files are never overwritten:
// If materials/wall.vtf exists, extraction is skipped
LZMA Compression Support
BSPSource supports LZMA-compressed pakfile entries (used in some Source 2007+ games):
- Automatic LZMA detection
- Proper decompression with EOS marker handling
- Fallback for unsupported compression methods
Detection Functions
You can check if a file is VBSP-generated:
boolean isGenerated = PakFile.isVBSPGeneratedFile("c-64_128_256.vtf");
// Returns: true
boolean isGenerated = PakFile.isVBSPGeneratedFile("materials/custom/wall.vtf");
// Returns: false
Detection patterns:
// VHV pattern: sp(_hdr)?_\d+\.vhv
Pattern.compile("sp(_hdr)?_\\d+\\.vhv")
// Cubemap pattern: c(-?\d+)_(-?\d+)_(-?\d+)(\.hdr)?\.vtf
Pattern.compile("c(-?\\d+)_(-?\\d+)_(-?\\d+)(\\.hdr)?\\.vtf")
// Exact matches:
// - cubemapdefault.vtf
// - cubemapdefault.hdr.vtf
Configuration Examples
BspSourceConfig config = new BspSourceConfig();
config.unpackEmbedded = true;
config.smartUnpack = true; // Filter out VBSP files
BspSourceConfig config = new BspSourceConfig();
config.unpackEmbedded = true;
config.smartUnpack = false; // Include VBSP files
BspSourceConfig config = new BspSourceConfig();
config.unpackEmbedded = false;
Pakfile Structure
Typical pakfile contents:
pakfile.zip
├── materials/
│ ├── custom/
│ │ ├── wall.vmt
│ │ └── wall.vtf
│ └── maps/
│ └── mapname/
│ ├── c-64_128_256.vtf (VBSP)
│ └── cubemapdefault.vtf (VBSP)
├── models/
│ └── props/
│ ├── custom.mdl
│ ├── custom.vvd
│ └── custom.vtx
├── sound/
│ └── custom/
│ └── sound.wav
└── sp_0.vhv (VBSP)
With smartUnpack = true, only the custom files are extracted (excluding VBSP-generated files).
Error Handling
Missing Pakfile
try (ZipFile zip = pakFile.getZipFile()) {
// Extract files
} catch (IOException ex) {
// Pakfile broken or missing
}
Unsupported Compression
Files with unsupported compression methods are logged:
Cannot extract unsupported: file.txt| method: IMPLODE(6)| encryption: false
Invalid Entry Names
Skipped invalid_chars\x00.txt (contains invalid characters)
After extraction, custom content is organized by type:
output/
├── materials/
│ └── custom/
├── models/
│ └── props/
└── sound/
└── custom/
You can then:
- Copy to your game’s directory structure
- Package into a custom content pack
- Include in a recompiled map
Advanced Usage
PakFile pakFile = new PakFile(bspFile);
// Materials only
pakFile.unpack(materialsPath, name ->
(name.endsWith(".vmt") || name.endsWith(".vtf")) &&
!PakFile.isVBSPGeneratedFile(name)
);
// Models only
pakFile.unpack(modelsPath, name ->
name.startsWith("models/") &&
(name.endsWith(".mdl") || name.endsWith(".vvd") ||
name.endsWith(".vtx") || name.endsWith(".phy"))
);
Get ZipFile for Manual Processing
PakFile pakFile = new PakFile(bspFile);
try (ZipFile zip = pakFile.getZipFile()) {
for (Enumeration<ZipArchiveEntry> e = zip.getEntries(); e.hasMoreElements();) {
ZipArchiveEntry entry = e.nextElement();
// Custom processing
}
}
Some protected maps may include encrypted entity data (entities.dat) in the pakfile. See Protection Detection for details.