Skip to content

Filesystem

wowlib.fs is the storage gateway: one interface over MPQ archives (pre-WoD retail clients) and CASC storages (WoD+ retail and every Classic client), plus the settings that configure a client mount. Open a storage, resolve paths or FileDataIDs, read bytes.

ClientInstall.detect / FileSystemSettings.detect read a CASC installation's own version and product code off disk — the reliable way to open a Classic client, whose build number, not its version number, decides which engine's files it carries.

FileSystem is a context manager: prefer with FileSystem.open(settings) as fs: — the block exit calls close() for you, exception or not. Outside a with block, pair open() with an explicit close() (see the examples on the class).

Client filesystem access: storage backends, listfile databases, the project-directory overlay and the FileSystem gateway.

ClientInstall

ClientInstall()
ClientInstall(path: str | PathLike, version: ClientVersion, casc_product: str)

What a client installation says about itself: the exact version, the flavor that follows from its product code, and the TACT product code itself.

Detecting beats hand-writing a ClientVersion for anything Classic. Those products ship new builds continuously and the build — not the 1.15 / 4.4 version number — decides which engine's file formats the install carries, so the build has to be right. detect() reads it from the files Blizzard's installer leaves behind.

Examples:

from wowlib.fs import ClientInstall, FileSystem, FileSystemSettings

install = ClientInstall.detect("/Games/World of Warcraft/_classic_era_")
print(install.version)         # 1.15.9.69109 (ClassicEra)
print(install.casc_product)    # 'wow_classic_era'

settings = FileSystemSettings(install.path, install.version,
                              casc_product=install.casc_product)
with FileSystem.open(settings) as fs:
    ...

path property writable

path: Path

The installation directory that was inspected — the one holding Data/, ready to hand to FileSystemSettings.

version property writable

version: ClientVersion

The exact installed version, flavor included.

casc_product property writable

casc_product: str

The exact TACT product code the installation records ('wow', 'wow_classic_era', 'wow_classic_ptr', ...) — which can be more specific than the flavor's default.

detect staticmethod

detect(client_path: str | PathLike) -> ClientInstall

Read a client installation's identity from the files its installer leaves behind: .flavor.info (the product code) beside Data/, and .build.info (the version table) there or one directory up, where a multi-flavor install keeps it.

Only works for CASC installations. MPQ-era clients (< 6.0) record no such thing, and neither do repacks that ship only Data/ — construct their ClientVersion directly, which is unambiguous anyway since those versions are not shared with any Classic product.

Parameters:

Name Description Default
clientPath

the installation directory holding Data/

required

Returns:

Type Description
ClientInstall

the detected installation, or NotSupported when the directory carries no CASC build information

FileSystemSettings

FileSystemSettings()
FileSystemSettings(client_path: str | PathLike, version: ClientVersion, locale: Locale | None = ..., project_directory: str | PathLike | None = None, listfile_csv: str | PathLike | None = None, custom_fdid_start: FileDataID | None = ..., casc_product: str | None = None)

Everything needed to open a client filesystem: where the client is, which version it is, and the optional listfile / project directory / locale configuration. An immutable value, fully described at construction — a settings object can never be half-edited into an inconsistent state. In C++ it stays an aggregate (designated initializers; const members); the scripting languages construct through the synthesized field constructor, where everything after the version is optional.

client_path property

client_path: Path

The client installation root (the directory containing Data/).

version property

version: ClientVersion

The client version; selects the storage backend and MPQ chain.

locale property

locale: Locale

Client locale. For MPQ clients its Data/{code}/ directory must exist; for CASC clients it is the locale mask for content selection.

project_directory property

project_directory: Path | None

The project-directory overlay: the ultimate patch, where new files are added. Optional.

listfile_csv property

listfile_csv: Path | None

The working listfile CSV ('fileDataId;filepath'): read for path<->FileDataID resolution and appended to when new files are added. Effectively mandatory for modern CASC clients — without it files can only be requested by FileDataID.

custom_fdid_start property

custom_fdid_start: FileDataID

First FileDataID handed to files added to the project; keep far above Blizzard's ~6-7M so future official content never collides.

casc_product property

casc_product: str | None

TACT product code for CASC storages ('wow', 'wow_classic_era', 'wowt', ...). Left unset it is derived from the version's flavor (ClientVersion.default_casc_product), which is right for every ordinary install; set it explicitly for PTR, beta and one-off products ('wow_classic_titan'), or let ClientInstall.detect read the exact code off the installation.

detect staticmethod

detect(client_path: str | PathLike, locale: Locale, project_directory: str | PathLike | None, listfile_csv: str | PathLike | None, custom_fdid_start: FileDataID) -> FileSystemSettings
detect(client_path: str | PathLike) -> FileSystemSettings
detect(client_path: str | PathLike, locale: Locale) -> FileSystemSettings
detect(client_path: str | PathLike, locale: Locale, project_directory: str | PathLike | None) -> FileSystemSettings
detect(client_path: str | PathLike, locale: Locale, project_directory: str | PathLike | None, listfile_csv: str | PathLike | None) -> FileSystemSettings

Settings for a client whose version is read off the installation itself (see ClientInstall.detect) instead of being spelled out — the reliable way to open anything Classic, where the build number decides which engine's file formats you get. The remaining arguments are the same optional configuration the constructor takes.

Example
settings = FileSystemSettings.detect("/Games/World of Warcraft/_classic_era_",
                                     listfile_csv="listfile.csv")
with FileSystem.open(settings) as fs:
    ...     # formats resolve to the engine the install really is

Parameters:

Name Type Description Default
clientPath

the installation directory holding Data/

required
locale Locale

client locale

required
projectDirectory

the project-directory overlay

required
listfileCsv

the working listfile CSV

required
customFdidStart

first FileDataID for added files

required

Returns:

Type Description
FileSystemSettings

the settings, or the error ClientInstall.detect raised

FileSystem

The runtime gateway to one client's files. Picks the storage backend from the client version and hides the static composition behind one bindable type; C++ code that wants zero dispatch overhead can use MpqFileSystem/CascFileSystem directly.

Lifetime: open() returns an open filesystem and destruction releases it — in C++ that is the whole story (RAII; no public close, so no reachable wrong state beyond a moved-from object). The scripting languages, where finalization timing belongs to the runtime, additionally get explicit control: close() and is_open (welded from the protected surface), and in Python the with-statement.

Examples:

Preferred: scope the storage with a with block — open() returns the filesystem, __enter__ hands it to the block, and __exit__ calls close() on the way out, exception or not:

from wowlib import versions
from wowlib.fs import FileSystem, FileSystemSettings

settings = FileSystemSettings("/Games/WoW 3.3.5a", versions.wotlk)
with FileSystem.open(settings) as fs:
    data = fs.read_file("Interface/FrameXML/UIParent.lua")

Equivalent explicit form, when the lifetime cannot be a single block (a viewer holding the storage across UI events):

fs = FileSystem.open(settings)
try:
    data = fs.read_file("Interface/FrameXML/UIParent.lua")
finally:
    fs.close()      # storage released now, not at GC time

kind property

Which storage technology backs this filesystem: Mpq or Casc. A static fact of the opened client; remains valid after close().

version property

version: ClientVersion

The client version this filesystem was opened for — the anchor of version-agnostic code (expansion_of, for_version, Table.open). A static fact of the opened client; remains valid after close().

is_open property

is_open: bool

Whether the filesystem still holds its storage (false after close()).

open staticmethod

open(settings: FileSystemSettings) -> FileSystem

Initialize a client filesystem: open the storage (the full MPQ chain or the CASC storage), load the listfile if given, and attach the project-directory overlay.

Parameters:

Name Type Description Default
settings FileSystemSettings

what to open and how

required

Returns:

Type Description
FileSystem

the opened filesystem

read_file

read_file(key: FileKey) -> bytes
read_file(path: str) -> bytes
read_file(fdid: FileDataID) -> bytes

Read a file by FileKey — the generic identity for version-independent code: whichever half the key carries (path, FileDataID or both), the backend uses what it can address and the listfile fills the gap.

Parameters:

Name Type Description Default
key FileKey

the file identity (path, id, or both)

required

Returns:

Type Description
bytes

the file bytes

Read a file by client-internal path.

Parameters:

Name Description Default
path

the client-internal file path

required

Returns:

Type Description
bytes

the file bytes

Read a file by FileDataID.

Parameters:

Name Description Default
fdid

the numeric file identifier

required

Returns:

Type Description
bytes

the file bytes

exists

exists(key: FileKey) -> bool
exists(path: str) -> bool
exists(fdid: FileDataID) -> bool

Whether a file is reachable in the overlay or the storage.

Parameters:

Name Type Description Default
key FileKey

the file identity (path, id, or both)

required

Returns:

Type Description
bool

true if a read would find it

Whether a path is reachable.

Parameters:

Name Description Default
path

the client-internal file path

required

Returns:

Type Description
bool

true if a read would find it

Whether a FileDataID is reachable.

Parameters:

Name Description Default
fdid

the numeric file identifier

required

Returns:

Type Description
bool

true if a read would find it

enumerate_paths

enumerate_paths() -> list[str]

Enumerate every client-internal file path reachable in the storage: the union of the MPQ chain's archive listings (loose directories included), or — on CASC clients — every FileDataID the storage holds that the loaded listfile can name (unnamed ids are skipped, and without a listfile the listing is empty). Paths come back canonical, deduplicated and sorted; the project-directory overlay is not included.

Returns:

Type Description
list[str]

the sorted canonical paths

resolve

resolve(key: FileKey) -> FileKey

Fill the missing half of a key (path or FileDataID) from the listfile, best-effort: present halves are preserved, unknown halves stay empty (on MPQ-era clients there is no FileDataID space at all). Lets generic tools learn a file's full identity without storage-specific code.

Parameters:

Name Type Description Default
key FileKey

the file identity to complete

required

Returns:

Type Description
FileKey

the completed key

add_file

add_file(path: str, content: bytes) -> FileDataID

Add (or overwrite) a file in the project directory; on CASC-era clients new paths get a custom FileDataID, persisted in the working listfile.

Parameters:

Name Type Description Default
path str

the client-internal path of the file

required
content bytes

the file contents

required

Returns:

Type Description
FileDataID

the file's FileDataID (0 on MPQ-era clients)

import_keys

import_keys(key_list: str) -> None

Register TACT encryption keys (CASC only) from the community 'KeyName KeyHex' per-line text, so encrypted .db2 sections decrypt and decode.

Parameters:

Name Description Default
keyList

newline-separated 'KeyName KeyHex' lines

required

Returns:

Type Description
None

nothing; raises on an MPQ client or a malformed list

close

close() -> None

Release the client storage now — every MPQ archive handle or the CASC storage handle — instead of waiting for garbage collection. Safe to call repeatedly; afterwards reads and writes raise StorageNotOpen. In Python, prefer the with-statement, which calls this on block exit.

__enter__

__enter__() -> FileSystem

Enter a with-block: returns the filesystem itself, unchanged (open() already opened the storage).

__exit__

__exit__(exc_type: object, exc_value: object, traceback: object) -> bool

Leave the with-block: close()s the filesystem; an in-flight exception propagates (always returns False).