From 5b6f59bbbda38601d9ae26b6e4acb6b6f82822bd Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 06:05:17 -0700 Subject: [PATCH] fix(adt): scrub NaN/inf in MDDF + MODF placement floats ADT placement positions and rotations are loaded as raw floats from the binary chunks. A corrupted MDDF/MODF entry could feed NaN into the M2/WMO instance transform and crash render. Now position/rotation are scrubbed for both MDDF doodads and MODF buildings; MODF also scrubs the extentLower/extentUpper bounding box used for cull tests. --- src/pipeline/adt_loader.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pipeline/adt_loader.cpp b/src/pipeline/adt_loader.cpp index f9f3f472..a00daa2c 100644 --- a/src/pipeline/adt_loader.cpp +++ b/src/pipeline/adt_loader.cpp @@ -216,6 +216,12 @@ void ADTLoader::parseMDDF(const uint8_t* data, size_t size, ADTTerrain& terrain) placement.rotation[2] = readFloat(data, offset + 28); placement.scale = readUInt16(data, offset + 32); placement.flags = readUInt16(data, offset + 34); + // Sanitize NaN/inf — corrupted MDDF entries would propagate bad + // floats into the WMO/M2 instance transform and crash render. + for (int k = 0; k < 3; k++) { + if (!std::isfinite(placement.position[k])) placement.position[k] = 0.0f; + if (!std::isfinite(placement.rotation[k])) placement.rotation[k] = 0.0f; + } terrain.doodadPlacements.push_back(placement); } @@ -255,6 +261,14 @@ void ADTLoader::parseMODF(const uint8_t* data, size_t size, ADTTerrain& terrain) placement.scale = readUInt16(data, offset + 62); if (placement.scale == 0) placement.scale = 1024; } + // Same NaN scrub as MDDF entries — corrupted MODF would crash WMO + // instance transform. + for (int k = 0; k < 3; k++) { + if (!std::isfinite(placement.position[k])) placement.position[k] = 0.0f; + if (!std::isfinite(placement.rotation[k])) placement.rotation[k] = 0.0f; + if (!std::isfinite(placement.extentLower[k])) placement.extentLower[k] = 0.0f; + if (!std::isfinite(placement.extentUpper[k])) placement.extentUpper[k] = 0.0f; + } terrain.wmoPlacements.push_back(placement); }