# Building the `.mw4` resource files How the runtime game resources (`resource\*.mw4` + `resource\*.dep`) are produced from the `Content\` source tree. Reverse-engineered from the engine source (`mw4\Code\MW4Application`, `mw4\Code\MW4`, `mw4\Libraries\Adept`). > **VERIFIED end-to-end (2026-06-11)** with our freshly built `MW4pro.exe` (Profile/LAB_ONLY): > a minimal `Content\MechWarrior4.build` packed a test file into `resource\restest.mw4` + > `.dep` (exit 0, ~4 s). Confirmed: `.mw4` carries the `#VBD` ResourceFile magic + content > version + payload; `.dep` records the source dependency; **incremental logic works** — > re-running with no change skipped (timestamp unchanged), changing the source triggered a > rebuild (new timestamp + new payload). The Release exe was also confirmed to ignore > `-build` as predicted. Run line used: > `MW4pro.exe -build -norun -window -nosound -nocd -noeula` (cwd = a dir containing `Content\`). ## TL;DR - The resource compiler is **built into the game executable itself**, not a separate tool. - It runs when the exe is launched with the **`-build`** flag. - **Only `LAB_ONLY` build configs can build resources** — i.e. `MW4_dbg` (Debug), `MW4_arm` (Armor), or `MW4pro.exe` (Profile). The **Release `MW4.exe` cannot** (the `-build` handling is `#ifdef LAB_ONLY`). - It reads **`Content\MechWarrior4.build`** (a master manifest) and writes the `.mw4`/`.dep` packages into `resource\`. - It is **incremental**: a package is only rebuilt if missing or out of date. ## The exe-config requirement (important) `MW4Application.cpp` parses `-build` only inside `#ifdef LAB_ONLY`: ```cpp #ifdef LAB_ONLY Application::BuildOK = (strstr(all_lower, "-build") != NULL); #endif ``` From the `.dsp` preprocessor defines (see CLAUDE.md): | Config | LAB_ONLY? | exe | can -build? | |---------|-----------|----------------|-------------| | Release | no (`RELEASE`) | `rel.bin\MW4.exe` | **NO** | | Profile | yes | `rel.bin\MW4pro.exe` | yes | | Armor | yes | `arm.bin\MW4.exe` | yes | | Debug | yes | `dbg.bin\MW4.exe` | yes | That is why the stock `bldresources_game_*.bat` invoke the LAB_ONLY exes: ``` bldresources_game_dbg.bat -> ...\MW4_dbg -armorlevel 3 -build bldresources_game_armor.bat -> ...\MW4_arm -armorlevel 3 -build bldresources_game_profile.bat -> ...\MW4_prf -armorlevel 3 -build ``` (`-armorlevel 3` is an assertion/validation level; `-build` does the work. Without `-build` the same exe just runs the game using whatever `.mw4` files already exist.) ## Inputs 1. **Master manifest:** `Content\MechWarrior4.build` — a NotationFile whose top level is a tree of other `.build` files to process, e.g.: ``` textures.build={ core.build={ props.build={ maps\alpine02\alpine02.build={ missions\timberline\timberline.build={ } } ... ``` The nesting expresses build order + scope (children are built within the parent's context). 2. **Per-package `.build` files** — each defines exactly ONE output package. The single page name is the output path; the entries are the source items to pack. e.g. `core.build`: ``` [resource\core.mw4] <- output package core=yes <- mark as a "core" (global/shared) package data=Effects\AC_Flare\AC20_Flare.data data=Effects\AC_hit\AC10Hit_Grass.data ... ``` 3. **Source assets** under `Content\` referenced by the entries (`.data`, instance files, textures, etc.). The full source content tree must be present. ## Entry types (what can go into a package) Handled by `Adept::Tool::BuildResource` (generic) and `MechWarrior4::MWTool::BuildResource` (game-specific override). Each entry becomes one record in the output `.mw4`: Generic (`Adept\Tool.cpp`): - `instance=` — an entity instance (compiled CreateMessage) - `data=` — a data list (`ConstructDataList`) - `notation=` — a notation file embedded verbatim - `file=` — an arbitrary file embedded verbatim - `directory=`— every file in a directory embedded verbatim Game-specific (`MW4\MWTool.cpp`): `campaign`, `lancemate`, `salvage`, `armature`, `subsystems`, `damage`, `mwtable`, `options` — each parses a source NotationFile and constructs a typed stream. ## Output `Tool::BuildResources` first ensures the output dirs exist: `resource\`, `resource\Maps\`, `resource\Missions\`, `resource\Variants\`. For each `.build` it produces: - **`resource\.mw4`** — the packed `ResourceFile` (records keyed by ID). - **`resource\.dep`** — the `FileDependencies` list (source files + timestamps) used to decide staleness on the next build. A **content version** is stamped into every package: `VER_CONTENTVERSION` (currently `63`, from `Buildnum\buildnum.h`). The runtime uses it to reject mismatched resources. ## Incremental rebuild logic (`Tool::BuildResources`) For each package: ``` build_age = max(timestamp(this .build file), parent build_age) dirty = (timestamp(resource_file) < build_age) OR (.dep file missing) open ResourceFile(name, dep, level, content_version, dirty) ParseBuildFile(...) // each entry: BuildResource() only re-packs if the // individual Resource DoesResourceExist()==false // or IsResourceUpToDate()==false res_file->Save() ``` - `core=yes` packages: the engine ignores per-file dependency chaining (they're treated as global/shared and always considered in-scope for children). - Non-core packages: the package adds itself to the dependency set passed to its children, so a map/mission package depends on its parent map package, etc. - Net effect: re-running `-build` after editing one source file rebuilds only the affected package(s), not the whole 1 GB set. ## How to (re)build resources in practice 1. Build a **LAB_ONLY** game config (Profile is the lightest): in the VC6 IDE build `MW4Application - Win32 Profile` (outputs `rel.bin\MW4pro.exe`). See CLAUDE.md / build-env\README.md for the toolchain. 2. Run it from a directory that has the **`Content\` source tree** (and where you want `resource\` written), with `-build`: ``` MW4pro.exe -armorlevel 3 -build ``` (Add `-window -nosound` etc. as desired; it still launches the game after building, so `-norun` can be used to build-and-exit — see the flag parser in MW4Application.cpp.) 3. The engine creates/updates `resource\*.mw4` + `*.dep`. Copy the resulting `resource\` into the deployment (the deploy script already treats `resource\` as a whole-keep dir). ### Integrated into deployment (2026-06-23) `build-env\build-resources.ps1` wraps the above (stages the fresh `MW4pro.exe`, applies the DDraw shim, runs `-build -norun -window -nosound -nocd -noeula` in the source tree with a timeout guard, reports rebuilt packages). `deploy-mw4.ps1` now calls it as **step [0/5]** so a deploy repacks `resource\*.mw4` from current source *before* copying to the target — this is what makes loose source edits (e.g. `Content\ShellScripts\ConLobby.script`) actually take effect, since the runtime loads packed resources before loose disk files (`s_DiskFirst` defaults to false in `Application::GetFileForGOS`). Use `deploy-mw4.ps1 -SkipResourceBuild` to deploy the existing prebuilt packages unchanged. The build writes into `Gameleap\mw4\resource` (the editor junctions this, so it benefits automatically). A modal content STOP in the WIP tree trips the timeout and is reported (fix the asset, re-deploy) rather than hanging the deploy. ### Caveats - The **Release** build will silently ignore `-build` — you must use a LAB_ONLY exe. - Requires the **full `Content\` source tree** (the dev `Gameleap\mw4\Content`, ~4.6 GB). The trimmed runtime deployment does NOT contain it. - `-build` builds against `Content\MechWarrior4.build`; to build a subset, point a smaller master build file at just the packages you want (the format is plain NotationFile text). - Bump `VER_CONTENTVERSION` in `Buildnum\buildnum.h` if resource formats change, or the runtime will load stale `.mw4` files.