Closes the 5 remaining cpp/command-line-injection alerts plus 3
cpp/integer-multiplication-cast-to-long and 1 cpp/uncontrolled-arithmetic
in tools/editor/. (The other open high alerts are all in extern/
third-party headers — imgui, stb_image, miniaudio — and are out of
scope for us to patch.)
Critical (cmd-injection) → shell-free runChild() helper:
- cli_zone_packs.cpp:41,175,182 (+ a 4th site at line 235 that the
alert tooling missed). runSilently() refactored to take argv0+args.
- cli_audits.cpp:68 — per-zone `--validate-…` self-invocation.
- cli_gen_audio.cpp:386 — per-tone `--gen-audio-tone` self-invocation.
- editor_ui.cpp:3038 — manifest "open in default app" used a shell
concat (open / start / xdg-open). Now uses cli_subprocess::runChild
with the platform binary directly.
High (int-mul overflow) → widen one operand to size_t:
- wowee_terrain.cpp:272 — `resolution * resolution * 3` for the zone
map pixel buffer.
- terrain_editor.cpp:1848,1859 — `w * h` for stbi_load{,_16} heightmap
resize loops; precomputed pixelCount and switched the loop counter
to size_t.
High (uncontrolled-arithmetic) → bounded increment:
- editor_ui.cpp:987 — noise-seed `>>` button incremented `int` without
bound. Clamp to INT_MAX.
nlohmann::json serialization throws on NaN/inf floats, which would
abort the entire WOT save and leave the user with an unsaved zone
state. Scrub on the way out so a single corrupted placement can't
take down the whole save.
Sanitize at write time too, not just on load. A mid-edit NaN spike
(e.g. brush operation that produced NaN before being committed) would
otherwise be persisted into the WHM and require the load-time guard
to clean it up forever after. AlphaSize is also capped at 64KB to
match the loader cap.
- exportZoneMap(): renders terrain as colored top-down image with
height-based coloring (blue lowlands → green plains → brown hills →
white peaks), water overlay, hole visualization, doodad markers
- Configurable resolution (128-2048px, default 512)
- Auto-exported as zone_map.png alongside other assets on save
- File > Export Zone Map menu with resolution slider
- Useful for documentation, server admin tools, custom map websites
Architecture fixes for open format data fidelity:
- WOT now serializes full doodad/WMO placement arrays (positions,
rotations, scale, flags, doodad sets) — was only storing counts,
causing all placed objects to be lost on WOT round-trip
- WOT loader parses placements back into ADTTerrain for client rendering
- WOB Material struct added: preserves WMO material flags, shader type,
and blend mode during WMO→WOB conversion (was geometry-only)
- WOB doodad rotation: quaternion→euler conversion instead of hardcoded
zero (placed doodads inside buildings now retain their orientation)
- importOpen() deduplicated: delegates to pipeline::WoweeTerrainLoader
instead of duplicating 100 lines of parsing code
- WHM binary now includes per-chunk alpha map data (alphaSize + data)
so custom zones render with proper texture blending in the client
- WOT exporter rewritten with nlohmann/json (was manual string concat)
- WOT loader rewritten with nlohmann/json (was naive substring parsing)
- Backward compatible: old WHM files without alpha data still load fine
- Exports 129x129 grayscale PNG showing terrain elevation
- Auto-normalizes to 0-255 based on actual height range
- Useful for zone documentation, thumbnails, and previews
- Auto-exported alongside WOT/WHM/normals on every save
Novel open terrain format unique to wowee:
.wot (Wowee Open Terrain) — JSON metadata:
- Tile coordinates, chunk grid dimensions, texture list
- Per-chunk layer assignments and hole bitmasks
- Water data per chunk (type, height)
- Format version "wot-1.0"
.whm (Wowee HeightMap) — binary heightmap:
- "WHM1" magic header
- 256 chunks × (baseHeight + 145 float heights) = 148KB
- Direct float storage, no compression, fully portable
Both formats are entirely novel — no ADT, no WDT, no Blizzard
structures. The WCP content pack can bundle .wot/.whm instead of
ADT/WDT for fully open redistribution.
Import/export functions: WoweeTerrain::exportOpen() / importOpen()