ClientDB (DBC & DB2)¶
The client-side databases — every file under DBFilesClient/: maps, spells,
items, creature display info, taxi nodes… wowlib.db reads and writes all of
them through one runtime-schema table: the schema for any table of any
supported client era comes from the community
WoWDBDefs definitions baked into the
library as data, so nothing per-table is generated or compiled — and every
era of every table is available from the same class.
wowlib speaks every on-disk database format a last-minor client ships:
| Clients | .dbc |
.db2 |
|---|---|---|
| Vanilla / TBC / WotLK | WDBC | — |
| Cata / MoP / WoD | WDBC | WDB2 |
| Legion | WDBC leftovers | WDC1 |
| BfA / Shadowlands | — | WDC3 |
| Dragonflight | — | WDC4 / WDC5 |
| The War Within | — | WDC5 |
Reads sniff the file magic, so you never name the format; a loaded table re-emits the format it was read from, and a fresh table picks the era's native one.
The table surface¶
Everything lives on the generic table:
Table.open(name, version)resolves the era's schema by name (wowlib.db.table_names()lists what exists) and returns an empty table ready toreadany file of that table.table[i]is a liveRecordview — columns read and write as attributes (row.map_name), shaped by the column.table.column(name)hands out a whole column: a zero-copy numpy view for numeric columns, lists for strings.- Cell-level access (
get_int/set_string/…) addresses(row, column, element)directly — the shape renderers and bulk editors want.
Columns keep their WoWDBDefs names, converted to snake_case. Pre-Cataclysm
localized-string columns expose one slot per client language (the Record
attribute is the list[str] of slots; locstring_flags rides alongside);
Cataclysm onwards they are plain strings.
from wowlib import FileKey, versions
from wowlib.fs import FileSystem, FileSystemSettings
from wowlib.db import EncryptedPolicy, Table
settings = FileSystemSettings("/Games/WoW 3.3.5a", versions.wotlk)
with FileSystem.open(settings) as fs:
table = Table.open("Map", versions.wotlk)
table.read(fs, FileKey("DBFilesClient/Map.dbc"))
for row in table:
print(row.id, row.directory, row.map_name[0]) # slot 0 = enUS
names = table[0].map_name
names[0] = "Azeroth Reforged"
table[0].map_name = names
ids = table.column("id") # zero-copy numpy
data = table.write(EncryptedPolicy.Preserve) # bytes, or write(fs, key, …)
Round-trip guarantees¶
- WDBC / WDB2 (pre-Legion): byte-perfect — an unmodified table writes back identical bytes, string-block quirks included.
- WDC1/3/4/5: canonical re-encode — the write is a fresh, tightly packed encoding (bit widths, pallet/common compression and copy tables re-derived) that re-reads to equal values. Multi-section tables may reorder rows (records are id-keyed); single-section tables keep order.
Encrypted sections¶
Modern .db2 files can contain TACT-encrypted sections. Rows whose keys the
storage holds decode normally; rows behind missing keys are absent from
records and reported via encrypted_sections / fully_decoded. Register
keys with FileSystem.import_keys to
decode them. On write, EncryptedPolicy picks
between re-emitting a keyless file verbatim (Preserve, the default choice —
edits to decoded rows are not applied then) and writing a plain table of
just the decoded rows (Drop).
Shared value types¶
The table classes themselves live on the generic table; this renders only what they share.
Client-side database files (DBFilesClient): the runtime-schema Table and the value types it shares.
TableBase
¶
The common surface of every client-database table: decode (read), encode (write), validation, and the preserved decode state. Concrete tables add their typed records.
strings
property
¶
strings: StringBlock
The preserved string block the record string fields were decoded from; offsets never move, write() appends new strings past its end.
encrypted_sections
property
¶
encrypted_sections: list[EncryptedSection]
The encrypted sections skipped on read: their records are not in records, but the file re-writes them verbatim.
fully_decoded
property
¶
Whether the whole table decoded — false when encrypted sections were skipped.
read
¶
read(fs: FileSystem, key: FileKey) -> None
Decode a table file image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes
|
the whole file content |
required |
Returns:
| Type | Description |
|---|---|
None
|
nothing; raises on malformed input or a schema mismatch |
Load the table from a client filesystem.
Parameters:
| Name | Description | Default |
|---|---|---|
fs
|
the filesystem gateway |
required |
key
|
the file to read |
required |
Returns:
| Type | Description |
|---|---|
None
|
nothing; raises when loading fails |
write
¶
write(policy: EncryptedPolicy) -> bytes
write(fs: FileSystem, key: FileKey, policy: EncryptedPolicy) -> None
write(fs: FileSystem, key: FileKey) -> None
Serialize the table to a file image; a loaded table re-emits the format it was read from. policy decides how keyless encrypted sections are handled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policy
|
EncryptedPolicy
|
keyless-section handling (WDC only) |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
the file bytes |
Serialize the table into a client filesystem (project overlay); the target path's extension picks .dbc/.db2 in the mixed eras.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fs
|
the filesystem gateway |
required | |
key
|
the file to write |
required | |
policy
|
EncryptedPolicy
|
keyless-section handling (WDC only) |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
nothing; raises when saving fails |
validate
¶
Check the logical integrity contracts the records must satisfy to survive a write and load in the client: the primary key stays unique, and no string holds an embedded NUL the string block would truncate. write() never runs this.
Returns:
| Type | Description |
|---|---|
ValidationReport
|
every violated contract, in record order |
ensure_valid
¶
Validate and raise on the first error instead of returning a report — the assert-style face of validate().
Returns:
| Type | Description |
|---|---|
None
|
nothing; raises when validate() finds any error |
LocString8
¶
A localized string column of a pre-Cataclysm client database: one string per language slot in the client's fixed column order, plus the locale flags field. Use at()/set() to address slots by Locale.
values
property
¶
The language slot values in client column order (0 enUS, 1 koKR, 2 frFR, 3 deDE, 4 zhCN, 5 zhTW, 6 esES, 7 esMX, then ruRU/ptBR/itIT for 16-slot eras); empty for languages the file does not carry.
flags
property
writable
¶
The trailing locale flags field; a bitmask the client never reads, preserved verbatim.
LocString16
¶
A localized string column of a pre-Cataclysm client database: one string per language slot in the client's fixed column order, plus the locale flags field. Use at()/set() to address slots by Locale.
values
property
¶
The language slot values in client column order (0 enUS, 1 koKR, 2 frFR, 3 deDE, 4 zhCN, 5 zhTW, 6 esES, 7 esMX, then ruRU/ptBR/itIT for 16-slot eras); empty for languages the file does not carry.
flags
property
writable
¶
The trailing locale flags field; a bitmask the client never reads, preserved verbatim.
EncryptedPolicy
¶
Bases: IntEnum
How a client-database write handles keyless encrypted sections: Preserve re-emits the file's original bytes verbatim (encrypted content intact, edits to decoded rows NOT applied); Drop writes only the decoded rows as a plain unencrypted table, discarding the rows behind missing keys.
EncryptedSection
¶
An encrypted WDC section: its records are behind a TACT key wowlib does not hold, so they are absent from records.