Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

7.9 KiB

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:

#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.