1 #include "loadpgrd.hpp"
2 
3 #include "esmreader.hpp"
4 #include "esmwriter.hpp"
5 #include "defs.hpp"
6 
7 namespace ESM
8 {
9     unsigned int Pathgrid::sRecordId = REC_PGRD;
10 
operator =(const float rhs[3])11     Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3])
12     {
13         mX = static_cast<int>(rhs[0]);
14         mY = static_cast<int>(rhs[1]);
15         mZ = static_cast<int>(rhs[2]);
16         mAutogenerated = 0;
17         mConnectionNum = 0;
18         mUnknown = 0;
19         return *this;
20     }
Point(const float rhs[3])21     Pathgrid::Point::Point(const float rhs[3])
22     : mX(static_cast<int>(rhs[0])),
23       mY(static_cast<int>(rhs[1])),
24       mZ(static_cast<int>(rhs[2])),
25       mAutogenerated(0),
26       mConnectionNum(0),
27       mUnknown(0)
28     {
29     }
Point()30     Pathgrid::Point::Point():mX(0),mY(0),mZ(0),mAutogenerated(0),
31                              mConnectionNum(0),mUnknown(0)
32     {
33     }
34 
load(ESMReader & esm,bool & isDeleted)35     void Pathgrid::load(ESMReader &esm, bool &isDeleted)
36     {
37         isDeleted = false;
38 
39         mPoints.clear();
40         mEdges.clear();
41 
42         // keep track of total connections so we can reserve edge vector size
43         int edgeCount = 0;
44 
45         bool hasData = false;
46         while (esm.hasMoreSubs())
47         {
48             esm.getSubName();
49             switch (esm.retSubName().intval)
50             {
51                 case ESM::SREC_NAME:
52                     mCell = esm.getHString();
53                     break;
54                 case ESM::FourCC<'D','A','T','A'>::value:
55                     esm.getHT(mData, 12);
56                     hasData = true;
57                     break;
58                 case ESM::FourCC<'P','G','R','P'>::value:
59                 {
60                     esm.getSubHeader();
61                     int size = esm.getSubSize();
62                     // Check that the sizes match up. Size = 16 * s2 (path points)
63                     if (size != static_cast<int> (sizeof(Point) * mData.mS2))
64                         esm.fail("Path point subrecord size mismatch");
65                     else
66                     {
67                         int pointCount = mData.mS2;
68                         mPoints.reserve(pointCount);
69                         for (int i = 0; i < pointCount; ++i)
70                         {
71                             Point p;
72                             esm.getExact(&p, sizeof(Point));
73                             mPoints.push_back(p);
74                             edgeCount += p.mConnectionNum;
75                         }
76                     }
77                     break;
78                 }
79                 case ESM::FourCC<'P','G','R','C'>::value:
80                 {
81                     esm.getSubHeader();
82                     int size = esm.getSubSize();
83                     if (size % sizeof(int) != 0)
84                         esm.fail("PGRC size not a multiple of 4");
85                     else
86                     {
87                         int rawConnNum = size / sizeof(int);
88                         std::vector<int> rawConnections;
89                         rawConnections.reserve(rawConnNum);
90                         for (int i = 0; i < rawConnNum; ++i)
91                         {
92                             int currentValue;
93                             esm.getT(currentValue);
94                             rawConnections.push_back(currentValue);
95                         }
96 
97                         std::vector<int>::const_iterator rawIt = rawConnections.begin();
98                         int pointIndex = 0;
99                         mEdges.reserve(edgeCount);
100                         for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex)
101                         {
102                             unsigned char connectionNum = (*it).mConnectionNum;
103                             if (rawConnections.end() - rawIt < connectionNum)
104                                 esm.fail("Not enough connections");
105                             for (int i = 0; i < connectionNum; ++i) {
106                                 Edge edge;
107                                 edge.mV0 = pointIndex;
108                                 edge.mV1 = *rawIt;
109                                 ++rawIt;
110                                 mEdges.push_back(edge);
111                             }
112                         }
113                     }
114                     break;
115                 }
116                 case ESM::SREC_DELE:
117                     esm.skipHSub();
118                     isDeleted = true;
119                     break;
120                 default:
121                     esm.fail("Unknown subrecord");
122                     break;
123             }
124         }
125 
126         if (!hasData)
127             esm.fail("Missing DATA subrecord");
128     }
129 
save(ESMWriter & esm,bool isDeleted) const130     void Pathgrid::save(ESMWriter &esm, bool isDeleted) const
131     {
132         // Correct connection count and sort edges by point
133         // Can probably be optimized
134         PointList correctedPoints = mPoints;
135         std::vector<int> sortedEdges;
136 
137         sortedEdges.reserve(mEdges.size());
138 
139         for (size_t point = 0; point < correctedPoints.size(); ++point)
140         {
141             correctedPoints[point].mConnectionNum = 0;
142 
143             for (EdgeList::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it)
144             {
145                 if (static_cast<size_t>(it->mV0) == point)
146                 {
147                     sortedEdges.push_back(it->mV1);
148                     ++correctedPoints[point].mConnectionNum;
149                 }
150             }
151         }
152 
153         // Save
154         esm.writeHNCString("NAME", mCell);
155         esm.writeHNT("DATA", mData, 12);
156 
157         if (isDeleted)
158         {
159             esm.writeHNCString("DELE", "");
160             return;
161         }
162 
163         if (!correctedPoints.empty())
164         {
165             esm.startSubRecord("PGRP");
166             for (PointList::const_iterator it = correctedPoints.begin(); it != correctedPoints.end(); ++it)
167             {
168                 esm.writeT(*it);
169             }
170             esm.endRecord("PGRP");
171         }
172 
173         if (!sortedEdges.empty())
174         {
175             esm.startSubRecord("PGRC");
176             for (std::vector<int>::const_iterator it = sortedEdges.begin(); it != sortedEdges.end(); ++it)
177             {
178                 esm.writeT(*it);
179             }
180             esm.endRecord("PGRC");
181         }
182     }
183 
blank()184     void Pathgrid::blank()
185     {
186         mCell.clear();
187         mData.mX = 0;
188         mData.mY = 0;
189         mData.mS1 = 0;
190         mData.mS2 = 0;
191         mPoints.clear();
192         mEdges.clear();
193     }
194 }
195