# 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\<name>.mw4`** — the packed `ResourceFile` (records keyed by ID).
- **`resource\<name>.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.
