Older WCP files packed on Windows (before pack-side normalization
was added) carry backslash separators. Normalize to '/' first so
the unpack works on any platform — and so the traversal check sees
a consistent canonical form (no more '\' special case).
When the camera looks straight up/down, projecting forward onto XY
gives a zero vector — glm::normalize then returns NaN. The original
length<0.001 fallback ran AFTER the divide-by-zero, and NaN length
< 0.001 is false (NaN comparisons return false), so the fallback
never fired. Length-check the source before normalizing.
start == end would call glm::normalize on a zero vector, producing
NaN dir/perp and NaN ribbon vertex positions. Vulkan would either
drop the draw silently or trip a validation error. Hide the preview
when the segment is degenerate.
Symmetric with the load-side scrub. Without this, a stamp captured
on terrain that had a NaN mid-edit would throw on serialize and
abort the whole save.
WCP packs created on Windows would store paths with backslashes;
unpack on Linux/macOS would either fail the path-traversal check
('\' treated as absolute prefix) or land each file as a single
opaque filename rather than a directory tree. Normalize to '/' on
write so the format is portable in both directions.
Tests cover:
- non-existent nextQuestId is flagged
- orphan quests (no questgiver, no turn-in) are flagged
- turn-in only quest is accepted (auto-completed quest pattern)
- circular chain is detected
Renamed test_sql_escape → test_editor_units since the file now
houses both SQL escape and quest validation tests.
5 tests covering: doubled single quotes (King's Land case),
backslash escaping, ordinary text passthrough, control characters
(NUL drop, CR/LF/tab/Ctrl-Z escape sequences), and combined
escapes. Locks in the recent escape expansion that fixed the
multi-line INSERT bug.
A NaN move/rotate/scale delta would poison every selected object's
transform permanently and produce NaN model matrices in the
renderer. Reject upfront.
Same defensive pattern as paintAlongPath. carveRiver, flattenRoad,
and createRidge all called glm::normalize on a possibly-zero
direction vector, then divided by lineLen later. NaN endpoints
short-circuited dist comparisons and applied the height delta
to every vertex on every chunk.
Two bugs:
1. NaN start/end produced NaN distances that the chunk-skip check
(dist > width + 40) treated as 'always within range', so every
chunk got painted.
2. Zero-length line caused glm::normalize to return NaN; same
downstream effect.
Compute lineDir manually after the length check so we never hit
the divide-by-zero path.
Same defensive pattern as createHill — NaN center/radius would
short-circuit dist comparisons and apply the height delta to every
vertex on every chunk. Reject upfront.
Same NaN-comparison short-circuit pattern: dist >= radius is false
when dist is NaN, so the loop body would run for every vertex and
write garbage heights / bend the terrain unbounded.
Three issues:
1. NaN distance returned 1.0 (full influence) because distance >=
radius is false for NaN; the inner-radius check then returned 1.
2. Non-positive radius would divide by zero in the t computation.
3. falloff = 0 produces division by zero in the outer falloff path.
Also clamps falloff to [0,1] so a slider extreme can't break the math.
Same NaN-comparison short-circuit bug as the texture painter — a
brush with a NaN cursor position would mark every vertex in every
'affected' chunk as full influence and silently rewrite huge
swaths of terrain. Reject upfront in applyBrush.
NaN comparisons return false, so the dist >= radius early-out
would never fire and the falloff path would skip its inner check
too — the brush would paint full strength on every texel in the
chunk. Reject upfront.
Same defensive validation as loadADT — out-of-range tile coords
would generate broken save paths. Also guards against a NaN
baseHeight slider (would propagate into every terrain vertex).
A stray gigantic name/description/author field would inflate the
info JSON past the 16MB unpack cap and make the pack unreadable
via readInfo/unpackZone. Caps mirror the zone manifest limits.
A tileX/tileY outside 0..63 would generate ADT paths the asset
manager refuses, then poison the manifest.tiles entries on save.
Reject upfront with a log message.
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.
validateChains now also flags quests with no questgiver and no
turn-in NPC — those are unreachable in-game and a common authoring
mistake. Also replaced the O(n²) inner lookup with an O(1)
unordered_map of id → nextId so circular detection scales.
WoB allows uint32 indices but WMO format is uint16. The previous
static_cast would silently wrap a >65k index into a wrong-but-
valid value — producing visible mis-stitched triangles in the
renderer. Now log a warning once per group and clamp to 0
(degenerate triangle) so the bug is visible.
--info-wcp gives counts and totals; --list-wcp gives the full file
listing sorted by path. Useful for spotting missing texture/model
entries before unpacking and shipping a zone.
Symmetric with the load-side index clamp. A WoM whose indices
reference past the vertex buffer would crash the GPU vertex shader;
the save side now clamps to 0 (degenerate triangle) so the file
matches what the load guard would produce.
Symmetric with the load-side validation. A WOM3 batch whose
indexStart+indexCount exceeds the index buffer, or whose texture
index points past the texture array, would otherwise emit an
invalid file that the load-time guard then has to drop.
Filter at save instead so the on-disk file stays compact and
self-consistent.
Symmetric with the existing load-side guards. A bone with a NaN
pivot poisons its child bones' world matrices; an out-of-range
parent index would walk past the bones array during evaluation.
Symmetric scrub on the WoB save path matching the existing load
guard. A manually-constructed WoweeBuilding with NaN vertices
would otherwise persist them and force the load-time scrub to
re-clean the same data on every reload.
The load side already scrubs keyframe translation/rotation/scale
floats, but fromM2 → save → load is the typical path: a corrupt
M2 source would write NaN keyframes that the load-time guard would
have to clean up on every subsequent load. Symmetric scrub here
ensures the file is clean from the start.
movingSpeed also defaults to 0 if non-finite (matches load).
The previous escape only doubled quotes and backslashes. A quest
description containing a literal newline would emit a multi-line
INSERT that breaks per-line execution scripts; a NUL byte could
prematurely terminate the string in non-length-prefixed clients;
Ctrl-Z is the historical MySQL string terminator on Windows.
Now full MySQL/MariaDB string-literal escape: NUL drops, CR/LF/tab
become \r/\n/\t, Ctrl-Z becomes \Z.
Verifies the recent WOB doodad-transform sanitize. A WOB with NaN
position/rotation/scale on a doodad should load with the doodad
zeroed out (position/rotation 0, scale 1). Prevents regressing
the GPU crash that NaN model matrices would cause.
The existing WOT round-trip test asserted liquidType==5; the recent
commit clamped >3 to 0, so the test would have failed once rebuilt.
Updated the test data to use type=3 (slime, in valid range).
Adds 5 new hardening test cases:
- WOT clamps OOR tileX/tileY to 32
- WOT clamps OOR water liquidType to 0
- WOC load skips degenerate triangles
- WOC rejects > 2M triangle headers
- WOC clamps OOR tileX/tileY to 32
Catches regressions in the defensive bounds added across recent
commits.
Completes the --info-* family. Reports total/chained quest counts,
reward/item counts, total XP awarded, objective-type breakdown
(kill/collect/talk), and any quest-chain validation errors. Lets
zone authors spot broken chains, missing rewards, and lopsided XP
balance from the command line.
Mirrors --info-creatures and the other format inspectors. Reports
total placement count, M2/WMO breakdown, unique source paths, and
scale range. Useful for spotting empty zones, accidental scale
extremes, or duplicated placements before packing.
Mirrors the existing --info-wom/wob/woc/wot/wcp inspectors. Reports
total spawn count, hostile/questgiver/vendor/trainer flag counts,
behavior breakdown (stationary/wander/patrol), and unique displayId
count. Useful for triaging zone NPC content from the command line.
A WMO with a NaN doodad quaternion would produce NaN euler angles
through glm::eulerAngles() and persist them into the WoB. Identity
quaternion fallback for a non-finite source, plus NaN scrub on
position/rotation/scale separately so the converted WOB is always
load-safe.
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').
WoW liquid types are 0=water/1=ocean/2=magma/3=slime. A user-edited
WOT could carry an out-of-range value that the editor renderer
silently maps to plain water but the server treats as undefined.
A WOT water entry with non-finite height would push NaN through
the water mesh builder and produce a degenerate Vulkan draw
(invisible water at best, GPU hang at worst).
Same scrub now applied symmetrically on the save side so a
corrupted in-memory doodad transform can't be persisted into a
WOB and then have to be cleaned up on every subsequent load.
Already had a guard for scale; extending to position/rotation too.
A WoB with non-finite doodad transforms produces NaN model matrices
that propagate into the M2 instance SSBO and crash the GPU.
Mirrors the load-side sanitize. nlohmann throws on NaN/inf, so
a corrupted in-memory baseHeight or volume would abort the manifest
save and lose all zone-level settings (flags, audio config, etc.).
Same json-serialization-safety fix as for NPC and WOT saves.
Object position/rotation NaN would abort the entire objects.json
save and lose all placement work in the session.
nlohmann::json throws on non-finite serialization, which would
abort the entire creatures.json save and lose every spawn change
in the session. Scrub position/orientation/scale/radii/patrol
floats on the way out so a single corrupt NPC can't kill the
batch save.
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.
map_dbc.MapName / area_table_dbc.AreaName are varchar(100). Edited
zone.json could carry longer strings that would either fail the
INSERT or silently truncate. Cap mapName/displayName at 100,
biome at 64, description at 4096.
creature_template.name is varchar(100) in AzerothCore. Edited
creature JSON could carry longer names that would either fail
the INSERT or silently truncate. Cap name at 100 and modelPath
at 1024 on load.
quest_template.LogTitle is varchar(200) in AzerothCore. Edited
quest JSON could carry longer strings that would either fail the
INSERT or silently truncate at the server. Cap title at 200 chars
and the longer text fields at 8KB on load.
Mirrors the editor-side ZoneManifest sanitize on the discovery
scanner used by the launcher and asset manager. A custom_zones/
zone with bad mapId or out-of-range tile coords would otherwise
appear in the picker and silently fail when the user selects it.
Mirrors the existing --info-wom/--info-wob/--info-woc/--info-wcp
inspectors. Reports the tile coord, populated chunks, layer count,
water count, texture/doodad/WMO counts, and computed height range
across all chunks. Useful for triaging zones from the command line
without opening the GUI.