The unpacker used info.name verbatim as the destination subdirectory.
A malicious WCP could carry a name like '../etc' or '/usr/bin' to
write extracted files outside destDir. Now slugified to alphanumeric
+ underscore/dash, matching the server module slug rule.
The previous --validate output told you whether *some* file of each
type existed, which was hard to act on for partially-valid zones.
Now reports the per-format file count and how many failed magic
validation, e.g. 'WOM (12 invalid: 2)' so a zone author can spot
missing or corrupted models without grepping through file listings.
readInfo previously trusted fileCount/infoSize blindly, so a malicious
or corrupted WCP could allocate a 4GB string just to print metadata
via --info-wcp. Same 1M file / 16MB info caps now applied. Also
categorizes .woc collision files (was bucketed under 'other').
Two write-side guards mirroring the unpack-side ones:
- Path length truncated to 1KB (matches unpack cap; long paths would
silently wrap u16 and corrupt the pack)
- Files >4GB on disk skipped with a zero-length entry rather than
silently producing a truncated dataSize that overflows uint32
Path length cap (1KB) — uint16 can hold 64KB but no real zone path
should exceed 256 chars. Path traversal check extended to also catch:
- Windows backslash absolute paths ('\' at start)
- Windows drive-prefixed paths ('C:\...')
A WCP downloaded from a forum and unpacked on Windows would otherwise
have these vectors open.
Three security/robustness guards on unpackZone:
1. fileCount > 1M or infoSize > 16MB rejected upfront — would OOM on
the next allocation.
2. Per-file dataSize > 256MB rejected — single malicious entry could
exhaust memory mid-extraction.
3. Path traversal ('..' or absolute paths) rejected — would write
outside destDir/<zoneName>/ and clobber system files.
WCPs are user-shareable archives, so a hostile pack downloaded from a
forum should not be able to OOM the editor or write to /etc.
packZone stores files relative to the zone subdirectory (e.g. just
'MyZone_32_32.adt'), so unpacking to 'custom_zones/' produced files at
'custom_zones/MyZone_32_32.adt' — without the zone subdir the loader
expects. Now reads the info JSON to extract the zone name and unpacks
to 'custom_zones/<zoneName>/' so imported zones load correctly.
ContentPacker.validateZone only matched WOM1 magic (0x314D4F57). Any zone
exported with animated (WOM2) or multi-batch (WOM3) models was scored as
having invalid WOM files, lowering the open-format score from 7/7 to 6/7
even though everything is correct. Now accepts the WOM family.
New format: WOC (Wowee Open Collision) — binary collision mesh for
custom zone walkability. Magic WOC1 (0x31434F57).
- WoweeCollisionBuilder::fromTerrain() generates collision triangles
from terrain heightmap with slope classification (50 deg threshold)
- Per-triangle flags: walkable (0x01), water (0x02), steep (0x04)
- Respects terrain holes (skips triangles in hole regions)
- Binary save/load with bounds, tile coords, triangle data
- Auto-exported on zone save alongside WOT/WHM/WOM/WOB
- Added to content pack validation (score now 0-7)
- FORMAT_SPEC.md v1.1 updated with WOC binary layout
- 19 new test assertions: flat terrain generation (32k tris all
walkable), save/load round-trip, hole skipping
- 328 total assertions across 84 test cases
- Terrain stamps save/load to JSON: reuse terrain features across zones
and sessions. Save/Load buttons in Sculpt > Stamp/Clone panel
- WCP Inspect now shows full file breakdown: terrain/model/building/
texture/data counts with total size. Powered by readInfo file list
parsing with auto-categorization by extension
- stats.json now includes chunk count, triangle count, tile count, and
editor version alongside existing object/NPC/quest/texture counts
- Fix unprotected std::stoi in custom zone WOT filename parser
- Quest editor: add loadFromFile() with nlohmann/json, chain validation
with circular reference detection, wire into ADT load and save pipeline
- Project: replace naive substring JSON parsing with nlohmann/json for
both save() and load(), fix shell injection in gitCommit()
- Content pack: replace manual JSON with nlohmann/json, validate binary
format magic numbers (WHM1/WOM1/WOB1), add WOB to openFormatScore
(now scores 0-6), mark invalid files with (!) in summary
- ContentPacker::validateZone() scans a zone directory and checks
for all open format files (WOT, WHM, PNG, WOM, zone.json, etc.)
- openFormatScore(): returns 0-5 based on how many open formats present
- summary(): human-readable list of found formats
- Foundation for quality gate on WCP export: warn if zone uses
Blizzard formats that could be converted to open versions
- WCP format: simple binary archive with magic header, JSON manifest,
and concatenated file data. Not tied to any proprietary format.
- ContentPacker::packZone(): bundles all zone files from output dir
into a single .wcp file (terrain, objects, creatures, quests, manifest)
- ContentPacker::unpackZone(): extracts .wcp to a directory
- ContentPacker::readInfo(): reads pack metadata without extracting
- Format: "WCP1" magic + fileCount + infoJSON + file table + data
- Foundation for distributing custom zones to other wowee users
and private servers
Note: currently bundles ADT/WDT files as-is. Future: convert terrain
to open format (heightmap + JSON) for fully open redistribution.