2015-05-27 12:12:11 +00:00
|
|
|
#include "pathgridcheck.hpp"
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include <algorithm>
|
|
|
|
|
2018-06-19 22:20:03 +00:00
|
|
|
#include "../prefs/state.hpp"
|
|
|
|
|
2015-05-27 12:12:11 +00:00
|
|
|
#include "../world/universalid.hpp"
|
|
|
|
#include "../world/idcollection.hpp"
|
|
|
|
#include "../world/subcellcollection.hpp"
|
|
|
|
#include "../world/pathgrid.hpp"
|
|
|
|
|
2015-05-30 11:12:47 +00:00
|
|
|
CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)
|
|
|
|
: mPathgrids (pathgrids)
|
2018-06-19 22:20:03 +00:00
|
|
|
{
|
2018-06-20 09:29:38 +00:00
|
|
|
mIgnoreBaseRecords = false;
|
2018-06-19 22:20:03 +00:00
|
|
|
}
|
2015-05-27 12:12:11 +00:00
|
|
|
|
|
|
|
int CSMTools::PathgridCheckStage::setup()
|
|
|
|
{
|
2018-06-19 22:20:03 +00:00
|
|
|
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
|
|
|
|
|
2015-05-27 12:12:11 +00:00
|
|
|
return mPathgrids.getSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages)
|
|
|
|
{
|
|
|
|
const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);
|
|
|
|
|
2018-06-19 22:20:03 +00:00
|
|
|
// Skip "Base" records (setting!) and "Deleted" records
|
2018-06-20 09:29:38 +00:00
|
|
|
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
|
2015-05-27 12:12:11 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
const CSMWorld::Pathgrid& pathgrid = record.get();
|
|
|
|
|
|
|
|
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId);
|
|
|
|
|
|
|
|
// check the number of pathgrid points
|
Some PVS-Studio and cppcheck fixes
cppcheck:
[apps/esmtool/record.cpp:697]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/esmtool/record.cpp:1126]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/esmtool/record.cpp:1138]: (performance) Prefer prefix ++/-- operators for non-primitive types.
[apps/niftest/niftest.cpp:36]: (performance) Function parameter 'filename' should be passed by reference.
[apps/niftest/niftest.cpp:41]: (performance) Function parameter 'filename' should be passed by reference.
[apps/opencs/model/prefs/boolsetting.cpp:25]: (warning) Possible leak in public function. The pointer 'mWidget' is not deallocated before it is allocated.
[apps/opencs/model/prefs/shortcuteventhandler.cpp:52]: (warning) Return value of std::remove() ignored. Elements remain in container.
[apps/openmw/mwstate/quicksavemanager.cpp:5]: (performance) Variable 'mSaveName' is assigned in constructor body. Consider performing initialization in initialization list.
PVS-Studio:
apps/opencs/model/filter/parser.cpp 582 warn V560 A part of conditional expression is always true: allowPredefined.
apps/opencs/view/world/referencecreator.cpp 67 warn V547 Expression '!errors.empty()' is always false.
apps/opencs/view/world/referencecreator.cpp 74 warn V547 Expression '!errors.empty()' is always false.
apps/opencs/view/doc/loader.cpp 170 warn V560 A part of conditional expression is always true: !completed.
apps/opencs/view/doc/loader.cpp 170 warn V560 A part of conditional expression is always true: !error.empty().
apps/opencs/model/tools/pathgridcheck.cpp 32 err V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 32, 34.
apps/opencs/model/world/refidadapterimp.cpp 1376 err V547 Expression 'subColIndex < 3' is always true.
apps/openmw/mwgui/widgets.hpp 318 warn V703 It is odd that the 'mEnableRepeat' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:318, MyGUI_ScrollBar.h:179.
apps/openmw/mwgui/widgets.hpp 319 warn V703 It is odd that the 'mRepeatTriggerTime' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:319, MyGUI_ScrollBar.h:180.
apps/openmw/mwgui/widgets.hpp 320 warn V703 It is odd that the 'mRepeatStepTime' field in derived class 'MWScrollBar' overwrites field in base class 'ScrollBar'. Check lines: widgets.hpp:320, MyGUI_ScrollBar.h:181
apps/openmw/mwmechanics/actors.cpp 1425 warn V547 Expression '!detected' is always true.
apps/openmw/mwmechanics/character.cpp 2155 err V547 Expression 'mode == 0' is always true.
apps/openmw/mwmechanics/character.cpp 1192 warn V592 The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.
apps/openmw/mwmechanics/character.cpp 521 warn V560 A part of conditional expression is always true: (idle == mIdleState).
apps/openmw/mwmechanics/pathfinding.cpp 317 err V547 Expression 'mPath.size() >= 2' is always true.
apps/openmw/mwscript/interpretercontext.cpp 409 warn V560 A part of conditional expression is always false: rank > 9.
apps/openmw/mwgui/windowbase.cpp 28 warn V560 A part of conditional expression is always true: !visible.
apps/openmw/mwgui/journalwindow.cpp 561 warn V547 Expression '!mAllQuests' is always false.
apps/openmw/mwgui/referenceinterface.cpp 18 warn V571 Recurring check. The '!mPtr.isEmpty()' condition was already verified in line 16.
apps/openmw/mwworld/scene.cpp 463 warn V547 Expression 'adjustPlayerPos' is always true.
apps/openmw/mwworld/worldimp.cpp 409 err V766 An item with the same key '"sCompanionShare"' has already been added.
apps/openmw/mwworld/cellstore.cpp 691 warn V519 The 'state.mWaterLevel' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 689, 691.
apps/openmw/mwworld/weather.cpp 1125 warn V519 The 'mResult.mParticleEffect' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1123, 1125.
apps/openmw/mwworld/weather.cpp 1137 warn V519 The 'mResult.mParticleEffect' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1135, 1137.
apps/wizard/unshield/unshieldworker.cpp 475 warn V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.
apps/wizard/installationpage.cpp 163 warn V735 Possibly an incorrect HTML. The "</p" closing tag was encountered, while the "</span" tag was expected.
components/fontloader/fontloader.cpp 427 err V547 Expression 'i == 1' is always true.
components/nifosg/nifloader.cpp 282 warn V519 The 'created' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 278, 282.
components/esm/loadregn.cpp 119 err V586 The 'clear' function is called twice for deallocation of the same resource. Check lines: 112, 119.
components/esm/cellref.cpp 178 warn V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 175, 178.
components/esmterrain/storage.cpp 235 warn V560 A part of conditional expression is always true: colStart == 0.
components/esmterrain/storage.cpp 237 warn V560 A part of conditional expression is always true: rowStart == 0.
2018-04-09 15:55:16 +00:00
|
|
|
if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size()))
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error);
|
2015-05-27 12:12:11 +00:00
|
|
|
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error);
|
2015-05-27 12:12:11 +00:00
|
|
|
|
2015-05-27 22:44:17 +00:00
|
|
|
std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());
|
2015-05-27 12:12:11 +00:00
|
|
|
std::vector<int> duplList;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i)
|
|
|
|
{
|
|
|
|
if (pathgrid.mEdges[i].mV0 < static_cast<int>(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0)
|
|
|
|
{
|
|
|
|
pointList[pathgrid.mEdges[i].mV0].mConnectionNum++;
|
|
|
|
// first check for duplicate edges
|
|
|
|
unsigned int j = 0;
|
|
|
|
for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j)
|
|
|
|
{
|
|
|
|
if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2018-09-16 13:47:33 +00:00
|
|
|
ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1;
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
|
2015-05-27 12:12:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// only add if not a duplicate
|
|
|
|
if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size())
|
|
|
|
pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2018-09-16 13:47:33 +00:00
|
|
|
ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0;
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
|
2015-05-27 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i)
|
|
|
|
{
|
|
|
|
// check that edges are bidirectional
|
|
|
|
bool foundReverse = false;
|
|
|
|
for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j)
|
|
|
|
{
|
|
|
|
for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k)
|
|
|
|
{
|
|
|
|
if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast<int>(i))
|
|
|
|
{
|
|
|
|
foundReverse = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundReverse)
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2018-09-16 13:47:33 +00:00
|
|
|
ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j];
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
|
2015-05-27 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check duplicate points
|
|
|
|
// FIXME: how to do this efficiently?
|
2018-08-25 03:40:14 +00:00
|
|
|
for (unsigned int j = 0; j != i; ++j)
|
2015-05-27 12:12:11 +00:00
|
|
|
{
|
|
|
|
if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&
|
|
|
|
pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
|
|
|
|
pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)
|
|
|
|
{
|
2018-09-04 14:14:51 +00:00
|
|
|
std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), static_cast<int>(i));
|
2015-05-27 12:12:11 +00:00
|
|
|
if (it == duplList.end())
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2018-09-16 13:47:33 +00:00
|
|
|
ss << "Point #" << i << " duplicates point #" << j
|
2018-08-24 19:01:08 +00:00
|
|
|
<< " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")";
|
|
|
|
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
|
2015-05-27 12:12:11 +00:00
|
|
|
|
|
|
|
duplList.push_back(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check pathgrid points that are not connected to anything
|
|
|
|
for (unsigned int i = 0; i < pointList.size(); ++i)
|
|
|
|
{
|
|
|
|
if (pointList[i].mConnectionNum == 0)
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2018-09-16 13:47:33 +00:00
|
|
|
ss << "Point #" << i << " ("
|
|
|
|
<< pathgrid.mPoints[i].mX << ", "
|
|
|
|
<< pathgrid.mPoints[i].mY << ", "
|
|
|
|
<< pathgrid.mPoints[i].mZ << ") is disconnected from other points";
|
2018-08-24 19:01:08 +00:00
|
|
|
messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
|
2015-05-27 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: check whether there are disconnected graphs
|
|
|
|
}
|