MapCreator.exe — bitmap → .mw4 map authoring tool (deep-dive)¶
Reference record; not an oracle target. See the editor-tools overview.
1. Binary facts¶
| File | reference\MapCreator.exe |
| Size | 528,384 B (0x81000) |
| SHA-256 | 4f1a37548b352fd36e99a71aded94db13253f9ec84c6d350237b525f5936ea7d |
| Toolchain | MSVC6, Linker 6.0, TimeDateStamp 2002-11-26 20:47:03Z |
| Machine / Subsystem | x86 (0x014C) / GUI (2) |
| ImageBase | 0x00400000 |
| Sections | .text RVA 0x1000 VSize 0x68ABB · .rdata RVA 0x6A000 · .data RVA 0x7C000 VSize 0x78358 (raw 0x3000 — most of .data is zero-fill; the tool's literal strings live in the raw part at 0x47C000–0x47C9xx) · .rsrc RVA 0xF5000 |
| Imports | MFC42 (135), MSVCRT (76), KERNEL32 (85), USER32 (63), GDI32 (12), ADVAPI32 (12), ole32 (3), MSACM32 (7), VERSION (3), WINMM (5) |
| Functions | 1,556 (Ghidra) |
Like NFMEditor, it bundles GameOS and must run from the game install root
(Cannot find Language.dll in %s\assets\binaries or %s, this file is required,
GameOS build version, Exit:DestroyTextureManager). It also links the
Stuff:: engine classes (Stuff::FileStream, Stuff::TreeNodeOf) and a
full image-decoding library (TIFF/GIF/TGA/PNG/BMP + zlib + libpng).
2. Purpose and pipeline¶
MapCreator converts a heightfield bitmap + textures into a playable
map .mw4. The overall flow (from the writer/handlers):
bitmap (TIF/GIF/TGA/PNG) ──> heightfield (256..1024 wide × 256..2048 high,
multiple of 256, 24-bit for "interest data")
│
MapCreator writes (Content\ + Resource\ trees under the game root):
Content\Maps\<name> map definition "info file"
Content\Maps\<name>\<name>.build build manifest
Content\Maps\<name>\tctd.ini Tctd input config
Content\Maps\<name>\tctd.ini.bsp Tctd BSP input config
Content\textures\maps\... copied/renamed textures
Maps\<name>\<name>.instance instance info
│
shell: Tctd /f Content\Maps\<name>\tctd.ini
shell: Tctd /f Content\Maps\<name>\tctd.ini -bsp
│
Tctd.exe builds the final packed map:
Resource\Maps\<name>.mw4 a #VBD archive (see §7)
Tctd.exe ("Terrain converter/compiler") is an external tool that must be
present (Cannot Find Tctd.exe,Make sure this file exists and try again);
MapCreator shells out to it with system(). Tctd is what produces the binary
terrain/BSP data packed into the final .mw4. The tool then reports
Map created successfully. Run Mission Editor to create a mission with this map
(or There was an Error in Createing your map [sic] on failure).
3. Layout — .text regions¶
| Range | Content |
|---|---|
| 0x401000–0x4072xx | Tool UI + map pipeline (heightfield load/validate, texture pickers, map create/save, Tctd launch) |
| 0x408xxx–0x44Exxx | Bundled GameOS / Stuff:: engine (file system, textures, DirectX, DirectSound, image codecs) |
| 0x45xxxx–0x465xxx | GameOS config/profile + helpers |
| 0x466Bxx+ | Ordinal thunks (MFC/GameOS imports) |
The Ghidra inventory names imports Ordinal_XXXX (jmp-thunk table at
0x466B80+). Key ordinal semantics seen in the map code: Ordinal_926/924/
922 = path-combine string ops, Ordinal_858 = write/create, Ordinal_800 =
close, Ordinal_540 = push a "file op" guard, Ordinal_5683 = strrchr,
Ordinal_4277 = substring after last \, Ordinal_4129 = strrchr('.'),
Ordinal_2818 = sprintf, Ordinal_941 = append to command line,
Ordinal_860 = get app path, Ordinal_6334 = _ftol-family.
4. The map definition "info file" (Content\Maps\<name>)¶
The map definition is written with the same GameOS info-file format that
NFMEditor uses (sections, key=value, case handling — see
docs/editor_tools/nfmeditor.md §4). The map file's root carries a gamedata
section (all keys below, written by FUN_004021b0 / FUN_004012b0):
| Key | Value |
|---|---|
grid |
<name>.erf — the grid/terrain source file |
cull |
nevercull |
class |
mechwarrior4::mwmap — the map class tag |
texturedata |
<name>.fgd |
tcffilename |
<name>.index.tcf |
waterlevel |
float (water plane height) |
watertexture |
path to water texture |
underwatertexture |
path to underwater texture |
waterdetailtexture |
path to water detail texture |
HudMap |
path to the HUD/minimap texture |
InputPath |
input bitmap path |
MapCenter |
two floats, e.g. "%.1f %.1f" (grid center; this+0x64 × 0x458280 scale const ×2) |
SmoothingPasses |
int (heightfield smoothing passes, this+0xbc) |
PolyCount |
int (target poly count, this+0xc0) |
HeightField |
heightfield file name |
DeltaY |
float (vertical scale, this+0xb8) |
Texture/geometry files referenced by the definition:
- <name>.fgd — the textured-terrain descriptor,
- <name>.index.tcf — the texture-collection/flag file,
- <name>.erf — the terrain grid source.
5. Heightfield input rules (from the validator at ~0x401480–0x401780)¶
| Rule | Message |
|---|---|
| exists | HeightField Does Not Exist / Bad Height Field |
| width 256..1024 | HeightField Width is too small, must be at least 256 / HeightField Width is too large, Cannot be larger than 1024 |
| height 256..2048 | HeightField Height is too small, must be at least 256 / HeightField Height is too large, must be at most 2048 |
| both multiple of 256 | HeightField has incorrect size, must be a multiple of 256 i.e. 256,512,768,1024... |
| 24-bit when "Interest data" used | HeightField must be 24-bit while using "Interest data" |
File dialog filter: Bitmap Images (*.tif,*.gif,*.tga,*.png)|*.tif;*.gif;*.tga;*.png||.
The image decoder handles TIFF (Tiff Error-*), GIF, TGA, PNG (libpng), BMP,
JPEG (via ijl10.dll).
6. Texture rules¶
| Texture | Constraint |
|---|---|
| Terrain texture | must exist, must be valid (Terrain Texture Does Not Exist / Bad Terrain Texture); size must equal the heightfield: For a Heightfield of %ix%i Terrain Texture should be %ix%i |
| Detail texture | Detail Texture Does Not Exist / Bad Detail Texture; Detail Texture MUST be 256x256 |
| Water / water-detail / underwater | must exist; all MUST be 256x256 (Water Texture Texture MUST be 256x256 [sic], etc.) |
| Selection | FGD Files (*.fgd)|*.fgd|| dialog for the FGD; textures written under Content\textures\maps\ with the %s\%s.Tga / %s\%c%c\%s_%c%c_0_0000.Tga naming pattern |
7. The final map archive — Resource\Maps\<name>.mw4 is a #VBD¶
The shipped maps (e.g. carps01.mw4, 7–27 MB) are #VBD v4 archives — the
container documented in include/mw4/vbd.h. Verified on
carps01.mw4: sig #VBD, version 4, key1==key2==0x108, filetime, flags 1/1,
archive path resource\maps\carps01.mw4 (25 chars incl. extension).
Two observations beyond vbd.h:
- The directory is multi-group. After the header/path, the directory is
organised as a sequence of blocks, each block = a path + its member entries:
e.g.
maps\carps01\carps01.instance, then entry rowstextures\composttexture\arctic01_color_rock_active.bid,textures\composttexture\alpine01_color_rockfaceshale_active.bid, ... — one per map asset.entry_count=63in the header is a capacity/count field, not the actual member count of every archive. - Members are compressed binary. The
.instance/.bidmembers are binary, high-entropy (LZW-style,uncompressed_size > stored_size; the decompressor is the game'sFUN_006FC220, see AGENTS.md "Compression codec — LZW").data_offis relative todir_file_offsetand members are laid out contiguously (next.data_off == prev.data_off + prev.stored_size).
So MapCreator itself never writes a #VBD — it writes the info-file definition + textures and lets Tctd.exe produce the packed archive.
8. Key functions¶
| VA | Size | Role |
|---|---|---|
| 0x004012b0 | 514 | Map-definition metadata writer (InputPath, MapCenter, SmoothingPasses, PolyCount, HeightField, DeltaY) |
| 0x00401750 | — | Heightfield load (file dialog *.tif;*.gif;*.tga;*.png) |
| ~0x401480–0x401780 | — | Heightfield validator (size rules §5; not a Ghidra-recognised function) |
| 0x004021b0 | 1869 | Map definition writer (gamedata section: grid/cull/class/texturedata/tcffilename/water*/HudMap) |
| 0x00403720 | 1684 | Create-map orchestrator: writes definition + .build + .instance, copies textures, calls the Tctd launcher |
| 0x00403dc0 | 266 | .build manifest writer (Content\Maps\<name>\<name>.build) |
| 0x00403ed0 | ~900 | Create-map command handler: name sanitise/validate (Give the map a valid name), overwrite prompt (A map by this name exists, OverWrite?), success/error messages (region, not a Ghidra function) |
| 0x00404220 | 486 | Tctd.exe launcher: option flags (/i, /s, /y, /uid, /p) + system("Tctd /f Content\Maps\<name>\tctd.ini") + system("Tctd /f Content\Maps\<name>\tctd.ini -bsp"); missing-Tctd error |
| 0x00404cb0 / 0x00405790 / 0x00405870 / 0x00405970 | — | texture pickers (FGD / water / detail dialogs) |
| 0x004095c0 / 0x00409690 / 0x00409740 | 60/43/43 | info-tree set string / int / float |
| 0x0040a530 / 0x0040a6b0 / 0x0040ad30 | 171/152/84 | info-file open / close / free |
| 0x0040b6b0 / 0x0040b6d0 / 0x0040b890 | 31/301/218 | info-tree helpers (gamedata lookup, subtree) |
| 0x00415f00 | 161 | file-exists check (Tctd.exe) |
(The info-file parse/serialize library itself is the same GameOS family as NFMEditor's §4 map — the parser here is the MapCreator's bundled copy.)
9. Bundled engine / libraries¶
- GameOS: file system (
Stuff::FileStream, memory-mapped files,Too many files,File '%s' is still open), texture manager (InitializeTextureManager called twice!,Exit:DestroyTextureManager, texture filter/format diagnostics), Direct3D/DirectDraw/DirectSound/DirectInput error tables, driver/registry dump (VideoCard.txt,Driver version,\Config\Platform), Language.dll loader, DirectPlay (DPERR_*), sound (acmStreamOpen, MSADPCM/PCM only). - Image codecs: TIFF (
(Is Tiff)Invalid File,Tiff Error-Wrong Version), GIF, TGA (Only 256 color palletized, 24 or 32 bit TGA files supported), PNG (libpng —Not a PNG file,Incompatible libpng version), BMP ("..." is an invalid .BMP file), JPEG (ijl10). Plus zlib. The(Create Fade)/(MaskTo)/(MakeRGB)/(UnDifference)/(Deinterlace)strings are a generic image-processing helper library. - Stuff:: engine classes:
Stuff::FileStream,Stuff::TreeNodeOf. - MAPI support (
MAPISendMail,Bad MAPI32.DLL) and the sameRAID/SQL connection string as NFMEditor (DRIVER={SQL Server};...).
10. Notable strings (VA → value, all in .data raw 0x47C000+)¶
0x47C2C0 Content\Maps\ · 0x47C2C8 Maps\ · 0x47C2D0 HudMap ·
0x47C2D8/0x47C2EC/0x47C2F1 waterdetailtexture/underwatertexture/
watertexture · 0x47C310 maps\ · 0x47C318 waterlevel · 0x47C324
tcffilename · 0x47C33C texturedata · 0x47C358 mechwarrior4::mwmap ·
0x47C350 class · 0x47C374/0x47C36C nevercull/cull · 0x47C380/
0x47C388 grid/.erf · 0x47C398 gamedata · 0x47C330 .index.tcf ·
0x47C348 .fgd · 0x47C3F0 textures.hint · 0x47C400 Content\Textures\
(also Content\Textures\Maps\) · 0x47C450 %s\%s.Tga · 0x47C4B0
TexturePool · 0x47C4E8 .mw4 · 0x47C4F0 Resource\Maps\ · 0x47C500
.build · 0x47C508 Content\ · 0x47C514 Maps\%s\%s.build · 0x47C444/
0x47C4DC .instance/instance · 0x47C2B4 \tctd.ini · 0x47C68C
\tctd.ini -bsp · 0x47C69C Tctd /f Content\Maps\ · 0x47C6B4 Tctd.exe ·
0x47C6C0/C4/C8/CC/D4 /i /s /y /uid /p ·
0x47C0A0 \ · 0x47C528 There was an Error in Createing your map ·
0x47C564 Map created successfully. Run Mission Editor to create a mission
with this map · 0x47C5EC A map by this name exists, OverWrite? · 0x47C628
Give the map a valid name · 0x47C64C Cannot Find Tctd.exe,Make sure this
file exists and try again · 0x47C244 Bitmap Images
(*.tif,*.gif,*.tga,*.png)|*.tif;*.gif;*.tga;*.png|| · 0x47C81C
FGD Files (*.fgd)|*.fgd||
Heightfield/texture rule strings: 0x47C14C/0x47C184/0x47C1BC/0x47C1F8
height too large/small, width too large/small · 0x47C0C0 multiple-of-256 ·
0x47C114 24-bit-for-interest-data · 0x47C0A4/0x47C230 HeightField Does
Not Exist/Bad Height Field · 0x47C714/0x47C734 Terrain Texture Does Not
Exist/Bad Terrain Texture · 0x47C77C Detail Texture MUST be 256x256 ·
0x47C7E0 For a Heightfield of %ix%i Terrain Texture should be %ix%i ·
0x47C924 Water Texture Texture MUST be 256x256
11. Key data-structure take-aways¶
- MapCreator writes an info-file map definition tagged
class=mechwarrior4::mwmap(thegamedatasection keys in §4) — the same GameOS key/value grammar as.nfm. - The heavy lifting (binary terrain/BSP + final packing into the
#VBD.mw4) is delegated to the externalTctd.exe; MapCreator only stages inputs and textures. The shippedResource\Maps\*.mw4are therefore Tctd's output, not MapCreator's. - Heightfields are strictly bounded: 256..1024 wide, 256..2048 high, both a multiple of 256, 24-bit when "interest data" is enabled.
- Terrain texture must match the heightfield size; detail/water textures are fixed 256×256.
- The
#VBDmap archives use a multi-group directory (each asset group has its own path prefix) — a layout detail not captured invbd.h, whoseentry_count=63field is not the member count. Worth a follow-up update toinclude/mw4/vbd.h.