FAQ

Everything extracted as @bytes(...) — why?

No schema. Pass --game-root pointing at the game install, and make sure it actually contains the script packages (Core.u, Engine.u, the game's own .u files). Run schema-resolve on a known class to check the index:

ue3-tools --game-root /path/to/game schema-resolve engine "Class Engine.Actor"

package 'x' not in --game-root

The import graph reached a package your root doesn't cover. Point --game-root higher up, or check that DLC and patch directories are included. --verbose streams every miss as it happens.

Invalid file signature

Not a UE3 package, or it's encrypted/wrapped by the title. The tool requires the literal 0x9E2A83C1 tag at offset zero.

Corrupted pak

The header parsed but reported a non-positive name, export or import count. Usually a truncated file or a wrong offset — try upk-header to see how far it got.

Decompression fails or panics

Only LZO is implemented. Zlib and LZX packages are recognised but can't be inflated. A panic inside decompression means the chunk data didn't match its declared size — the file is likely truncated.

Textures extract with no .dds

Three common causes:

  • Unmapped pixel format. Only PF_DXT1, PF_DXT3, PF_DXT5, PF_A8R8G8B8, PF_G8, PF_BC5, PF_FloatRGBA are handled; anything else warns and skips.
  • Mips live in a .tfc that isn't under --game-root, or whose payload is compressed — compressed TFC payloads aren't inflated.
  • No resolvable mips at all, which the warning states explicitly.

My replacement .dds is rejected

Its top mip has to match one of the original mips' dimensions. To change resolution, also update SizeX and SizeY in the .uo to your DDS's top mip — that authorises replacing the whole mip list.

Can I replace audio?

Not through the packer. SoundNodeWave extracts to .ogg / .wav but has no injector.

Can I edit a class, add a function, or change UnrealScript?

No. Class, struct, enum and const .uo files are read-only reference material — pack-mod skips them. Bytecode is located and skipped during schema parsing, never decompiled or re-emitted.

type mismatch: original is Int but edit is string

By design. The packer won't coerce types, because a wrong guess produces a package the game will mis-deserialize. Match the original's form — quotes for strings, bare digits for numbers, 'name' for names, Enum::Value for enums, &Ref for object references.

struct has no field 'X' in the original export

That field held its default value, so the engine never serialized it. With --game-root the packer creates it from the schema; without one, it can't know the field's type and fails.

could not resolve object reference '&Foo'

The label was ambiguous or matched nothing. Use a fuller dotted path (Outer.Foo), or extern: for imports. If the target genuinely doesn't exist yet, the packer reserves a slot and records it in .newexports instead of failing.

Does pack-mod produce a modified .upk?

No, and pack is unimplemented. The output is per-export .bin blobs plus a .namemap, intended for a runtime loader such as CU3ML. Repacking a whole package is out of scope.

Do I have to ship the .namemap?

Yes. Property tags reference names by index, and almost any edit can append entries. Ship the .namemap from the same run as the .bin files.

Can the GUI extract?

No — it's a read-only inspector. It decodes headers, tables, schemas and hex, but extraction and packing are CLI-only.

Is it slow?

Yes, on large packages. Whole packages are held in memory, import resolution scans export tables linearly, and resolving one import can pull in and decompress another package. Running decompress once and working against the flattened file helps.

Will it work on my game?

Anything UE3-era with a package version inside the supported gate range should read. Console (byte-swapped) packages decompress, but cooked-for-console script layouts aren't wired up yet, so their meta-objects will mis-parse. Expect to add version constants for anything far from the Dishonored-era target.