A map is playable or it is not, and the difference is usually one question: can the worker who spawns beside your town hall actually walk to gold. Every starting position on every map in the library answers yes, and that guarantee is not a hope. It is a check that runs on every import and rejects the positions that fail it.

Getting there meant reading maps this engine did not write. It could generate one perfectly well. It could not read one that anything else had produced.
The two formats are .pud and .w3x, the map files of two earlier generations
of real-time strategy game, and they are binary rather than text: not
something you can open in an editor, but a precise sequence of bytes whose
meaning comes entirely from a layout you have to know in advance. .pud is a
flat run of tagged sections — a four-byte label, a length, then that many bytes
of content. .w3x is an MPQ archive, a container format of the era holding
several files inside one, so it has to be unpacked before anything can be read.
They describe terrain, resources and start positions in layouts that share nothing with each other and nothing with this game's own save format, and turning either of them into something playable is about 6,700 lines of code.
Two front ends, one shared middle
The two parsers are the only code that knows what a source byte means, and neither of them decides anything. Both produce the same neutral description of a map, and everything opinionated happens behind that: normalising resources, cleaning up terrain, validating start positions, placing neutral camps, saving and rendering the thumbnail, identically for both sources, because the half that does all that never learns which format it came from. The description does carry a field saying which format it was, and nothing ever branches on it.
That split pays immediately on things only one format has. A .w3x carries
per-tile height and a separate ramp layer. A .pud has no height at all. So
cliff levels and ramps are optional in the middle, and a flat map leaves them
out and saves exactly as it did before elevation existed, which matters because
cliffs in this game are height rather than walls,
and that change had to not rewrite every map that predated it.
The converter also uses the game's own terrain, tile map, walkability grid, save format, map generator and random number source directly, rather than copies of them living in a tools directory. If the game's rules move, the import breaks loudly instead of quietly emitting maps the renderer cannot draw.
The two parsers are deliberately free of dependencies, which means they cannot use the game's own terrain table and have to hold a hand-copied version of it. That copy is the one place drift could hide, so an equality check runs once before a single file is opened. It compares all ten terrain values in both directions, compares the copied list of creep families against the game's own, and throws with a per-key difference rather than converting anything. A copy that has silently fallen behind writes wrong tiles into every map it touches, and that failure is invisible until a unit walks across a lake.
Why an importer should refuse to write a name
Reading another game's file format is a normal thing to do. Taking its content is not, and the line between the two is easy to cross by accident, which is why here it is a guard that throws.
Every converted map is renamed. Where no name has been written by hand, one is built from fixed word lists, seeded by a hash of the source file's contents, so re-running the importer never churns the library and two copies of the same file under different names land on the same result. Then, before anything is written, the authored text is checked:
- A list of four lowercase strings covers the source franchise, its publisher and two common abbreviations. Any of them anywhere in the name, description or slug throws.
- The source filename is checked, and so is the source map's own internal title, with trailing distribution tags stripped, because those are not part of an identity.
- A source title of several words is caught anywhere in the text, because a multi-word title is distinctive enough that reusing it is reusing the identity.
- A one-word source title is caught only as a whole word, and only in the name, never the description, with a 45-entry allow-list of ordinary English nouns that happen also to be source filenames.
The scope is as deliberate as the checks. Only authored text is scanned, meaning the name, the description and the slug:
A leak can only ever originate in prose that was written by hand.
The rest of a saved map is the game's own. The terrain is compressed digits, the
map type and biome are this game's own values, and building names are this
game's constants. Scanning the whole saved file would flag the map type
islands as a leak, which is not a leak, it is the map type.
Three patterns worth taking out of an importer
None of the three ideas holding this together are about map formats.
One shared middle, and the front ends decide nothing. Two parsers that share no code both emit the same neutral description, and everything opinionated happens once behind it. The test for whether the line is in the right place is whether the back half ever branches on which source it is holding. This one knows and never asks. If your back half needs to know, the front end has not normalised enough, and what you actually have is two conversion pipelines that will diverge on the next fix to either.
Run the importer against the real types rather than copies of them. Using the game's own terrain, grid and save format inside a tool means a rule moving breaks the import loudly instead of quietly producing output the game cannot use. Where a copy is genuinely unavoidable, pin it with an equality check that runs before any file is opened and throws with a per-key difference. A copy that has silently fallen behind is worse than no copy, because it produces plausible output.
Encode a legal constraint as a test, not as a policy document. A rule that says "nothing derived from their content ships" lives in a document that has to be read and remembered. The same rule as a guard that runs before anything is written, and throws, is enforced on every run, including the runs driven by a model with no recollection of having agreed to it. The design work is all in the scope. Check only the text that was authored by hand, because everything else in the output is your own vocabulary, and a guard that fires on your own map type gets switched off inside a week. After that there is no guard at all, which is a worse position than never having written one.
A tile that is walkable in one table and a wall in another
The .pud terrain layer stores a tile as either solid or boundary, and the same
four bits are read out of both. They do not mean the same thing. On a solid tile
those bits are a terrain class. On a boundary tile they are an index into the
format's table of transitions between terrains.
Exactly one value comes out wrong if you assume otherwise. Class 4 as a solid tile is dirt, on 3,441 tiles across the library. Class 4 as a boundary tile is the transition from land to mountain, on 36,281 tiles, and emitting dirt there punches a walkable gap through every cliff on every map.
That was not found by reading a specification. It was found by cross-tabulating the terrain layer against the movement bitmap that ships inside the same file, which is a second, independent statement of what is passable. After the two adjustments it forced, the decoded terrain agrees with the source's own movement data on 100% of 1,789,952 tiles.
The same cross-check produced a more useful negative result. The .pud format
has two water classes that look like they might be shallow and deep, and
shallow water in this game is a ford you walk through,
so getting that wrong turns every crossing into a wall and rewrites the design
of the map. Both classes turned out to be a rendering cue for depth rather than
a movement class, and the movement data agrees with treating both as impassable
on 100% of those same tiles. If a single one of them had been wadeable, that
agreement would not be 100%. So the .pud side never emits shallow water and
the .w3x side does, because that format genuinely has both and says so twice.

Does this start position actually reach gold?
The check that answers that is used both when the importer picks start positions and when a separate tool verifies a finished map, deliberately the same code, because two implementations of "is this start playable" that disagree is how gold-starved positions ship. A start needs all four of: a 7x7 buildable box, a landmass of at least 60 tiles, a stone tile reachable on foot, and a gold tile reachable on foot.
Box and landmass are different questions and both are needed. A 7x7 box fits perfectly well on a 28-tile islet, while a 4x4 town hall plus the walkable ring around it needs about 36 tiles and leaves nowhere for the starting workers.
Landmass is measured by building the game's real walkability grid from the tile map and asking it how big the connected land area is, rather than by a hand-rolled flood fill. That was not a stylistic choice. The hand-rolled version got two structural things wrong that no threshold could have compensated for. The real grid marks land next to water as unwalkable for ground units, which on one start is the difference between 58 terrain tiles and 28 you can stand on. And the real grid spreads only through shared edges, where a flood fill that also spreads diagonally squeezes through water pinches a ground unit cannot use. On one map that merged genuinely separate 97-tile and 103-tile islands into a single 1,135-tile area, overstating it by nearly twelve times. That one silently disabled the starved-position rule entirely, because 25 positions then measured as 100% of the roomiest position on their own map.
Trees are handled in opposite directions on purpose. Reachability treats a tree as passable, since a worker chops it and the tile becomes a walkable stump. Landmass counts only land you can stand on, because it asks how much room the player has when the match begins. Counting trees toward landmass was tried, and four clearings of roughly 240 tiles merged into one 16,145-tile area, after which every start on every map reported as roomy.
The run that lost a format and reported success
The existing guard refused to write the library index when a whole source format converted zero maps, which covered the incident it was written for. It did not cover the quieter shape of the same problem. A parser edited mid-session started throwing on every file of one format, and the run printed a healthy-looking "wrote N map(s)" line, exited successfully, and passed its own self-test.
Two artefacts produced by the same run cannot cross-check that run.
Both the import log and the maps on disk had been written by the bad run, so they agreed with each other perfectly while describing the wrong thing. The fix is 49 lines and two ideas. Failures are now budgeted per format and counted separately from skips, because a skip is a named expected category (a map with no gold mines is not a skirmish map) and a failure is an exception no category covers, so more than 10% of a format failing aborts the run rather than republishing a thinned library. And then there is an accounting check, which is the part to steal:
// The denominator must come from OUTSIDE the run. Counting what the run
// produced and comparing it to what the run reported proves nothing.
const discovered = walkSourceDirs().length; // the disk, not the run
const accounted = converted.length + skipped.length + failed.length;
if (accounted !== discovered) {
throw new Error(
`accounting: ${accounted} of ${discovered} sources accounted for. ` +
`${discovered - accounted} vanished without being converted, skipped or failed.`,
);
}Any batch job that writes both output and a report has this hole in it, and the report is only evidence if its denominator was measured independently of the run it describes.
The whole converter is checked by a standalone self-test with no framework under it, which is the house style for tests here, and the previews it bakes are the same ones the map gallery renders. The next thing the pipeline wants is not another format. It is a viability check that reasons about start positions in pairs rather than one at a time, because two perfectly playable starts nine tiles apart are still a bad map.





