#include "loadpgrd.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include namespace ESM { template T> void decompose(T&& v, const auto& f) { f(v.mX, v.mY, v.mGranularity, v.mPoints); } template T> void decompose(T&& v, const auto& f) { char padding[2] = { 0, 0 }; f(v.mX, v.mY, v.mZ, v.mAutogenerated, v.mConnectionNum, padding); } Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3]) { mX = static_cast(rhs[0]); mY = static_cast(rhs[1]); mZ = static_cast(rhs[2]); mAutogenerated = 0; mConnectionNum = 0; return *this; } Pathgrid::Point::Point(const float rhs[3]) : mX(static_cast(rhs[0])) , mY(static_cast(rhs[1])) , mZ(static_cast(rhs[2])) , mAutogenerated(0) , mConnectionNum(0) { } Pathgrid::Point::Point() : mX(0) , mY(0) , mZ(0) , mAutogenerated(0) , mConnectionNum(0) { } void Pathgrid::load(ESMReader& esm, bool& isDeleted) { isDeleted = false; mPoints.clear(); mEdges.clear(); // keep track of total connections so we can reserve edge vector size size_t edgeCount = 0; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().toInt()) { case SREC_NAME: mCell = esm.getRefId(); break; case fourCC("DATA"): esm.getSubComposite(mData); hasData = true; break; case fourCC("PGRP"): { esm.getSubHeader(); uint32_t size = esm.getSubSize(); if (size != getCompositeSize(Point{}) * mData.mPoints) esm.fail("Path point subrecord size mismatch"); else { mPoints.reserve(mData.mPoints); for (uint16_t i = 0; i < mData.mPoints; ++i) { Point p; esm.getComposite(p); mPoints.push_back(p); edgeCount += p.mConnectionNum; } } break; } case fourCC("PGRC"): { esm.getSubHeader(); uint32_t size = esm.getSubSize(); if (size % sizeof(uint32_t) != 0) esm.fail("PGRC size not a multiple of 4"); else { size_t rawConnNum = size / sizeof(uint32_t); std::vector rawConnections; rawConnections.reserve(rawConnNum); for (size_t i = 0; i < rawConnNum; ++i) { uint32_t currentValue; esm.getT(currentValue); rawConnections.push_back(currentValue); } auto rawIt = rawConnections.begin(); size_t pointIndex = 0; mEdges.reserve(edgeCount); for (const auto& point : mPoints) { unsigned char connectionNum = point.mConnectionNum; if (rawConnections.end() - rawIt < connectionNum) esm.fail("Not enough connections"); for (int i = 0; i < connectionNum; ++i) { Edge edge; edge.mV0 = pointIndex; edge.mV1 = *rawIt; ++rawIt; mEdges.push_back(edge); } ++pointIndex; } } break; } case SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasData) esm.fail("Missing DATA subrecord"); } void Pathgrid::save(ESMWriter& esm, bool isDeleted) const { // Correct connection count and sort edges by point // Can probably be optimized PointList correctedPoints = mPoints; std::vector sortedEdges; sortedEdges.reserve(mEdges.size()); for (size_t point = 0; point < correctedPoints.size(); ++point) { correctedPoints[point].mConnectionNum = 0; for (const auto& edge : mEdges) { if (edge.mV0 == point) { sortedEdges.push_back(static_cast(edge.mV1)); ++correctedPoints[point].mConnectionNum; } } } // Save esm.writeHNCRefId("NAME", mCell); esm.writeNamedComposite("DATA", mData); if (isDeleted) { esm.writeHNString("DELE", {}, 3); return; } if (!correctedPoints.empty()) { esm.startSubRecord("PGRP"); for (const Point& point : correctedPoints) { esm.writeComposite(point); } esm.endRecord("PGRP"); } if (!sortedEdges.empty()) { esm.startSubRecord("PGRC"); for (const uint32_t& edge : sortedEdges) { esm.writeT(edge); } esm.endRecord("PGRC"); } } void Pathgrid::blank() { mCell = ESM::RefId(); mData.mX = 0; mData.mY = 0; mData.mGranularity = 0; mData.mPoints = 0; mPoints.clear(); mEdges.clear(); } }