Kelsidavis-WoWee/src/pipeline/wdt_loader.cpp
Kelsi 16d88f19fc Fix instance portals: WDT byte order, box trigger sizing, suppress ping-pong, WMO cache cleanup
- Fix WDT chunk magic constants to big-endian ASCII (matching ADTLoader)
- Add minimum effective size for box area triggers (90 units, like sphere 45-unit radius)
- Add areaTriggerSuppressFirst_ flag to prevent portal ping-pong on map transfer
- Add WMORenderer::clearAll() to clear models/textures on map change (prevents GPU crash)
- Increase WMO texture cache default to 8GB
- Fix setMapName called after loadTestTerrain so WMO renderer exists
- Save/restore player position around CMSG_AREATRIGGER to prevent bad DB persistence
2026-02-27 04:59:12 -08:00

110 lines
3.6 KiB
C++

#include "pipeline/wdt_loader.hpp"
#include "core/logger.hpp"
#include <cstring>
namespace wowee {
namespace pipeline {
namespace {
uint32_t readU32(const uint8_t* data, size_t offset) {
uint32_t v;
std::memcpy(&v, data + offset, 4);
return v;
}
uint16_t readU16(const uint8_t* data, size_t offset) {
uint16_t v;
std::memcpy(&v, data + offset, 2);
return v;
}
float readF32(const uint8_t* data, size_t offset) {
float v;
std::memcpy(&v, data + offset, 4);
return v;
}
// Chunk magic constants (big-endian ASCII, same as ADTLoader)
constexpr uint32_t MVER = 0x4D564552; // "MVER"
constexpr uint32_t MPHD = 0x4D504844; // "MPHD"
constexpr uint32_t MAIN = 0x4D41494E; // "MAIN"
constexpr uint32_t MWMO = 0x4D574D4F; // "MWMO"
constexpr uint32_t MODF = 0x4D4F4446; // "MODF"
} // anonymous namespace
WDTInfo parseWDT(const std::vector<uint8_t>& data) {
WDTInfo info;
if (data.size() < 8) {
LOG_WARNING("WDT data too small (", data.size(), " bytes)");
return info;
}
size_t offset = 0;
while (offset + 8 <= data.size()) {
uint32_t magic = readU32(data.data(), offset);
uint32_t chunkSize = readU32(data.data(), offset + 4);
if (offset + 8 + chunkSize > data.size()) {
LOG_WARNING("WDT chunk extends beyond file at offset ", offset);
break;
}
const uint8_t* chunkData = data.data() + offset + 8;
if (magic == MVER) {
if (chunkSize >= 4) {
uint32_t version = readU32(chunkData, 0);
LOG_DEBUG("WDT version: ", version);
}
} else if (magic == MPHD) {
if (chunkSize >= 4) {
info.mphdFlags = readU32(chunkData, 0);
LOG_DEBUG("WDT MPHD flags: 0x", std::hex, info.mphdFlags, std::dec);
}
} else if (magic == MWMO) {
// Null-terminated WMO path string(s)
if (chunkSize > 0) {
const char* str = reinterpret_cast<const char*>(chunkData);
size_t len = std::strlen(str);
if (len > 0) {
info.rootWMOPath = std::string(str, len);
LOG_DEBUG("WDT root WMO: ", info.rootWMOPath);
}
}
} else if (magic == MODF) {
// MODF entry is 64 bytes (same layout as ADT MODF)
if (chunkSize >= 64) {
// nameId at offset 0 (unused for WDT — path comes from MWMO)
// uniqueId at offset 4
info.position[0] = readF32(chunkData, 8);
info.position[1] = readF32(chunkData, 12);
info.position[2] = readF32(chunkData, 16);
info.rotation[0] = readF32(chunkData, 20);
info.rotation[1] = readF32(chunkData, 24);
info.rotation[2] = readF32(chunkData, 28);
// extents at 32-55
info.flags = readU16(chunkData, 56);
info.doodadSet = readU16(chunkData, 58);
LOG_DEBUG("WDT MODF placement: pos=(", info.position[0], ", ",
info.position[1], ", ", info.position[2], ") rot=(",
info.rotation[0], ", ", info.rotation[1], ", ",
info.rotation[2], ") doodadSet=", info.doodadSet);
}
}
offset += 8 + chunkSize;
}
LOG_WARNING("WDT parse result: mphdFlags=0x", std::hex, info.mphdFlags, std::dec,
" isWMOOnly=", info.isWMOOnly(),
" rootWMO='", info.rootWMOPath, "'");
return info;
}
} // namespace pipeline
} // namespace wowee