Verified on August 9, 2026 with Contractors Showdown: ExfilZone 1.108.0 (
oculus-5.5.3-release-1.108.0-v76.0-CL-0). This workflow only reads PAK files and writes copies to a separate directory. Do not runpackor replace game files.
This is the expanded extraction workflow behind our ammunition, armor and weapon report. The first version covered ammunition and armor from pakchunk1. It now also covers receivers, magazines and weapon curves from pakchunk0, plus catalog and compatibility tables from pakchunk1.
What you will extract
The isolated workspace will contain:
- 81 ammunition-profile pairs;
- 38 helmet pairs, 36 vest pairs and 7 global armor curves;
- 61 receiver pairs and 90 magazine pairs;
- 6 catalog, dependency and compatibility tables;
- 32 weapon DOD/velocity curves resolved from receiver references;
.uasset+.uexppairs and JSON copies of structured assets.
For the verified snapshot this is 351 pairs, or 702 source files. Counts may legitimately change after a patch, so compare the paths as well as the totals.
The .uasset contains the header, name tables and object description. The main serialized payload is often in the adjacent .uexp, so you must keep both files with the same base name.
Step 1. Locate the game directory
The default Meta Horizon path is:
C:\Program Files\Meta Horizon\Software\Software\caveman-game-production-contractors-showdown-hrjitv\Contractors_Showdown
Older Oculus installations may start with C:\Program Files\Oculus\Software\Software\....
The correct root contains:
Content\Paks\pakchunk0-Windows.pak
Content\Paks\pakchunk1-Windows.pak
Binaries\Win64\Contractors_Showdown.exe
pakchunk1 contains Warfare data: ammunition, armor and tables. pakchunk0 contains the weapon layer: receivers, magazines and referenced curves.
Step 2. Create an isolated workspace
Never unpack over the game installation:
D:\ExfilZoneExtract\
├─ tools\
├─ extracted\pakchunk0\
└─ extracted\pakchunk1\
Step 3. Download the tools
Pinned versions used for the analysis:
- repak CLI v0.2.3 — PAK listing and selective extraction.
- UAssetGUI v1.1.0 — converts structured assets to JSON.
oo2core_9_win64.dllfrom WorkingRobot/OodleUE — Oodle decompression support.
Put all three in D:\ExfilZoneExtract\tools. The DLL must be next to repak.exe.
| File | SHA-256 of the verified version |
|---|---|
| repak v0.2.3 ZIP | 6720d602144d75df477a99d5bedb6ea780997546afc335901d4937cafeaa73fa |
repak.exe |
fcd538e5994b9bb833622d425ae346f4e0692f02d4b0025114a559f9b6286022 |
UAssetGUI.exe |
b7d75c0893f1a60e565853ae638bc21f2416cd12c2d9d854e297abb87ceb3263 |
oo2core_9_win64.dll |
6f5d41a7892ea6b2db420f2458dad2f84a63901c9a93ce9497337b16c195f457 |
Get-FileHash "D:\ExfilZoneExtract\tools\repak.exe" -Algorithm SHA256
Get-FileHash "D:\ExfilZoneExtract\tools\UAssetGUI.exe" -Algorithm SHA256
Get-FileHash "D:\ExfilZoneExtract\tools\oo2core_9_win64.dll" -Algorithm SHA256
Do not disable antivirus protection for a file from an unknown source.
Step 4. Set paths and verify both PAK files
$GameRoot = "C:\Program Files\Meta Horizon\Software\Software\caveman-game-production-contractors-showdown-hrjitv\Contractors_Showdown"
$Work = "D:\ExfilZoneExtract"
$Repak = Join-Path $Work "tools\repak.exe"
$UAssetGUI = Join-Path $Work "tools\UAssetGUI.exe"
$PakWeapons = Join-Path $GameRoot "Content\Paks\pakchunk0-Windows.pak"
$PakWarfare = Join-Path $GameRoot "Content\Paks\pakchunk1-Windows.pak"
$OutWeapons = Join-Path $Work "extracted\pakchunk0"
$OutWarfare = Join-Path $Work "extracted\pakchunk1"
foreach ($Path in @($Repak, $UAssetGUI, $PakWeapons, $PakWarfare)) {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "File not found: $Path"
}
}
New-Item -ItemType Directory -Force -Path $OutWeapons, $OutWarfare | Out-Null
The source PAK files are read only.
Step 5. Create two independent indexes
$IndexWeapons = & $Repak list $PakWeapons
if ($LASTEXITCODE -ne 0) { throw "Failed to list pakchunk0" }
$IndexWarfare = & $Repak list $PakWarfare
if ($LASTEXITCODE -ne 0) { throw "Failed to list pakchunk1" }
$IndexWeapons | Set-Content -Encoding utf8 (Join-Path $Work "pakchunk0-index.txt")
$IndexWarfare | Set-Content -Encoding utf8 (Join-Path $Work "pakchunk1-index.txt")
"pakchunk0: $($IndexWeapons.Count) entries"
"pakchunk1: $($IndexWarfare.Count) entries"
Snapshot 1.108.0 had 42,214 entries in pakchunk0 and 27,203 in pakchunk1. Paths inside a PAK use / even on Windows.
Step 6. Test one asset pair
$Asset = "Contractors_Showdown/Content/Blueprints/GameModes/Warfare/Ammo/BulletProfiles/12Gauge/12Gauge_DamageType_Buckshot7.uasset"
$Payload = $Asset -replace '\.uasset$', '.uexp'
& $Repak unpack -q -f -o $OutWarfare -i $Asset -i $Payload $PakWarfare
if ($LASTEXITCODE -ne 0) { throw "Test extraction failed" }
Test-Path -LiteralPath (Join-Path $OutWarfare $Asset)
Test-Path -LiteralPath (Join-Path $OutWarfare $Payload)
Both checks must return True.
Step 7. Select data from pakchunk1
$AmmoPrefix = "Contractors_Showdown/Content/Blueprints/GameModes/Warfare/Ammo/BulletProfiles/"
$HelmetPattern = '^Contractors_Showdown/Content/Blueprints/GameModes/Warfare/Helmet/(?:.*/)?WarfareHelmet_[^/]+\.(uasset|uexp)$'
$VestPattern = '^Contractors_Showdown/Content/Blueprints/GameModes/Warfare/TecVest/(?:.*/)?WarfareTecVest_[^/]+\.(uasset|uexp)$'
$ArmorCurvePattern = '/(PenetrationChangeCurveHelmet|PenetrationChangeCurveVest|AntiPenetrationDurabilityScalarCurveLv[2-6])\.(uasset|uexp)$'
$TablePattern = '/(AmmoBoxTable|WarfarePropDependencyTable|GunSmithBlackDataTable|GunSmithTypeDataTable|GunSmithGunPartDataTable|TagCheckDataTable)\.(uasset|uexp)$'
$IncludesWarfare = @($IndexWarfare | Where-Object {
($_ -like "$AmmoPrefix*.uasset") -or
($_ -like "$AmmoPrefix*.uexp") -or
($_ -match $HelmetPattern) -or
($_ -match $VestPattern) -or
($_ -match $ArmorCurvePattern) -or
($_ -match $TablePattern)
} | Sort-Object -Unique)
$IncludesWarfare | Set-Content -Encoding utf8 (Join-Path $Work "selected-pakchunk1.txt")
"pakchunk1: selected $($IncludesWarfare.Count) files"
Build 1.108.0 produces 336 files: the original 324 ammunition and armor files plus 12 files for six tables.
Step 8. Select receivers and magazines from pakchunk0
$ReceiverPattern = '/AdvanceGun/.+/GunParts?/LowerReceiver_[^/]+\.(uasset|uexp)$'
$MagazinePattern = '/AdvanceGun/.+/GunParts?/Mag_[^/]+\.(uasset|uexp)$'
$IncludesWeapons = @($IndexWeapons | Where-Object {
(($_ -match $ReceiverPattern) -and ($_ -notlike '*/GrabInteractionActor/*')) -or
($_ -match $MagazinePattern)
} | Sort-Object -Unique)
$IncludesWeapons | Set-Content -Encoding utf8 (Join-Path $Work "selected-pakchunk0-base.txt")
"pakchunk0 without curves: selected $($IncludesWeapons.Count) files"
Build 1.108.0 produces 302 files: 61 receiver pairs and 90 magazine pairs.
Step 9. Extract selections in batches
function Expand-PakSelection {
param(
[string[]]$Entries,
[string]$Pak,
[string]$Out
)
for ($Start = 0; $Start -lt $Entries.Count; $Start += 28) {
$Last = [Math]::Min($Start + 27, $Entries.Count - 1)
$Args = @("unpack", "-q", "-f", "-o", $Out)
foreach ($Entry in @($Entries[$Start..$Last])) {
$Args += @("-i", $Entry)
}
$Args += $Pak
& $Repak @Args
if ($LASTEXITCODE -ne 0) { throw "repak failed at index $Start" }
}
}
Expand-PakSelection -Entries $IncludesWarfare -Pak $PakWarfare -Out $OutWarfare
Expand-PakSelection -Entries $IncludesWeapons -Pak $PakWeapons -Out $OutWeapons
Step 10. Verify pairs and create JSON
$MissingPairs = Get-ChildItem -LiteralPath $OutWeapons, $OutWarfare -Recurse -File -Filter *.uasset | ForEach-Object {
$Pair = [System.IO.Path]::ChangeExtension($_.FullName, ".uexp")
if (-not (Test-Path -LiteralPath $Pair -PathType Leaf)) { $_.FullName }
}
if ($MissingPairs) {
$MissingPairs
throw "Found .uasset files without adjacent .uexp files"
}
$JsonCandidates = Get-ChildItem -LiteralPath $OutWeapons, $OutWarfare -Recurse -File -Filter *.uasset | Where-Object {
$_.BaseName -notmatch '_DamageType_'
}
foreach ($Source in $JsonCandidates) {
$Json = [System.IO.Path]::ChangeExtension($Source.FullName, ".json")
& $UAssetGUI tojson $Source.FullName $Json VER_UE5_4
if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $Json)) {
throw "Conversion failed: $($Source.FullName)"
}
}
JSON files are written next to the extracted pairs and do not modify the game.
Step 11. Resolve referenced weapon curves
Receivers reference external CurveFloat objects for weapon falloff (DamageRangeCurve) and velocity scaling (BulletVelocityScalingCurve). Parse the receiver JSON first, then extract the referenced packages from pakchunk0. Include these two fallback assets as well:
Contractors_Showdown/Content/Core/VRInteractables/ZomboyGunSystem/AI/Curves/Pistol_AOD.uasset
Contractors_Showdown/Content/Core/VRInteractables/ZomboyGunSystem/AI/Curves/Sniper_AOD_Warfare.uasset
A manual investigation can collect imported CurveFloat names like this:
$CurveNames = @(
Get-ChildItem -LiteralPath $OutWeapons -Recurse -File -Filter "LowerReceiver_*.json" | ForEach-Object {
$Document = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
@($Document.Imports) | Where-Object { $_.ClassName -eq "CurveFloat" } | ForEach-Object { $_.ObjectName }
}
"Pistol_AOD"
"Sniper_AOD_Warfare"
) | Sort-Object -Unique
$CurveIncludes = @($IndexWeapons | Where-Object {
$_ -match '\.(uasset|uexp)$' -and
$CurveNames -contains [System.IO.Path]::GetFileNameWithoutExtension($_)
} | Sort-Object -Unique)
$CurveIncludes | Set-Content -Encoding utf8 (Join-Path $Work "selected-weapon-curves.txt")
Expand-PakSelection -Entries $CurveIncludes -Pak $PakWeapons -Out $OutWeapons
foreach ($CurveAsset in Get-ChildItem -LiteralPath $OutWeapons -Recurse -File -Filter *.uasset | Where-Object {
$CurveNames -contains $_.BaseName
}) {
$CurveJson = [System.IO.Path]::ChangeExtension($CurveAsset.FullName, ".json")
& $UAssetGUI tojson $CurveAsset.FullName $CurveJson VER_UE5_4
if ($LASTEXITCODE -ne 0) { throw "Curve conversion failed: $($CurveAsset.FullName)" }
}
This broad manual scan may include extra CurveFloat imports. Our analyzer narrows the list to receiver DOD/velocity fields and found 32 curves with 262 control points in the verified snapshot.
Step 12. Why ammunition uses a different parser
Ammunition profiles are unversioned class default objects of ZomboyAdvancedBulletDamageType. Without the complete schema, UAssetGUI may not label their values correctly. The specialized binary parser reads the .uexp payload in serialized order:
BulletCaliberandBulletAttribute;DamageOverDistance;PenetrationPowerOverDistance;MuzzleVelocity;BluntDamageScaleandBleedingChance;- penetrated and blunt gear-durability damage scalars.
Curve-key time is stored in centimeters; divide by 100 for meters. Velocity is stored in cm/s; divide by 100 for m/s. Nine profiles omit a serialized zero BluntDamageScale, so a naive float scan shifts the following fields.
Step 13. What to read from the other assets
- Armor: raw
Default__<asset name>_Cexport, penetration/damage curves, armor level, durability and durability-curve reference. - Receivers:
HeadDamageScale,DamageRangeCurve,FiringRate, legacyPenetrationPower/DamageType, ergonomics, velocity/drop and shotgun fields. - Magazines:
ClipSize; when an override is absent, a value inferred from the name or catalog must be marked derived rather than exact. TagCheckDataTable: part catalog and slot/tag requirements.WarfarePropDependencyTable: dependencies and bullet-to-gun-generator links.- Curves: every key and interpolation mode, not only the first and last points.
Do not multiply advanced bullet profiles by legacy receiver fields and call the result a confirmed final-damage formula. Their runtime composition order is still unresolved.
Source map
| Data | PAK | Path or rule |
|---|---|---|
| Ammunition | pakchunk1 |
Warfare/Ammo/BulletProfiles/<caliber>/* |
| Helmets and vests | pakchunk1 |
WarfareHelmet_*, WarfareTecVest_* |
| Armor curves | pakchunk1 |
PenetrationChangeCurve*, AntiPenetrationDurabilityScalarCurveLv2..Lv6 |
| Catalog and compatibility | pakchunk1 |
the six tables from step 7 |
| Receivers | pakchunk0 |
AdvanceGun/**/GunParts/LowerReceiver_* |
| Magazines | pakchunk0 |
AdvanceGun/**/GunParts/Mag_* |
| Weapon curves | pakchunk0 |
external CurveFloat objects referenced by receivers |
| Native names | EXE | Binaries/Win64/Contractors_Showdown.exe |
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| repak reports an Oodle error | Check oo2core_9_win64.dll next to repak.exe. |
A .uasset exists but UAssetGUI fails |
Check the matching .uexp and pass VER_UE5_4. |
| No receivers or magazines are found | You are probably searching pakchunk1; they are in pakchunk0. |
| Warfare tables are missing | The tables are in pakchunk1, not beside the receivers. |
| More than 32 curves are selected | The manual scan collected every CurveFloat; narrow it to receiver DOD/velocity references. |
| A field is absent from JSON | A Blueprint may inherit the value; absence does not mean zero. |
| Ammunition values look incomplete | Unversioned profiles require the class schema or a specialized parser. |
Repeating the workflow after a patch
Never reuse old indexes. For every new snapshot, record:
- the version and SHA-256 of
Contractors_Showdown.exe; - SHA-256 for both PAK files;
pakchunk0-index.txtandpakchunk1-index.txt;- the three
selected-*.txtfiles; - normalized CSV files and fingerprints for extracted assets.
Compare path lists and hashes first, then parameters. A changed fingerprint without a recognized parameter is a potential new field, not a reason to ignore the asset.
Extracted .uasset and .uexp files are game materials. Keep them local for research and do not redistribute the assets. Publish methodology, normalized tables and your own findings instead.