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(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
¶
The installation directory that was inspected — the one holding Data/, ready to hand to FileSystemSettings.
casc_product
property
writable
¶
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(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
¶
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
¶
The project-directory overlay: the ultimate patch, where new files are added. Optional.
listfile_csv
property
¶
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
¶
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
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
¶
kind: StorageKind
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
¶
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 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
¶
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 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
¶
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
¶
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
¶
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__
¶
Leave the with-block: close()s the filesystem; an in-flight exception propagates (always returns False).