wadup is a command-line toolkit for reshaping Doom-engine WAD/PK3 archives: bundling a map with only the resources it actually uses, converting between WAD and PK3 layouts, and renaming textures/flats/patches so they can't collide with another WAD loaded alongside them.
It understands both classic binary formats (TEXTURE1/TEXTURE2, PNAMES, SIDEDEFS/SECTORS) and ZDoom's text-based ones (TEXTURES, TEXTMAP/UDMF, ANIMDEFS, SWITCHES, SNDINFO), and keeps their cross-references in sync whenever it renames or repacks something.
Requires Go 1.22+. No external runtime dependencies - it's a static binary.
git clone https://github.com/indrora/wadup.git
cd wadup
go buildThis produces a wadup (or wadup.exe on Windows) binary in the current directory.
| Command | Purpose |
|---|---|
mkstandalone |
Merge map WAD(s) with only the resources they reference from one or more resource archives, optionally renaming everything to avoid collisions |
mkwad |
Pack a PK3-structured directory (or PK3 file) into a single WAD |
wad2pk3 |
Unpack a WAD into a PK3 (or a loose directory), decoding graphics to PNG |
uniqify |
Rename only the entries in a WAD that collide with a given set of other WADs/PK3s |
Every command accepts -v (repeatable) to raise log verbosity: none is warnings-only, -v adds info, -vv adds more detail, -vvv is debug, -vvvv also turns on byte-level tracing inside the WAD/format decoders. Run wadup <command> --help for the full flag reference at any time.
Takes one or more map WADs and one or more resource archives, and produces a single WAD containing every map plus exactly the textures, flats, and patches those maps reference - nothing more. The output no longer depends on the resource archives at runtime.
wadup mkstandalone --map mymap.wad --res OTEX.wad -o standalone.wad--map(repeatable, at least one required) - a map WAD/PK3/directory. Every map it contains, and any resource lumps it already carries, is always included in the output.--res(repeatable) - a resource WAD/PK3/directory to pull missing textures/flats from, searched in the order given; the first one that defines a name wins.-o, --output(required) - the output WAD path.--png- re-encode every flat/patch/sprite/simple-texture/graphic lump as PNG (grAb offsets preserved). Composite TEXTURE1/TEXTURE2 definitions stay binary metadata; the patches they're built from are converted individually.-g, --game- stock palette to fall back on for--pngwhen the output has no PLAYPAL of its own (doom,heretic,hexen,strife; defaultdoom).
Textures, flats, and their composite-texture patches are resolved recursively: if a texture is built from patches, those patches get pulled in too, wherever they live among the --res archives. Anything that can't be resolved is logged as a warning rather than failing the build - it'll just show up as a missing-texture placeholder in-game. SWITCHES pairs and ANIMATED/ANIMDEFS animation chains that involve a used texture/flat are pulled in the same way (see How renaming actually works for how those two end up represented in the output).
By default, names keep whatever they were called in the map/resource archives. Two flags make every name unique instead, so this WAD can be loaded next to any other without stepping on its textures:
--uniq- replaces every texture/flat/patch name physically present in the output with a deterministicPREFIX+ CRC-16 hash name (the prefix is derived from the output filename). Opaque, but guaranteed collision-free.--res-prefix XXXX(1-4 characters) - overwrites the leading characters of every name withXXXXinstead, keeping the rest intact (SW1BRN1with--res-prefix MYPXbecomesMYPXRN1). Much more readable than--uniq. If two names still collide after the overwrite, the loser keeps as much of its original suffix as possible and only has its last character or two replaced with a disambiguator; a full CRC-16 hash is the last resort, and in practice should never actually happen.
--uniq and --res-prefix are two answers to the same problem - use one or the other, not both. Every reference is rewritten to match: map sublumps (SIDEDEFS/SECTORS or TEXTMAP), TEXTURE1/TEXTURE2, TEXTURES, PNAMES, SWITCHES, ANIMDEFS, and MAPINFO/ZMAPINFO's Sky1/Sky2. A composite texture's patch reference that isn't backed by a lump anywhere in --map or --res (most often something expected from the base IWAD) is left alone rather than renamed, since it isn't this WAD's resource to rename.
--no-clobber- narrows either scheme to only the names actually pulled in from a--resarchive. The map's own resources are left completely untouched, however they're named (a map-defined textureBINGOstaysBINGOinstead of becomingXXXINGO). Requires--uniqor--res-prefix.
Packs a ZDoom PK3-structured directory (or an existing PK3 archive) into a single WAD file, routing files into WAD namespaces by their folder (flats/ → F_START/F_END, patches/ → P_START/P_END, and so on) and folding any maps/<NAME>.wad entries into real map lumps.
wadup mkwad myproject/ -o myproject.wad-o, --output- output WAD path (defaults to<inputdir>.wad).--map(repeatable) - additional map WAD(s) whose maps get appended to the output.-c, --crushpng- recompress PNG lumps at maximum compression (grAb offsets preserved).-z, --compression- PNG recompression level 1-9 (0 leaves PNGs as-is).
Graphic lumps that are already PNG are carried over as-is - ZDoom-family engines read them natively, so there's no need to convert back to raw Doom picture format.
The reverse of mkwad: unpacks a WAD into a PK3 (or a loose directory tree), routing entries into the standard ZDoom folder layout and decoding flats/patches/sprites/textures/graphics to PNG (with grAb offsets preserved as a PNG chunk). Maps come out as maps/<NAME>.wad.
wadup wad2pk3 mymod.wad -o mymod.pk3-o, --output- output PK3 file or directory (defaults to<input>.pk3, or<input>_pk3with--dir).-d, --dir- write an unpacked directory tree instead of a zipped PK3.-t, --textures- convert binary TEXTURE1/TEXTURE2 + PNAMES into a ZDoom TEXTURES text lump before writing.-g, --game- stock palette to use when the input has no PLAYPAL of its own (doom,heretic,hexen,strife, plus aliasesdoom1/doom2/ultimate/freedoom; defaultdoom).-c, --crushpng/-z, --compression- same PNG recompression controls asmkwad.--keep-unknown- keep root lumps wadup can't otherwise classify as<name>.lmpinstead of dropping them.
Colors come from the WAD's own PLAYPAL if it has one; a PWAD that relies on its IWAD's palette needs --game set to the right one.
A narrower, older sibling of mkstandalone --uniq: renames only the entries in one WAD that collide with a given set of other WAD/PK3/directory "patches", instead of computing a fresh standalone build.
wadup uniqify input.wad output.wad other1.wad other2.pk3 ...Takes exactly INPUT OUTPUT PATCH... (at least one patch is required). The rename prefix is derived automatically from OUTPUT's basename (mymaps.wad → MYMA, short names are padded with X). Only names that actually collide get renamed - everything else in INPUT is left alone.
This does not understand scripts: if an ACS/SNDSEQ/etc. script references a lump by name and that lump gets renamed, the script keeps referring to the old (now different) resource. In that situation, either rename the resource by hand ahead of time or update the script afterward. mkstandalone avoids this class of problem for the lumps it knows how to parse (see the reference-rewriting list above), but neither tool touches compiled ACS bytecode.
Every rename scheme in wadup ultimately answers the same question - "what does resource X become?" - and then rewrites every place that name is used, not just the resource's own lump. A name can simultaneously be a texture, a flat, and a patch (a raw patch graphic doubling as its own simple texture is a common ZDoom pattern); when that happens, wadup assigns it exactly one new name and uses that everywhere, rather than letting each category drift to a different answer.
--uniq/--res-prefix (and uniqify's renaming) are tracked and rewritten across:
- Classic binary maps (SIDEDEFS texture fields, SECTORS floor/ceiling flats)
- UDMF text maps (TEXTMAP's
texture*sidedef and sector fields) - TEXTURE1/TEXTURE2 (binary composite textures) and TEXTURES (ZDoom's text equivalent), plus PNAMES
- SWITCHES, ANIMATED, and ANIMDEFS - whichever are present, each rewritten in its own native format
- MAPINFO/ZMAPINFO's
Sky1/Sky2
mkstandalone additionally normalizes animations as it assembles the output, before any of the above renaming happens: ANIMATED is always folded into ANIMDEFS (explicit frame-by-frame, never using ANIMDEFS' Range shorthand, since a range's in-between members are only implied by their position in the final texture directory - a position nothing guarantees survives merging and renaming). SWITCHES stays SWITCHES, just deduplicated. This mainly matters for mkstandalone; uniqify works on a single WAD as-is and never does this conversion.
Separately, whenever mkwad/mkstandalone folds a PK3 or directory into a WAD, SNDINFO's quoted PK3-style path references (e.g. "sounds/beepclunk.wav") get rewritten to whatever lump name that file actually ended up with - this is unrelated to --uniq/--res-prefix, and mainly matters when a filename is long enough that the 8-character lump name truncates it, which would otherwise silently break the reference.
| Lump / namespace | Read | Write |
|---|---|---|
| TEXTURE1 / TEXTURE2 (binary composite textures) | Yes | Yes |
| TEXTURES (ZDoom text composite textures) | Yes | Yes |
| PNAMES | Yes | Yes |
| SWITCHES (binary) | Yes | Yes |
| ANIMATED (binary) | Yes | mkstandalone always converts it to ANIMDEFS; uniqify renames it in place |
| ANIMDEFS (text) | Yes | Yes |
MAPINFO / ZMAPINFO (Sky1/Sky2 only) |
Yes | Yes |
| SNDINFO (quoted PK3-path references only) | Yes | Rewritten when folding a PK3/directory into a WAD; not part of --uniq/--res-prefix |
TX_START/TX_END, F_START/F_END (+ FF_ alias), P_START/P_END (+ PP_ alias), S_START/S_END (+ SS_ alias) |
Yes | Yes |
PK3 folders (textures/, flats/, patches/, sprites/, sounds/, music/, graphics/, maps/, ...) |
Yes | Yes |
| Map format | Support |
|---|---|
| Classic binary (SIDEDEFS/SECTORS) | Yes |
| UDMF (TEXTMAP) | Yes |
| Hexen-format THINGS/LINEDEFS, ACS BEHAVIOR/SCRIPTS | Carried through as opaque data; not parsed or rewritten |
- Names are limited to 8 characters, as Doom's format requires, and are matched case-insensitively.
--uniq's CRC-16 hash space is 65536 values; renaming everything (not just what collides) makes this a real, if small, risk at very large name counts. Collisions are detected and resolved with a salted retry, so uniqueness is always guaranteed - it just occasionally costs an extra hash.- Scripts (ACS bytecode, SNDSEQ, and similar) that reference a lump by name aren't rewritten when that lump is renamed, except where wadup explicitly parses the reference (see the list above).
uniqifyin particular makes no attempt at this. - PK3/directory inputs are expected to follow the standard ZDoom folder layout; nonstandard layouts may misclassify entries.
go build ./... # build everything
go test ./... # run the test suite
go vet ./... # static checksTests live alongside the code they cover (internal/doom/*_test.go, cmd/*_test.go) plus a set of root-level wadup_*_test.go files exercising the lower-level rewrite functions directly; _testdata/ holds real WAD/PK3 fixtures used for integration-level checks. See docs/TESTING.md for more background on the test layout (some of it predates the current subcommand structure and may lag behind).
main.go - entry point, delegates to cmd.Execute()
cmd/ - one file per subcommand (mkstandalone, mkwad, uniqify, wad2pk3) + root/shared flags
internal/doom/
├── archive.go - Archive/Entry model, WAD <-> PK3 reading/writing, renaming
├── standalone.go - mkstandalone's resource-resolution and pull-in logic
├── maps.go - map sublump handling (SIDEDEFS/SECTORS/TEXTMAP)
├── textures.go - TEXTURE1/2, TEXTURES, PNAMES
├── animations.go - SWITCHES, ANIMATED, ANIMDEFS
├── sndinfo.go - SNDINFO quoted-path rewriting
├── rename.go - CRC-16 hashing and the --uniq/--res-prefix schemes
├── palette.go - PLAYPAL / stock game palettes
├── flat.go, picture.go, png_grab.go - graphic <-> PNG conversion
├── pk3.go - PK3/directory path <-> lump name derivation
├── classify.go - root lump classification for wad2pk3
├── wad.go - raw WAD file I/O
└── crc16.go, trace.go - small supporting utilities
MIT - see LICENSE.