wowlib reads and writes World of Warcraft client files — a modern C++26 core, automatically bound to Python (and, later, Lua) by welder. It exists to make community tooling — map viewers, model viewers, Blender add-ons, exploration projects — possible on top of one shared, well-tested file layer.
- Filesystem gateway — one interface over MPQ (pre-WoD clients, via StormLib) and CASC (WoD+ clients, via CascLib), with client patch-chain resolution, listfile / FileDataID machinery and a project-directory overlay for modding workflows.
- Reflection-driven format framework — every chunked format is declared once as annotated C++26 structs; parsing, writing, per-expansion version ranges, the Python bindings and the documentation are all derived from that single declaration.
- Round-trip guarantees — formats whose containers allow it are
byte-perfect (
write(read(bytes)) == bytes); offset-table formats are semantic (read(write(x)) == x, canonical relayout on write). Unknown and undocumented chunks are preserved verbatim, never dropped. - ClientDB — every database container era from WDBC through WDC5, with typed record structs for 1221 tables generated from WoWDBDefs across all supported client versions.
- Typed Python bindings — a stable-ABI (
abi3) extension for CPython ≥ 3.13 with rich.pyistubs and zero-copy NumPy views over WoW vectors. MSVC-ABI-compatible builds for use inside Blender on Windows.
wowlib targets the last minor release of every major expansion, Vanilla through The War Within. Formats are modelled for all versions in one entity; the matrix below shows how far each has been verified:
- ✅ — implemented, round-trip verified against a real client's file corpus
- 🟡 — implemented, awaiting a client install to verify against
- ➖ — not applicable to that client
✅ file-format cells are backed by the nightly exhaustive audit: every file of every format in every installed client (~3.4M files) is round-tripped, with the handful of shipped-corrupt originals catalogued in the audit report artifact.
| Format | Round-trip | Vanilla 1.12.1 |
TBC 2.4.3 |
WotLK 3.3.5a |
Cata 4.3.4 |
MoP 5.4.8 |
WoD 6.2.3 |
Legion 7.3.5 |
BfA 8.3.7 |
SL 9.2.7 |
DF 10.2.7 |
TWW 11.2.7 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| WMO (root + groups) | byte-perfect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
M2 (+ .skin .anim .skel .bone)¹ |
semantic | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
| ADT (root + split files)² | semantic | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
WDT (+ _occ _lgt _fogs _mpv) |
byte-perfect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
| WDL | byte-perfect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
| BLP³ | byte-perfect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
| DBC / DB2⁴ | byte-perfect (WDBC/WDB2), semantic (WDC*) | ✅ WDBC | ✅ WDBC | ✅ WDBC | ✅ WDB2 | 🟡 WDB2 | 🟡 WDB2 | 🟡 WDC1 | 🟡 WDC3 | ✅ WDC3 | 🟡 WDC5 | 🟡 WDC5 |
| MPQ storage & patch chain⁵ | — | ✅ | ✅ | ✅ | ✅ | ✅ | ➖ | ➖ | ➖ | ➖ | ➖ | ➖ |
| CASC storage | — | ➖ | ➖ | ➖ | ➖ | ➖ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟡 |
¹ .phys is carried as an opaque blob for now (structured records planned).
Semantic round-trip means the writer relays out offset tables canonically, so
output is equivalent, not byte-identical.
² Monolithic (WotLK-) and split (Cata+ _tex0/_obj0/_obj1/_lod) layouts
both supported; _obj1/_lod LOD chunks currently round-trip verbatim as raw
chunks, structured access planned.
³ BLP is identical across all client versions; verification spans every
installed client's full texture corpus. JPEG-encoded BLPs round-trip verbatim
but do not decode.
⁴ WDC4 (10.1–10.2.5) is implemented as well. Encrypted (TACT) DB2 sections are
preserved and reported; key injection is wired but not yet exercisable locally.
⁵ The 3.3.5a patch chain was verified against the client binary itself
(reverse-engineered); the 4.3.4 and 5.4.8 UpdateChain-era tables have
dedicated integration tests against their installs.
WoW Classic Era, the Classic progression realms (BCC, WotLK, Cataclysm, MoP
Classic) and the Anniversary realms are not old clients — they are the
modern client rebuilt from whatever retail branch was current, shipping
old-looking content. Classic Era 1.15.9 is a Midnight-era CASC client; Cata
Classic 4.4.2 writes War Within-era files. One version number can even span
two engines (4.4.0 shipped on Dragonflight and, months later, on The War
Within), and wow_classic_titan calls itself 3.80.
wowlib keys these off the build number — Blizzard's counter is global
across every product — via a flavor on ClientVersion and the
format_lineage it implies, so a Classic client resolves to the retail column
above that its files actually match. ClientInstall.detect reads the exact
version and product code off an installation. No Classic install is available
locally, so the mapping is covered by unit tests rather than a file corpus.
Structured M2 .phys records · structured ADT _obj1/_lod (and blend-mesh)
chunks · TACT decryption of encrypted DB2 sections · cross-version format
conversion (scaffolding only) · Lua bindings (annotated throughout, module
deferred) · hotfix caches (DBCache.bin) · standalone formats not yet started
(.tex, .lit, .wlw/.wlq/.wlm, …).
Prebuilt packages need no toolchain — the compiler requirement below is only for building from source:
pip install wowlib-py # then: import wowlib
dotnet add package Wowlib # C#/.NET(The PyPI distribution is wowlib-py because wowlib there belongs to an
unrelated, long-abandoned project; the module you import is wowlib.)
Building from source requires gcc ≥ 16 (currently the only toolchain implementing C++26 reflection); on macOS specifically, gcc ≥ 16.2 — 16.1's Darwin port silently corrupts string literals placed after weak definitions (GCC PR 126723), which reflection-heavy code hits constantly, and the configure refuses it. All dependencies are fetched by CMake — no manual installs.
# C++ library + tests
cmake --preset gcc16-debug
cmake --build --preset gcc16-debug
ctest --preset gcc16-debug
# Python extension (stable-ABI, CPython ≥ 3.13) + .pyi stubs
cmake --preset gcc16-bindings
cmake --build --preset gcc16-bindings
# …or straight into your environment:
pip install -e ".[dev]"import wowlib
from wowlib.formats import wmo
root = wmo.WMOWotlk.read(open("Building.wmo", "rb").read())
for material in root.materials:
...
group = wmo.WMOWotlk.read_group(group_data)
verts = group.vertices # zero-copy numpy.ndarray view#include <wowlib/formats/wmo/wmo.hpp>
auto root = wowlib::formats::wmo::WMO<Expansion::Wotlk>::read(bytes);
for (auto const& mat : root.materials()) { /* … */ }The unit suite (Catch2 + pytest) runs anywhere — that is what CI runs. Integration tests validate round-trips against real client installs and self-skip unless you point them at one:
export WOWLIB_TEST_CLIENTS_DIR=/path/to/your/clients # folders with WoW installs
export WOWLIB_TEST_LISTFILE=/path/to/community-listfile.csv
ctest --preset gcc16-debugThe docs site (guide + typed Python API + Doxygen C++ reference) is hosted at skarndev.github.io/wowlib — start with Getting started. To build it locally:
pip install ".[docs]"
python docs/build.py serve # live-reload at http://127.0.0.1:8000/- welder — the reflection-based binding generator that produces wowlib's Python (and future Lua) surface.
- wowdev.wiki — the community file-format documentation this library is checked against (and occasionally corrects).
- WoWDBDefs — column definitions for every client database table.
- pywowlib / WoWLib — earlier iterations of this effort, superseded by this library.
MIT.