MW4Ed2.exe — Mission/Level Editor (deep-dive)¶
Reference record; not an oracle target. See the editor-tools overview.
1. Binary facts¶
| File | reference\MW4Ed2.exe |
| Size | 3,833,856 B (0x3A8000) |
| SHA-256 | 7b19a1cbc18b2f4c16fd8fdeb9e44c2861c97464e8275e28817b28bbdae03b60 |
| Toolchain | MSVC6, Linker 6.0, TimeDateStamp 2002-11-26 23:53:19Z |
| Machine / Subsystem | x86 (0x014C) / GUI (2) |
| ImageBase / Entry | 0x00400000 / 0x00727C54 (in .text) |
| Rich header | XOR key 0xF2BFBE51, DanS @ 0x80 (MSVC6 tools, ProdId 0x000C/0x000B/0x000A/0x0013/0x0001 mixes) |
| Functions | 11,388 (Ghidra) — the largest of the four editor tools by ~10× |
Sections (5 — note the SafeDisc .txt2, same as the game EXE):
| Section | RVA | VSize | RawSize | Notes |
|---|---|---|---|---|
.text |
0x1000 | 0x3393F2 | 0x33A000 | game + editor + framework code |
.txt2 |
0x33B000 | 0x17C5 | 0x2000 | SafeDisc leftover (out of scope) |
.rdata |
0x33D000 | 0x4F8B4 | 0x50000 | strings, vtables, IAT |
.data |
0x38D000 | 0x1059C8 | 0xA000 | initialized data + ~0x659C8 zero tail |
.rsrc |
0x493000 | 0x101F8 | 0x11000 | resources |
Imports (import dir at RVA 0x38A3F0, 15 DLLs, ~736 functions): MFC42 356
(dynamic MFC — the editor shell), KERNEL32 107, MSVCRT 111, USER32 90, GDI32
28, ADVAPI32 12, MSACM32 7, ole32 5, WINMM 5, SHELL32 4, VERSION 3, COMCTL32
2, WSOCK32 2, plus the DRM imports (ARTPCLNT 1, CdaC14BA 3). The binary also
names (LoadLibrary/delay-style references, not import-table) the full game
runtime: blade.dll/bladed.dll (renderer wrapper + its debug build),
gundll.dll (object library), dplayx.dll, dinput/ddraw/d3dim/
dsound, amstream/quartz/msacm/Ir50_32/ijl10 (Indeo/JPEG codecs),
Language.dll/MissionLang_MERCS.dll/ScriptStrings_MERCS.dll
(localization), machin16/32.dll, dbghelp/imagehlp, debug CRTs
(msvcrtd/msvcirtd) alongside release — the tool was built against both
runtime flavors.
2. Purpose¶
MW4Ed2 is FASA's Mission Editor 2 — a full in-house mission/level editor
that embeds the complete game runtime (renderer through blade, object
code through gundll, DirectPlay, the ABL scripting VM, localization). It
creates and edits the Resource\Missions\*.mw4 mission archives: pick a map
(≈ \Games\*.mw4 browser), lay out spawns/units/lights/drop-zones/skies,
bind ABL mission scripts and bot AI, and write the archive whose members are
the serialized MechWarrior4::MWMission object graph plus per-aspect binary
blobs. It is the companion to MapCreator (which builds the maps/mission
terrain) — MW4Ed2 builds what runs on top of a map.
3. Architecture — a game framework with an editor shell¶
Two layers:
- MFC editor shell (356 MFC42 imports): dialogs, toolbars, file dialogs,
the
\Games\*.mw4map browser andResource\Missions\*.nfm/Resource\UserMissions\*.nfmscenario dialogs. Thin compared to the framework below. - Bundled game framework (the bulk of
.text): the same class/property system the MW4Mercs engine uses, driven headlessly: - Class registry —
FUN_0046a280(size, "Class::Name", base, ...)registers serializable classes by name;FUN_006d3d80(name, ...)is a second registry flavor. Objects are identified in files by their class tag string (Adept::Mission,MechWarrior4::MWMission). - Property system —
FUN_00469910(index, "Name", type, offset)+FUN_00469c60(list)register reflected, offset-addressed fields per class. This is the serialization contract (§5). - Allocator/factory —
FUN_00449aa0(size)(calloc-like, returns 0 on failure);FUN_00449ab0(p)free. - GameOS framework (shared with the other editor tools + the game):
file system finder (
FUN_0043d7e0set path /FUN_0043d850next /FUN_0043d9b0), string table loader, the game console/message color helper (FUN_005a4bf0(0xbb7ff, ...)= blue "loading" text). - ABL scripting VM —
FUN_005a6270(path, ...)compiles/loads an ABL script; mission and bot behavior is ABL (see §6). - Full runtime side-loading — the renderer (
blade) and object library (gundll) DLLs are referenced by name, so the editor can actually run/render the level being edited.
4. The mission file format (Resource\Missions\*.mw4)¶
Ground truth from the shipped coliseum.mw4 (58,948 B) and the sibling
mission archives (0001.mw4, alpine.mw4, avalon01.mw4, ...).
4.1 Container — #VBD v4 (see include/mw4/vbd.h)¶
Header (43 bytes + path): #VBD (4) · version 4 (4) · count=63 (4) ·
dir_off (4) · keys (4) · filetime (8) · flags1=0x1A (4) · flags2=0x1A (4) ·
reserved (6) · path-len (1) · path (resource\missions\coliseum.mw4, 30
chars). The count=63 is a capacity, not the member count.
4.2 Directory — multi-group rows¶
The directory is a sequence of fixed-prefix rows grouped by a shared FILETIME:
filetime (8) u32 u32 u32 u16 index u8 name-len name[..]
All rows in a group carry the same filetime; consecutive groups differ (a group = one filetime run, e.g. one save session). Member names observed for coliseum:
missions\coliseum\coliseum.instance (index 1)
missions\coliseum\coliseum.data{Element}
missions\coliseum\coliseum.data{GameModel}
missions\coliseum\coliseum.data
missions\coliseum\coliseum.lights
missions\coliseum\coliseumnight.lights
missions\coliseum\DropZones\DropTeam2.contents
missions\coliseum\DropZones\SecondaryLanceStart.contents
missions\coliseum\skies\colsm01_sky\colsm01_sky.erf
missions\coliseum\mission.bounds
missions\coliseum\coliseumgraph.data
missions\coliseum\scripts\Coliseum.abl
missions\coliseum\scripts\Coliseum_SinglePlayer.abl
missions\coliseum\scripts\Coliseum_Campaign.abl
missions\coliseum\scripts\player.abl
... (per-game-type script variants)
Member semantics: .instance (the serialized mission object, see §4.3),
.data{...} aspect sub-blobs, .lights/...night.lights (light lists),
DropZones\*.contents (spawn/drop-zone sets), skies\<map>_sky\*.erf
(environment resource), mission.bounds (bounds), <name>graph.data
(nav/unit graph), scripts\*.abl (mission scripts per game type).
4.3 First member — the serialized mission object¶
The first member's content begins directly with the map reference as a NUL-terminated path, followed by binary object data:
"resource\maps\colsm01.mw4" \0 <binary mission object serialization...>
i.e. the mission archive is an object graph whose first field is the backing
map. The exact directory → data-offset encoding is not fully resolved
(multiple groups, variable-length rows; maps decode cleanly at
data_off = prev.data_off + prev.stored_size relative to dir_off, but the
mission archives interleave group headers — see the #VBD follow-up note in
include/mw4/vbd.h).
5. The mission class + property table¶
5.1 Class registration¶
Adept::Mission— registered byFUN_00462610(3,144 B) viaFUN_0046a280(0x1E6, "Adept::Mission", base, factory, ...)→ class size 0x1E6 (486 bytes). This is the mission environment base class.MechWarrior4::MWMission— registered byFUN_00585f60viaFUN_0046a280(0x420, "MechWarrior4::MWMission", ...)→ class size 0x420 (1056 bytes); also constructs theStartMusicStatusandTransMusicStatushelper objects. This is the MW4 concrete mission subclass (the.instancemember's top-level type).Mission— registered byFUN_006d3d80("Mission", ...).
5.2 Adept::Mission property table (the serialized environment)¶
FUN_00462610 registers 55 reflected properties (index, name, type-code,
offset-in-class). Type codes seen: 1=int, 2=float, 0xE=bool,
0x10=color/vector4, 0x231=string/resource. The environment is organized
in flavors — base, Underwater, Smoke — each with fog start/end/
density + color, plus night/water/weather extras:
| idx | name | type | off | idx | name | type | off | |
|---|---|---|---|---|---|---|---|---|
| 6 | GeneralFogStart | f | 0x7C | 0x25 | WeatherEffectResource | s | 0x24 | |
| 7 | GeneralFogEnd | f | 0x80 | 0x26 | AllowSearchLights | b | 0x105 | |
| 8 | GeneralFogDensity | f | 0x84 | 0x27 | AllowRunningLights | b | 0x106 | |
| 9 | LightFogStart | f | 0x88 | 0x28 | HeightFogStart | f | 0xE8 | |
| 10 | LightFogEnd | f | 0x8C | 0x29 | HeightFogEnd | f | 0xEC | |
| 11 | LightFogDensity | f | 0x90 | 0x2A | HeightFogOpacity | f | 0xF0 | |
| 12 | CustomFogStart | f | 0x94 | 0x2B | NearClip | f | 0xF4 | |
| 13 | CustomFogEnd | f | 0x98 | 0x2C | FarClip | f | 0xF8 | |
| 14 | CustomFogDensity | f | 0x9C | 0x2D | NightGroundColor | c | 0x6C | |
| 15 | FogColor | c | 0x2C | 0x2E | CanSetTime | b | 0x107 | |
| 16 | GeneralFogStartUnderwater | f | 0xA0 | 0x2F | NightFogColor | c | 0x5C | |
| 17 | GeneralFogEndUnderwater | f | 0xA4 | 0x30 | HeatSinkEfficiency | f | 0x114 | |
| 18 | GeneralFogDensityUnderwater | f | 0xA8 | 0x31 | NightWeatherEffectResource | s | 0x28 | |
| 19 | LightFogStartUnderwater | f | 0xAC | 0x32 | WaterSpecularFactor | f | 0xFC | |
| 20 | LightFogEndUnderwater | f | 0xB0 | 0x33 | WaterAtNightSpecularFactor | f | 0x100 | |
| 21 | LightFogDensityUnderwater | f | 0xB4 | 0x34 | WaterSpecularPower | i | 0x10C | |
| 22 | CustomFogStartUnderwater | f | 0xB8 | 0x35 | WaterAtNightSpecularPower | i | 0x110 | |
| 23 | CustomFogEndUnderwater | f | 0xBC | 0x36 | IsNightMission | b | 0x108 | |
| 24 | CustomFogDensityUnderwater | f | 0xC0 | |||||
| 25 | FogColorUnderwater | c | 0x3C | |||||
| 26 | GeneralFogStartSmoke | f | 0xC4 | |||||
| 27 | GeneralFogEndSmoke | f | 0xC8 | |||||
| 28 | GeneralFogDensitySmoke | f | 0xCC | |||||
| 29 | LightFogStartSmoke | f | 0xD0 | |||||
| 30 | LightFogEndSmoke | f | 0xD4 | |||||
| 31 | LightFogDensitySmoke | f | 0xD8 | |||||
| 32 | CustomFogStartSmoke | f | 0xDC | |||||
| 33 | CustomFogEndSmoke | f | 0xE0 | |||||
| 34 | CustomFogDensitySmoke | f | 0xE4 | |||||
| 35 | FogColorSmoke | c | 0x4C | |||||
| 36 | AllowRespawn | b | 0x104 |
(f=float type 2, c=color type 0x10, b=bool type 0xE, s=string type 0x231,
i=int type 1. Offsets are into the Adept::Mission class; indices 0–5 and
the MusicStatus properties belong to base classes.)
The Adept::Mission constructor (FUN_00586a90, 2,627 B) initializes
this state (all-zero spawn/bounds slots at +0x894..+0x8B5, two 0x2F8-byte
object slots at +0x895/+0x897 via FUN_00621990, random fog
near/far/density via FUN_006daa90, default respawn count 0x1E) and applies
per-map tuning flags at +0x13B3 by comparing the map path against
hard-coded names: coliseum→1, factory→2, gladiatorpit→1, rubblepile
→2, jungle→… (an editor-side special-case table, not a game rule).
6. ABL scripting integration¶
Mission behavior is bound through ABL scripts stored as archive members
(scripts\*.abl), one set per game type (Stock_MissionPlay, Campaign,
SinglePlayer, ...), plus shared library scripts:
content\ablscripts\miscfuncs.abl— loaded byFUN_00586090(the ABL library bootstrap; blue "loading" console message, thenFUN_005a6270(path, ...)— the ABL compile/load entry).Content\ABLScripts\StockScripts\Stock_MissionPlay.abl— referenced by the mission object (FUN_00586a90's cluster) as the default stock mission script.Content\ABLScripts\Bots\UberBot.abl— the default bot AI script, loaded by the bot subsystem (FUN_00616430,FUN_00616f80,FUN_00618d00,FUN_0061b540— a 4-site bot-script family in the 0x61xxxx region).scriptName(0x00753954) — the per-mission script-name property binding (referenced from the 0x569xxx mission region).
7. Subsystem map / key functions¶
| VA | Size | Role |
|---|---|---|
| 0x00585F60 | 242 | Class registration MechWarrior4::MWMission + MusicStatus helpers |
| 0x005862A0 | (LAB) | MWMission vtable method (constructor cluster) |
| 0x00462610 | 3,144 | Adept::Mission registration + full property table (§5.2) |
| 0x00586A90 | 2,627 | Adept::Mission constructor (env init + per-map tuning) |
| 0x00586090 | 139 | ABL library bootstrap (miscfuncs.abl load) |
| 0x005FEE20 | 602 | Mission browser/loader (content\missions\<name>.instance) |
| 0x004574F0 | — | Resource\Missions path handler (save/browse) |
| 0x00572CF0 / 0x006197F0 / 0x00619960 | — | \Games\*.mw4 map browser |
| 0x0061CC07 / 0x0060BE92 | — | Resource\Missions\*.nfm / UserMissions\*.nfm scenario dialogs |
| 0x00616430, 0x00616F80, 0x00618D00, 0x0061B540 | — | Bot AI script family (UberBot.abl) |
| 0x0046A280 | 279 | RegisterClass (size, name, base, factory, methods) |
| 0x00469910 | 74 | RegisterProperty (index, name, type, offset) |
| 0x00469C60 | 71 | publish property list |
| 0x00449AA0 | 13 | framework allocator (calloc-style) |
| 0x005A6270 | 231 | ABL compile/load entry |
| 0x0043D7E0 / 0x0043D850 / 0x0043D9B0 | — | GameOS file finder (set path / next / close) |
| 0x005A4BF0 | 1,878 | console/message color helper (0xbb7ff blue load text) |
The largest functions (framework dispatchers, not editor logic):
FUN_005A94E0 (25,866 B), FUN_006AD980 (25,018 B), FUN_006C9920
(16,743 B), FUN_004EF280 (10,339 B), FUN_004F2EC0 (10,312 B) — the
engine's big per-frame/update loops and the ABL VM.
8. Notable strings¶
MechWarrior4::MWMission (0x754B70) · MW4Mission (0x754B88) ·
Adept::Mission (0x748024) · Mission (0x78DAxx reg) · Mission.Bounds
(0x74819C) · MissionBounds (0x74818C) · mission.bounds ·
StartMusicStatus / TransMusicStatus · GeneralFog*/LightFog*/
CustomFog*/HeightFog* property names (0x78F4CC–0x78F64C) ·
content\missions\ (0x761C88) · content\ablscripts\miscfuncs.abl
(0x7541B8) · Content\ABLScripts\StockScripts\Stock_MissionPlay.abl
(0x754D98) · Content\ABLScripts\Bots\UberBot.abl (0x763BC0) ·
\Games\*.mw4 (0x754614) · Resource\Missions\*.nfm (0x763A1C) ·
Resource\UserMissions\*.nfm (0x763A00) · MissionLang_MERCS.dll
(0x753F95) · MissionNames.tbl (0x75479F) · scriptName (0x753954) ·
UserMission (0x7542B8) · options.mw4 (0x763AA4) · coliseum/factory/
gladiatorpit/rubblepile/jungle (per-map tuning tags).
9. Open questions¶
- #VBD directory encoding: the mission archives' multi-group directory
(shared-filetime row runs, 23-byte prefix) does not match the map archives'
single-group layout captured in
include/mw4/vbd.h. A dedicated decode pass of the mission directory and data-offset rule is still needed. - Mission object graph: decoding the serialized
MechWarrior4::MWMissiongraph is the highest-value follow-up; the game loads the sameResource\Missions\*.mw4files.