void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage); if (record.isDeleted()) return; const ESM::BirthSign& birthsign = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); // test for empty name, description and texture if (birthsign.mName.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); if (birthsign.mDescription.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); if (birthsign.mTexture.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); /// \todo test if the texture exists /// \todo check data members that can't be edited in the table view }
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages) { const CSMWorld::Record<ESM::MagicEffect> &record = mMagicEffects.getRecord(stage); // Skip "Base" records (setting!) and "Deleted" records if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) return; ESM::MagicEffect effect = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); if (effect.mData.mBaseCost < 0.0f) { messages.push_back(std::make_pair(id, "Base Cost is negative")); } if (effect.mIcon.empty()) { messages.push_back(std::make_pair(id, "Icon is not specified")); } else if (!isTextureExists(effect.mIcon, true)) { messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); } if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) { messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); } addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object")); addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound")); if (effect.mDescription.empty()) { messages.push_back(std::make_pair(id, "Description is empty")); } }
void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage); if (record.isDeleted()) return; const ESM::Class& class_ = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); // test for empty name and description if (class_.mName.empty()) messages.push_back (std::make_pair (id, class_.mId + " has an empty name")); if (class_.mDescription.empty()) messages.push_back (std::make_pair (id, class_.mId + " has an empty description")); // test for invalid attributes for (int i=0; i<2; ++i) if (class_.mData.mAttribute[i]==-1) { std::ostringstream stream; stream << "Attribute #" << i << " of " << class_.mId << " is not set"; messages.push_back (std::make_pair (id, stream.str())); } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { messages.push_back (std::make_pair (id, "Class lists same attribute twice")); } // test for non-unique skill std::map<int, int> skills; // ID, number of occurrences for (int i=0; i<5; ++i) for (int i2=0; i2<2; ++i2) ++skills[class_.mData.mSkills[i][i2]]; for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) if (iter->second>1) { messages.push_back (std::make_pair (id, ESM::Skill::indexToId (iter->first) + " is listed more than once")); } }
void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); if (!mPlayable) messages.push_back (std::make_pair (id, "No playable race")); }
void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage); if (record.isDeleted()) return; const ESM::Spell& spell = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); // test for empty name and description if (spell.mName.empty()) messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); // test for invalid cost values if (spell.mData.mCost<0) messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); /// \todo check data members that can't be edited in the table view }
void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage); if (record.isDeleted()) return; const ESM::Faction& faction = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId); // test for empty name if (faction.mName.empty()) messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { messages.push_back (std::make_pair (id , "Faction lists same attribute twice")); } // test for non-unique skill std::map<int, int> skills; // ID, number of occurrences for (int i=0; i<7; ++i) if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) if (iter->second>1) { messages.push_back (std::make_pair (id, ESM::Skill::indexToId (iter->first) + " is listed more than once")); } /// \todo check data members that can't be edited in the table view }
void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage); if (record.isDeleted()) return; const ESM::Sound& sound = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Maximum range larger than minimum range")); /// \todo check, if the sound file exists }
void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage); // Skip "Base" records (setting!) and "Deleted" records if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) return; const ESM::Sound& sound = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); /// \todo check, if the sound file exists }
void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage); if (record.isDeleted()) return; const ESM::Region& region = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId); // test for empty name if (region.mName.empty()) messages.push_back (std::make_pair (id, region.mId + " has an empty name")); /// \todo test that the ID in mSleeplist exists /// \todo check data members that can't be edited in the table view }
void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) { mId = mDocument.getData().getScripts().getId (stage); if (mDocument.isBlacklisted ( CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId))) return; mMessages = &messages; try { const CSMWorld::Data& data = mDocument.getData(); mFile = data.getScripts().getRecord (stage).get().mId; std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText); Compiler::Scanner scanner (*this, input, mContext.getExtensions()); Compiler::FileParser parser (*this, mContext); scanner.scan (parser); } catch (const Compiler::SourceException&) { // error has already been reported via error handler } catch (const std::exception& error) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); messages.push_back (std::make_pair (id, std::string ("Critical compile error: ") + error.what())); } mMessages = 0; }
void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &messages) { const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage); // Skip "Base" records (setting!) and "Deleted" records if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) return; const ESM::BodyPart &bodyPart = record.get(); CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId ); // Check BYDT if (bodyPart.mData.mPart > 14 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); if (bodyPart.mData.mFlags > 3 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); if (bodyPart.mData.mType > 2 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); // Check MODL if ( bodyPart.mModel.empty() ) messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); // Check FNAM if ( bodyPart.mRace.empty() ) messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); }
void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<ESM::Race>& record = mRaces.getRecord (stage); if (record.isDeleted()) return; const ESM::Race& race = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description if (race.mName.empty()) messages.push_back (std::make_pair (id, race.mId + " has an empty name")); if (race.mDescription.empty()) messages.push_back (std::make_pair (id, race.mId + " has an empty description")); // test for positive height if (race.mData.mHeight.mMale<=0) messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); if (race.mData.mHeight.mFemale<=0) messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); // test for non-negative weight if (race.mData.mWeight.mMale<0) messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); if (race.mData.mWeight.mFemale<0) messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); // remember playable flag if (race.mData.mFlags & 0x1) mPlayable = true; /// \todo check data members that can't be edited in the table view }
void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage); if (record.isDeleted()) return; const CSMWorld::Pathgrid& pathgrid = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId); // check the number of pathgrid points if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size())) messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected")); else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size())) messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected")); std::vector<Point> pointList(pathgrid.mPoints.size()); 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; ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); 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; ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); } } for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) { // check the connection number for each point matches the edge connections if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum) { std::ostringstream ss; ss << " has has less edges than expected for point " << i; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); } else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum) { std::ostringstream ss; ss << " has has more edges than expected for point " << i; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); } // 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; ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); } } // check duplicate points // FIXME: how to do this efficiently? for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) { if (j == i) continue; 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) { std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), i); if (it == duplList.end()) { std::ostringstream ss; ss << " has a duplicated point (" << i << ") x=" << pathgrid.mPoints[i].mX << ", y=" << pathgrid.mPoints[i].mY << ", z=" << pathgrid.mPoints[i].mZ; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); 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; ss << " has an orphaned point (" << i << ") x=" << pathgrid.mPoints[i].mX << ", y=" << pathgrid.mPoints[i].mY << ", z=" << pathgrid.mPoints[i].mZ; messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); } } // TODO: check whether there are disconnected graphs }