Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
153 lines
7.9 KiB
Markdown
153 lines
7.9 KiB
Markdown
# 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.
|