moonst3ad
OFFLINE
RU
Contractors Showdown: ExfilZone
// Contractors Showdown: ExfilZone

How to extract .uasset and .uexp from ExfilZone (2026-08-09 — patch 1.108.0)

Safely extract ExfilZone data from pakchunk0 and pakchunk1: ammunition, armor, receivers, magazines, compatibility tables and weapon curves.

EXFILZONEPCVRUNREAL ENGINEDATA MINING

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 run pack or 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 + .uexp pairs 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:

  1. repak CLI v0.2.3 — PAK listing and selective extraction.
  2. UAssetGUI v1.1.0 — converts structured assets to JSON.
  3. oo2core_9_win64.dll from 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:

  1. BulletCaliber and BulletAttribute;
  2. DamageOverDistance;
  3. PenetrationPowerOverDistance;
  4. MuzzleVelocity;
  5. BluntDamageScale and BleedingChance;
  6. 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>_C export, penetration/damage curves, armor level, durability and durability-curve reference.
  • Receivers: HeadDamageScale, DamageRangeCurve, FiringRate, legacy PenetrationPower/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.txt and pakchunk1-index.txt;
  • the three selected-*.txt files;
  • 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.

Tool sources