Merged pull request #1907

pull/541/head
Marc Zinnschlag 6 years ago
commit d76367918a

@ -125,6 +125,7 @@
Bug #4641: GetPCJumping is handled incorrectly Bug #4641: GetPCJumping is handled incorrectly
Bug #4644: %Name should be available for all actors, not just for NPCs Bug #4644: %Name should be available for all actors, not just for NPCs
Feature #912: Editor: Add missing icons to UniversalId tables Feature #912: Editor: Add missing icons to UniversalId tables
Feature #1617: Editor: Enchantment effect record verifier
Feature #1645: Casting effects from objects Feature #1645: Casting effects from objects
Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script Feature #3083: Play animation when NPC is casting spell via script
@ -159,6 +160,7 @@
Task #4606: Support Rapture3D's OpenAL driver Task #4606: Support Rapture3D's OpenAL driver
Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9
Task #4621: Optimize combat AI Task #4621: Optimize combat AI
Task #4643: Revise editor record verifying functionality
0.44.0 0.44.0
------ ------

@ -42,7 +42,7 @@ opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck topicinfocheck journalcheck mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck
) )
opencs_hdrs_noqt (model/tools opencs_hdrs_noqt (model/tools

@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string&
mMessages.push_back (Message (id, message, hint, severity)); mMessages.push_back (Message (id, message, hint, severity));
} }
void CSMDoc::Messages::push_back (const std::pair<CSMWorld::UniversalId, std::string>& data)
{
add (data.first, data.second);
}
CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const
{ {
return mMessages.begin(); return mMessages.begin();

@ -56,9 +56,6 @@ namespace CSMDoc
const std::string& hint = "", const std::string& hint = "",
Message::Severity severity = Message::Severity_Default); Message::Severity severity = Message::Severity_Default);
/// \deprecated Use add instead.
void push_back (const std::pair<CSMWorld::UniversalId, std::string>& data);
Iterator begin() const; Iterator begin() const;
Iterator end() const; Iterator end() const;

@ -1,16 +1,15 @@
#include "birthsigncheck.hpp" #include "birthsigncheck.hpp"
#include <sstream> #include <components/misc/resourcehelpers.hpp>
#include <map>
#include <components/esm/loadbsgn.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns) CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns,
: mBirthsigns (birthsigns) const CSMWorld::Resources &textures)
: mBirthsigns(birthsigns),
mTextures(textures)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -34,17 +33,20 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
// test for empty name, description and texture
if (birthsign.mName.empty()) if (birthsign.mName.empty())
messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (birthsign.mDescription.empty()) if (birthsign.mDescription.empty())
messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
if (birthsign.mTexture.empty()) if (birthsign.mTexture.empty())
messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error);
else if (mTextures.searchId(birthsign.mTexture) == -1)
/// \todo test if the texture exists {
std::string ddsTexture = birthsign.mTexture;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1))
messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view
} }

@ -4,6 +4,7 @@
#include <components/esm/loadbsgn.hpp> #include <components/esm/loadbsgn.hpp>
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -12,12 +13,14 @@ namespace CSMTools
/// \brief VerifyStage: make sure that birthsign records are internally consistent /// \brief VerifyStage: make sure that birthsign records are internally consistent
class BirthsignCheckStage : public CSMDoc::Stage class BirthsignCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns; const CSMWorld::IdCollection<ESM::BirthSign> &mBirthsigns;
const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
public: public:
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns); BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign> &birthsigns,
const CSMWorld::Resources &textures);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

@ -34,25 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
// Check BYDT // Check BYDT
if (bodyPart.mData.mPart > 14 ) if (bodyPart.mData.mPart > 14 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mFlags > 3 ) if (bodyPart.mData.mFlags > 3 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error);
if (bodyPart.mData.mType > 2 ) if (bodyPart.mData.mType > 2 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error);
// Check MODL // Check MODL
if ( bodyPart.mModel.empty() ) if ( bodyPart.mModel.empty() )
messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check FNAM // Check FNAM
if ( bodyPart.mRace.empty() ) if ( bodyPart.mRace.empty() )
messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error);
} }

@ -1,6 +1,5 @@
#include "classcheck.hpp" #include "classcheck.hpp"
#include <sstream>
#include <map> #include <map>
#include <components/esm/loadclas.hpp> #include <components/esm/loadclas.hpp>
@ -37,26 +36,22 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
// A class should have a name // A class should have a name
if (class_.mName.empty()) if (class_.mName.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
// A playable class should have a description // A playable class should have a description
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable")); messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning);
// test for invalid attributes // test for invalid attributes
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
if (class_.mData.mAttribute[i]==-1) if (class_.mData.mAttribute[i]==-1)
{ {
std::ostringstream stream; messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error);
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) 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")); messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error);
} }
// test for non-unique skill // test for non-unique skill
@ -66,10 +61,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
for (int i2=0; i2<2; ++i2) for (int i2=0; i2<2; ++i2)
++skills[class_.mData.mSkills[i][i2]]; ++skills[class_.mData.mSkills[i][i2]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) for (auto &skill : skills)
if (iter->second>1) if (skill.second>1)
{ {
messages.push_back (std::make_pair (id, messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
} }
} }

@ -0,0 +1,82 @@
#include "enchantmentcheck.hpp"
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& enchantments)
: mEnchantments (enchantments)
{
mIgnoreBaseRecords = false;
}
int CSMTools::EnchantmentCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mEnchantments.getSize();
}
void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Enchantment>& record = mEnchantments.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Enchantment& enchantment = record.get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Enchantment, enchantment.mId);
if (enchantment.mData.mType < 0 || enchantment.mData.mType > 3)
messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error);
if (enchantment.mData.mCost < 0)
messages.add(id, "Cost is negative", "", CSMDoc::Message::Severity_Error);
if (enchantment.mData.mCharge < 0)
messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error);
if (enchantment.mData.mCost > enchantment.mData.mCharge)
messages.add(id, "Cost is higher than charge", "", CSMDoc::Message::Severity_Error);
if (enchantment.mEffects.mList.empty())
{
messages.add(id, "Enchantment doesn't have any magic effects", "", CSMDoc::Message::Severity_Warning);
}
else
{
std::vector<ESM::ENAMstruct>::const_iterator effect = enchantment.mEffects.mList.begin();
for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++)
{
const std::string number = std::to_string(i);
// At the time of writing this effects, attributes and skills are hardcoded
if (effect->mEffectID < 0 || effect->mEffectID > 142)
{
messages.add(id, "Effect #" + number + " is invalid", "", CSMDoc::Message::Severity_Error);
++effect;
continue;
}
if (effect->mSkill < -1 || effect->mSkill > 26)
messages.add(id, "Effect #" + number + " affected skill is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mAttribute < -1 || effect->mAttribute > 7)
messages.add(id, "Effect #" + number + " affected attribute is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mRange < 0 || effect->mRange > 2)
messages.add(id, "Effect #" + number + " range is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mArea < 0)
messages.add(id, "Effect #" + number + " area is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mDuration < 0)
messages.add(id, "Effect #" + number + " duration is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMin < 0)
messages.add(id, "Effect #" + number + " minimum magnitude is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMax < 0)
messages.add(id, "Effect #" + number + " maximum magnitude is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMin > effect->mMagnMax)
messages.add(id, "Effect #" + number + " minimum magnitude is higher than maximum magnitude", "", CSMDoc::Message::Severity_Error);
++effect;
}
}
}

@ -0,0 +1,31 @@
#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H
#define CSM_TOOLS_ENCHANTMENTCHECK_H
#include <components/esm/loadench.hpp>
#include "../world/idcollection.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief Make sure that enchantment records are correct
class EnchantmentCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Enchantment>& mEnchantments;
bool mIgnoreBaseRecords;
public:
EnchantmentCheckStage (const CSMWorld::IdCollection<ESM::Enchantment>& enchantments);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

@ -1,9 +1,7 @@
#include "factioncheck.hpp" #include "factioncheck.hpp"
#include <sstream>
#include <map> #include <map>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -37,12 +35,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
// test for empty name // test for empty name
if (faction.mName.empty()) if (faction.mName.empty())
messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
// test for invalid attributes // test for invalid attributes
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) 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")); messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error);
} }
// test for non-unique skill // test for non-unique skill
@ -52,11 +50,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
if (faction.mData.mSkills[i]!=-1) if (faction.mData.mSkills[i]!=-1)
++skills[faction.mData.mSkills[i]]; ++skills[faction.mData.mSkills[i]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) for (auto &skill : skills)
if (iter->second>1) if (skill.second>1)
{ {
messages.push_back (std::make_pair (id, messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error);
ESM::Skill::indexToId (iter->first) + " is listed more than once"));
} }
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view

@ -1,7 +1,6 @@
#include "journalcheck.hpp" #include "journalcheck.hpp"
#include <set> #include <set>
#include <sstream>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -57,34 +56,27 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
if (journalInfo.mResponse.empty()) if (journalInfo.mResponse.empty())
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
} }
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
// Duplicate index // Duplicate index
if (result.second == false) if (!result.second)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error);
std::ostringstream stream;
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
if (totalInfoCount == 0) if (totalInfoCount == 0)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning);
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
} }
else if (statusNamedCount > 1) else if (statusNamedCount > 1)
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
} }
} }

@ -4,79 +4,26 @@
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/resources.hpp" std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id,
#include "../world/data.hpp"
namespace
{
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text)
{
if (!text.empty())
{
messages.push_back(std::make_pair(id, text));
}
}
}
bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const
{
const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures;
bool exists = false;
if (textures.searchId(texture) != -1)
{
exists = true;
}
else
{
std::string ddsTexture = texture;
if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1)
{
exists = true;
}
}
return exists;
}
std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type, const CSMWorld::UniversalId &type,
const std::string &column) const const std::string &column) const
{ {
std::string error; CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id);
if (!id.empty()) if (index.first == -1)
{ return (column + " '" + id + "' does not exist");
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); else if (index.second != type.getType())
if (index.first == -1) return (column + " '" + id + "' does not have " + type.getTypeName() + " type");
{ return std::string();
error = "No such " + column + " '" + id + "'";
}
else if (index.second != type.getType())
{
error = column + " is not of type " + type.getTypeName();
}
}
return error;
}
std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const
{
std::string error;
if (!id.empty() && mSounds.searchId(id) == -1)
{
error = "No such " + column + " '" + id + "'";
}
return error;
} }
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects, CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables, const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons, const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures) const CSMWorld::Resources &textures)
: mMagicEffects(effects), : mMagicEffects(effects),
mSounds(sounds), mSounds(sounds),
mReferenceables(referenceables), mObjects(objects),
mIcons(icons), mIcons(icons),
mTextures(textures) mTextures(textures)
{ {
@ -100,46 +47,75 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa
ESM::MagicEffect effect = record.get(); ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mDescription.empty())
{
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
}
if (effect.mData.mBaseCost < 0.0f) if (effect.mData.mBaseCost < 0.0f)
{ {
messages.push_back(std::make_pair(id, "Base Cost is negative")); messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error);
} }
if (effect.mIcon.empty()) if (effect.mIcon.empty())
{ {
messages.push_back(std::make_pair(id, "Icon is not specified")); messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error);
} }
else if (!isTextureExists(effect.mIcon, true)) else
{ {
messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); if (mIcons.searchId(effect.mIcon) == -1)
{
std::string ddsIcon = effect.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
} }
if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) if (!effect.mParticle.empty())
{ {
messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); if (mTextures.searchId(effect.mParticle) == -1)
{
std::string ddsParticle = effect.mParticle;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1))
messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
} }
addMessageIfNotEmpty(messages, if (!effect.mCasting.empty())
id, {
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object");
addMessageIfNotEmpty(messages, if (!error.empty())
id, messages.add(id, error, "", CSMDoc::Message::Severity_Error);
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()) if (!effect.mHit.empty())
{
const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mArea.empty())
{
const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
}
if (!effect.mBolt.empty())
{ {
messages.push_back(std::make_pair(id, "Description is empty")); const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object");
if (!error.empty())
messages.add(id, error, "", CSMDoc::Message::Severity_Error);
} }
if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1)
messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1)
messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1)
messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1)
messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
} }

@ -6,14 +6,10 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../world/refidcollection.hpp" #include "../world/refidcollection.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
namespace CSMWorld
{
class Resources;
}
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that magic effect records are internally consistent /// \brief VerifyStage: make sure that magic effect records are internally consistent
@ -21,23 +17,18 @@ namespace CSMTools
{ {
const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects; const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects;
const CSMWorld::IdCollection<ESM::Sound> &mSounds; const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::RefIdCollection &mObjects;
const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures; const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
private: private:
bool isTextureExists(const std::string &texture, bool isIcon) const; std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const;
std::string checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const;
std::string checkSound(const std::string &id, const std::string &column) const;
public: public:
MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects, MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables, const CSMWorld::RefIdCollection &objects,
const CSMWorld::Resources &icons, const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures); const CSMWorld::Resources &textures);

@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
// check the number of pathgrid points // check the number of pathgrid points
if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size())) if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size()))
messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error); messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error);
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size())) else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error); messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error);
std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size()); std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());
std::vector<int> duplList; std::vector<int> duplList;
@ -56,9 +56,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1;
<< " and " << pathgrid.mEdges[i].mV1; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
break; break;
} }
} }
@ -70,8 +69,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
else else
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
@ -93,18 +92,15 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (!foundReverse) if (!foundReverse)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j];
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error);
} }
} }
// check duplicate points // check duplicate points
// FIXME: how to do this efficiently? // FIXME: how to do this efficiently?
for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) for (unsigned int j = 0; j != i; ++j)
{ {
if (j == i)
continue;
if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&
pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)
@ -113,11 +109,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (it == duplList.end()) if (it == duplList.end())
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has a duplicated point (" << i ss << "Point #" << i << " duplicates point #" << j
<< ") x=" << pathgrid.mPoints[i].mX << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")";
<< ", y=" << pathgrid.mPoints[i].mY messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning);
duplList.push_back(i); duplList.push_back(i);
break; break;
@ -132,11 +126,11 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
if (pointList[i].mConnectionNum == 0) if (pointList[i].mConnectionNum == 0)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << " has an orphaned point (" << i ss << "Point #" << i << " ("
<< ") x=" << pathgrid.mPoints[i].mX << pathgrid.mPoints[i].mX << ", "
<< ", y=" << pathgrid.mPoints[i].mY << pathgrid.mPoints[i].mY << ", "
<< ", z=" << pathgrid.mPoints[i].mZ; << pathgrid.mPoints[i].mZ << ") is disconnected from other points";
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning);
} }
} }

@ -1,9 +1,5 @@
#include "racecheck.hpp" #include "racecheck.hpp"
#include <sstream>
#include <components/esm/loadrace.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -29,24 +25,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
// test for empty name and description // test for empty name and description
if (race.mName.empty()) if (race.mName.empty())
messages.push_back (std::make_pair (id, race.mId + " has an empty name")); messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning);
if (race.mDescription.empty()) if (race.mDescription.empty())
messages.push_back (std::make_pair (id, race.mId + " has an empty description")); messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
// test for positive height // test for positive height
if (race.mData.mHeight.mMale<=0) if (race.mData.mHeight.mMale<=0)
messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error);
if (race.mData.mHeight.mFemale<=0) if (race.mData.mHeight.mFemale<=0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error);
// test for non-negative weight // test for non-negative weight
if (race.mData.mWeight.mMale<0) if (race.mData.mWeight.mMale<0)
messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error);
if (race.mData.mWeight.mFemale<0) if (race.mData.mWeight.mFemale<0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view
} }
@ -56,7 +52,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
if (!mPlayable) if (!mPlayable)
messages.push_back (std::make_pair (id, "No playable race")); messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError);
} }
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races) CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)

@ -1,6 +1,7 @@
#include "referenceablecheck.hpp" #include "referenceablecheck.hpp"
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
@ -11,13 +12,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races, const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction, const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts) const CSMWorld::IdCollection<ESM::Script>& scripts,
: const CSMWorld::Resources& models,
mReferencables(referenceable), const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
:mReferencables(referenceable),
mRaces(races), mRaces(races),
mClasses(classes), mClasses(classes),
mFactions(faction), mFactions(faction),
mScripts(scripts), mScripts(scripts),
mModels(models),
mIcons(icons),
mBodyParts(bodyparts),
mPlayerPresent(false) mPlayerPresent(false)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
@ -270,9 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get(); const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);
//Checking for model, IIRC all activators should have a model
if (activator.mModel.empty()) if (activator.mModel.empty())
messages.push_back (std::make_pair (id, activator.mId + " has no model")); messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(activator.mModel) == -1)
messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Activator>(activator, messages, id.toString()); scriptCheck<ESM::Activator>(activator, messages, id.toString());
@ -293,7 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString()); inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
//IIRC potion can have empty effects list just fine. /// \todo Check magic effects for validity
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Potion>(potion, messages, id.toString()); scriptCheck<ESM::Potion>(potion, messages, id.toString());
@ -338,13 +345,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true); inventoryItemCheck<ESM::Armor>(armor, messages, id.toString(), true);
//checking for armor class, armor should have poistive armor class, but 0 is considered legal // Armor should have positive armor class, but 0 class is not an error
if (armor.mData.mArmor < 0) if (armor.mData.mArmor < 0)
messages.push_back (std::make_pair (id, armor.mId + " has negative armor class")); messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error);
//checking for health. Only positive numbers are allowed, or 0 is illegal // Armor durability must be a positive number
if (armor.mData.mHealth <= 0) if (armor.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Armor>(armor, messages, id.toString()); scriptCheck<ESM::Armor>(armor, messages, id.toString());
@ -383,18 +390,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get(); const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId);
//Checking for model, IIRC all containers should have a model //checking for name
if (container.mName.empty())
messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for model
if (container.mModel.empty()) if (container.mModel.empty())
messages.push_back (std::make_pair (id, container.mId + " has no model")); messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(container.mModel) == -1)
messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//Checking for capacity (weight) //Checking for capacity (weight)
if (container.mWeight < 0) //0 is allowed if (container.mWeight < 0) //0 is allowed
messages.push_back (std::make_pair (id, messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error);
container.mId + " has negative weight (capacity)"));
//checking for name
if (container.mName.empty())
messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
//checking contained items //checking contained items
inventoryListCheck(container.mInventory.mList, messages, id.toString()); inventoryListCheck(container.mInventory.mList, messages, id.toString());
@ -416,68 +424,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get(); const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId);
if (creature.mModel.empty())
messages.push_back (std::make_pair (id, creature.mId + " has no model"));
if (creature.mName.empty()) if (creature.mName.empty())
messages.push_back (std::make_pair (id, creature.mId + " has an empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (creature.mModel.empty())
messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(creature.mModel) == -1)
messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//stats checks //stats checks
if (creature.mData.mLevel < 1) if (creature.mData.mLevel <= 0)
messages.push_back (std::make_pair (id, creature.mId + " has non-positive level")); messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mStrength < 0) if (creature.mData.mStrength < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mIntelligence < 0) if (creature.mData.mIntelligence < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence")); messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mWillpower < 0) if (creature.mData.mWillpower < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative willpower")); messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mAgility < 0) if (creature.mData.mAgility < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative agility")); messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mSpeed < 0) if (creature.mData.mSpeed < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative speed")); messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mEndurance < 0) if (creature.mData.mEndurance < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative endurance")); messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mPersonality < 0) if (creature.mData.mPersonality < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative personality")); messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mLuck < 0) if (creature.mData.mLuck < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative luck")); messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mCombat < 0)
messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mMagic < 0)
messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mStealth < 0)
messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning);
if (creature.mData.mHealth < 0) if (creature.mData.mHealth < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative health")); messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mMana < 0)
messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mFatigue < 0)
messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error);
if (creature.mData.mSoul < 0) if (creature.mData.mSoul < 0)
messages.push_back (std::make_pair (id, creature.mId + " has negative soul value")); messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error);
for (int i = 0; i < 6; ++i) for (int i = 0; i < 6; ++i)
{ {
if (creature.mData.mAttack[i] < 0) if (creature.mData.mAttack[i] < 0)
{ messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error);
messages.push_back (std::make_pair (id, if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1])
creature.mId + " has negative attack strength")); messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error);
break;
}
} }
//TODO, find meaning of other values if (creature.mData.mGold < 0)
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
if (creature.mScale == 0) if (creature.mScale == 0)
messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error);
if (!creature.mOriginal.empty())
{
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal);
if (index.first == -1)
messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error);
else if (index.second != CSMWorld::UniversalId::Type_Creature)
messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error);
}
// Check inventory // Check inventory
inventoryListCheck(creature.mInventory.mList, messages, id.toString()); inventoryListCheck(creature.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString()); scriptCheck<ESM::Creature>(creature, messages, id.toString());
/// \todo Check spells, teleport table, AI data and AI packages for validity
} }
void CSMTools::ReferenceableCheckStage::doorCheck( void CSMTools::ReferenceableCheckStage::doorCheck(
@ -495,10 +516,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
//usual, name or model //usual, name or model
if (door.mName.empty()) if (door.mName.empty())
messages.push_back (std::make_pair (id, door.mId + " has an empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (door.mModel.empty()) if (door.mModel.empty())
messages.push_back (std::make_pair (id, door.mId + " has no model")); messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(door.mModel) == -1)
messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
// Check that mentioned scripts exist // Check that mentioned scripts exist
scriptCheck<ESM::Door>(door, messages, id.toString()); scriptCheck<ESM::Door>(door, messages, id.toString());
@ -572,7 +595,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId);
if (light.mData.mRadius < 0) if (light.mData.mRadius < 0)
messages.push_back (std::make_pair (id, light.mId + " has negative light radius")); messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error);
if (light.mData.mFlags & ESM::Light::Carry) if (light.mData.mFlags & ESM::Light::Carry)
inventoryItemCheck<ESM::Light>(light, messages, id.toString()); inventoryItemCheck<ESM::Light>(light, messages, id.toString());
@ -644,97 +667,76 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
return; return;
short level(npc.mNpdt.mLevel); short level(npc.mNpdt.mLevel);
char disposition(npc.mNpdt.mDisposition);
char reputation(npc.mNpdt.mReputation);
char rank(npc.mNpdt.mRank);
//Don't know what unknown is for
int gold(npc.mNpdt.mGold); int gold(npc.mNpdt.mGold);
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{ {
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
{ {
messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen? messages.add(id, "NPC with autocalculated stats doesn't have autocalc flag turned on", "", CSMDoc::Message::Severity_Error); //should not happen?
return; return;
} }
level = npc.mNpdt.mLevel;
disposition = npc.mNpdt.mDisposition;
reputation = npc.mNpdt.mReputation;
rank = npc.mNpdt.mRank;
gold = npc.mNpdt.mGold;
} }
else else
{ {
if (npc.mNpdt.mStrength == 0)
messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mIntelligence == 0)
messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mWillpower == 0)
messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mAgility == 0) if (npc.mNpdt.mAgility == 0)
messages.push_back (std::make_pair (id, npc.mId + " agility has zero value")); messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mSpeed == 0)
messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mEndurance == 0) if (npc.mNpdt.mEndurance == 0)
messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value")); messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mIntelligence == 0)
messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value"));
if (npc.mNpdt.mLuck == 0)
messages.push_back (std::make_pair (id, npc.mId + " luck has zero value"));
if (npc.mNpdt.mPersonality == 0) if (npc.mNpdt.mPersonality == 0)
messages.push_back (std::make_pair (id, npc.mId + " personality has zero value")); messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (npc.mNpdt.mLuck == 0)
if (npc.mNpdt.mStrength == 0) messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning);
messages.push_back (std::make_pair (id, npc.mId + " strength has zero value"));
if (npc.mNpdt.mSpeed == 0)
messages.push_back (std::make_pair (id, npc.mId + " speed has zero value"));
if (npc.mNpdt.mWillpower == 0)
messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value"));
} }
if (level < 1) if (level <= 0)
messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
if (gold < 0) if (gold < 0)
messages.push_back (std::make_pair (id, npc.mId + " gold has negative value")); messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error);
if (npc.mName.empty()) if (npc.mName.empty())
messages.push_back (std::make_pair (id, npc.mId + " has any empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
if (npc.mClass.empty()) if (npc.mClass.empty())
messages.push_back (std::make_pair (id, npc.mId + " has an empty class")); messages.add(id, "Class is missing", "", CSMDoc::Message::Severity_Error);
else if (mClasses.searchId (npc.mClass) == -1) else if (mClasses.searchId (npc.mClass) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid class")); messages.add(id, "Class '" + npc.mClass + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (npc.mRace.empty()) if (npc.mRace.empty())
messages.push_back (std::make_pair (id, npc.mId + " has an empty race")); messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error);
else if (mRaces.searchId (npc.mRace) == -1) else if (mRaces.searchId (npc.mRace) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid race")); messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (disposition < 0) if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid if (npc.mHead.empty())
messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error);
else
{ {
messages.push_back (std::make_pair (id, npc.mId + " has negative reputation")); if (mBodyParts.searchId(npc.mHead) == -1)
messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body parts stuff validity for the specific NPC
} }
if (!npc.mFaction.empty()) if (npc.mHair.empty())
messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error);
else
{ {
if (rank < 0) if (mBodyParts.searchId(npc.mHair) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has negative rank")); messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error);
/// \todo Check gender, race and other body part stuff validity for the specific NPC
if (mFactions.searchId(npc.mFaction) == -1)
messages.push_back (std::make_pair (id, npc.mId + " has invalid faction"));
} }
if (npc.mHead.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no head"));
if (npc.mHair.empty())
messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else
// Check inventory // Check inventory
inventoryListCheck(npc.mInventory.mList, messages, id.toString()); inventoryListCheck(npc.mInventory.mList, messages, id.toString());
@ -793,28 +795,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
weapon.mData.mType == ESM::Weapon::Bolt)) weapon.mData.mType == ESM::Weapon::Bolt))
{ {
if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum slash damage higher than maximum"));
if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum thrust damage higher than maximum"));
} }
if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) if (weapon.mData.mChop[0] > weapon.mData.mChop[1])
messages.push_back (std::make_pair (id, messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning);
weapon.mId + " has minimum chop damage higher than maximum"));
if (!(weapon.mData.mType == ESM::Weapon::Arrow || if (!(weapon.mData.mType == ESM::Weapon::Arrow ||
weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::Bolt ||
weapon.mData.mType == ESM::Weapon::MarksmanThrown)) weapon.mData.mType == ESM::Weapon::MarksmanThrown))
{ {
//checking of health //checking of health
if (weapon.mData.mHealth <= 0) if (weapon.mData.mHealth == 0)
messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health")); messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning);
if (weapon.mData.mReach < 0) if (weapon.mData.mReach < 0)
messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); messages.add(id, "Reach is negative", "", CSMDoc::Message::Severity_Error);
} }
} }
@ -877,7 +876,9 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId);
if (staticElement.mModel.empty()) if (staticElement.mModel.empty())
messages.push_back (std::make_pair (id, staticElement.mId + " has no model")); messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(staticElement.mModel) == -1)
messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
} }
//final check //final check
@ -885,8 +886,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages)
{ {
if (!mPlayerPresent) if (!mPlayerPresent)
messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError);
"There is no player record"));
} }
void CSMTools::ReferenceableCheckStage::inventoryListCheck( void CSMTools::ReferenceableCheckStage::inventoryListCheck(
@ -900,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);
if (localIndex.first == -1) if (localIndex.first == -1)
messages.push_back (std::make_pair (id, messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error);
id + " contains non-existing item (" + itemName + ")"));
else else
{ {
// Needs to accommodate containers, creatures, and NPCs // Needs to accommodate containers, creatures, and NPCs
@ -922,8 +921,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
case CSMWorld::UniversalId::Type_ItemLevelledList: case CSMWorld::UniversalId::Type_ItemLevelledList:
break; break;
default: default:
messages.push_back (std::make_pair(id, messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error);
id + " contains item of invalid type (" + itemName + ")"));
} }
} }
} }
@ -935,67 +933,82 @@ template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemChe
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable)
{ {
if (someItem.mName.empty()) if (someItem.mName.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for weight //Checking for weight
if (someItem.mData.mWeight < 0) if (someItem.mData.mWeight < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error);
//Checking for value //Checking for value
if (someItem.mData.mValue < 0) if (someItem.mData.mValue < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error);
//checking for model //checking for model
if (someItem.mModel.empty()) if (someItem.mModel.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(someItem.mModel) == -1)
messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//checking for icon //checking for icon
if (someItem.mIcon.empty()) if (someItem.mIcon.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error);
else if (mIcons.searchId(someItem.mIcon) == -1)
{
std::string ddsIcon = someItem.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
if (enchantable && someItem.mData.mEnchant < 0) if (enchantable && someItem.mData.mEnchant < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment")); messages.add(someID, "Enchantment points number is negative", "", CSMDoc::Message::Severity_Error);
} }
template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( template<typename Item> void CSMTools::ReferenceableCheckStage::inventoryItemCheck (
const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) const Item& someItem, CSMDoc::Messages& messages, const std::string& someID)
{ {
if (someItem.mName.empty()) if (someItem.mName.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error);
//Checking for weight //Checking for weight
if (someItem.mData.mWeight < 0) if (someItem.mData.mWeight < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error);
//Checking for value //Checking for value
if (someItem.mData.mValue < 0) if (someItem.mData.mValue < 0)
messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error);
//checking for model //checking for model
if (someItem.mModel.empty()) if (someItem.mModel.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error);
else if (mModels.searchId(someItem.mModel) == -1)
messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error);
//checking for icon //checking for icon
if (someItem.mIcon.empty()) if (someItem.mIcon.empty())
messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error);
else if (mIcons.searchId(someItem.mIcon) == -1)
{
std::string ddsIcon = someItem.mIcon;
if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1))
messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
} }
template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck ( template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken)
{ {
if (someTool.mData.mQuality <= 0) if (someTool.mData.mQuality <= 0)
messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error);
if (canBeBroken && someTool.mData.mUses<=0) if (canBeBroken && someTool.mData.mUses<=0)
messages.push_back (std::make_pair (someID, messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error);
someTool.mId + " has non-positive uses count"));
} }
template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck ( template<typename Tool> void CSMTools::ReferenceableCheckStage::toolCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)
{ {
if (someTool.mData.mQuality <= 0) if (someTool.mData.mQuality <= 0)
messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error);
} }
template<typename List> void CSMTools::ReferenceableCheckStage::listCheck ( template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
@ -1004,12 +1017,10 @@ template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
for (unsigned i = 0; i < someList.mList.size(); ++i) for (unsigned i = 0; i < someList.mList.size(); ++i)
{ {
if (mReferencables.searchId(someList.mList[i].mId).first == -1) if (mReferencables.searchId(someList.mList[i].mId).first == -1)
messages.push_back (std::make_pair (someID, messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error);
someList.mId + " contains item without referencable"));
if (someList.mList[i].mLevel < 1) if (someList.mList[i].mLevel < 1)
messages.push_back (std::make_pair (someID, messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error);
someList.mId + " contains item with non-positive level"));
} }
} }
@ -1019,6 +1030,6 @@ template<typename Tool> void CSMTools::ReferenceableCheckStage::scriptCheck (
if (!someTool.mScript.empty()) if (!someTool.mScript.empty())
{ {
if (mScripts.searchId(someTool.mScript) == -1) if (mScripts.searchId(someTool.mScript) == -1)
messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); messages.add(someID, "Script '" + someTool.mScript + "' does not exist", "", CSMDoc::Message::Severity_Error);
} }
} }

@ -5,6 +5,7 @@
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/refiddata.hpp" #include "../world/refiddata.hpp"
#include "../world/resources.hpp"
namespace CSMTools namespace CSMTools
{ {
@ -16,7 +17,10 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Race>& races, const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions, const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::Script>& scripts); const CSMWorld::IdCollection<ESM::Script>& scripts,
const CSMWorld::Resources& models,
const CSMWorld::Resources& icons,
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);
virtual void perform(int stage, CSMDoc::Messages& messages); virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup(); virtual int setup();
@ -81,6 +85,9 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts; const CSMWorld::IdCollection<ESM::Script>& mScripts;
const CSMWorld::Resources& mModels;
const CSMWorld::Resources& mIcons;
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
bool mPlayerPresent; bool mPlayerPresent;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
}; };

@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::IdCollection<ESM::Faction>& factions) const CSMWorld::IdCollection<ESM::Faction>& factions)
: :
mReferences(references), mReferences(references),
mReferencables(referencables), mObjects(referencables),
mDataSet(referencables.getDataSet()), mDataSet(referencables.getDataSet()),
mCells(cells), mCells(cells),
mFactions(factions) mFactions(factions)
@ -28,78 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::CellRef& cellRef = record.get();
const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);
// Check for empty reference id // Check reference id
if (cellRef.mRefID.empty()) { if (cellRef.mRefID.empty())
messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error);
} else { else
{
// Check for non existing referenced object // Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) { if (mObjects.searchId(cellRef.mRefID) == -1)
messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error);
} else { else
{
// Check if reference charge is valid for it's proper referenced type // Check if reference charge is valid for it's proper referenced type
CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);
bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1))
std::string str = " has invalid charge "; messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error);
if (localIndex.second == CSMWorld::UniversalId::Type_Light)
str += std::to_string(cellRef.mChargeFloat);
else
str += std::to_string(cellRef.mChargeInt);
messages.push_back(std::make_pair(id, id.getId() + str));
}
} }
} }
// If object have owner, check if that owner reference is valid // If object have owner, check if that owner reference is valid
if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1)
messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); messages.add(id, "Owner object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error);
// If object have creature soul trapped, check if that creature reference is valid // If object have creature soul trapped, check if that creature reference is valid
if (!cellRef.mSoul.empty()) if (!cellRef.mSoul.empty())
if (mReferencables.searchId(cellRef.mSoul) == -1) if (mObjects.searchId(cellRef.mSoul) == -1)
messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error);
bool hasFaction = !cellRef.mFaction.empty();
// If object have faction, check if that faction reference is valid if (cellRef.mFaction.empty())
if (hasFaction) {
if (cellRef.mFactionRank != -2)
messages.add(id, "Reference without a faction has a faction rank", "", CSMDoc::Message::Severity_Error);
}
else
{
if (mFactions.searchId(cellRef.mFaction) == -1) if (mFactions.searchId(cellRef.mFaction) == -1)
messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction)); messages.add(id, "Faction '" + cellRef.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error);
else if (cellRef.mFactionRank < -1)
// Check item's faction rank messages.add(id, "Invalid faction rank", "", CSMDoc::Message::Severity_Error);
if (hasFaction && cellRef.mFactionRank < -1) }
messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + std::to_string(cellRef.mFactionRank)));
else if (!hasFaction && cellRef.mFactionRank != -2)
messages.push_back(std::make_pair(id, " has invalid faction rank " + std::to_string(cellRef.mFactionRank)));
// If door have destination cell, check if that reference is valid if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1)
if (!cellRef.mDestCell.empty()) messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error);
if (mCells.searchId(cellRef.mDestCell) == -1)
messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell));
// Check if scale isn't negative
if (cellRef.mScale < 0) if (cellRef.mScale < 0)
{ messages.add(id, "Negative scale", "", CSMDoc::Message::Severity_Error);
std::string str = " has negative scale ";
str += std::to_string(cellRef.mScale);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if enchantement points aren't negative or are at full (-1) // Check if enchantement points aren't negative or are at full (-1)
if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) if (cellRef.mEnchantmentCharge < -1)
{ messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error);
std::string str = " has negative enchantment points ";
str += std::to_string(cellRef.mEnchantmentCharge);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if gold value isn't negative // Check if gold value isn't negative
if (cellRef.mGoldValue < 0) if (cellRef.mGoldValue < 0)
{ messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error);
std::string str = " has negative gold value ";
str += cellRef.mGoldValue;
messages.push_back(std::make_pair(id, id.getId() + str));
}
} }
int CSMTools::ReferenceCheckStage::setup() int CSMTools::ReferenceCheckStage::setup()

@ -19,7 +19,7 @@ namespace CSMTools
private: private:
const CSMWorld::RefCollection& mReferences; const CSMWorld::RefCollection& mReferences;
const CSMWorld::RefIdCollection& mReferencables; const CSMWorld::RefIdCollection& mObjects;
const CSMWorld::RefIdData& mDataSet; const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells; const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;

@ -1,10 +1,5 @@
#include "regioncheck.hpp" #include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -36,7 +31,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
// test for empty name // test for empty name
if (region.mName.empty()) if (region.mName.empty())
messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
/// \todo test that the ID in mSleeplist exists /// \todo test that the ID in mSleeplist exists

@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
stream stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")";
<< "script " << mFile
<< ", line " << loc.mLine << ", column " << loc.mColumn
<< " (" << loc.mLiteral << "): " << message;
std::ostringstream hintStream; std::ostringstream hintStream;
@ -47,7 +44,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
std::ostringstream stream; std::ostringstream stream;
stream << "script " << mFile << ": " << message; stream << message;
mMessages->add (id, stream.str(), "", getSeverity (type)); mMessages->add (id, stream.str(), "", getSeverity (type));
} }
@ -128,7 +125,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
std::ostringstream stream; std::ostringstream stream;
stream << "script " << mFile << ": " << error.what(); stream << error.what();
messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError);
} }

@ -1,9 +1,5 @@
#include "skillcheck.hpp" #include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -33,16 +29,12 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
if (skill.mDescription.empty())
messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning);
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0) if (skill.mData.mUseValue[i]<0)
{ {
std::ostringstream stream; messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error);
stream << "Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (std::make_pair (id, stream.str()));
} }
if (skill.mDescription.empty())
messages.push_back (std::make_pair (id, skill.mId + " has an empty description"));
} }

@ -1,15 +1,13 @@
#include "soundcheck.hpp" #include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds) CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound> &sounds,
: mSounds (sounds) const CSMWorld::Resources &soundfiles)
: mSounds (sounds),
mSoundFiles (soundfiles)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -34,7 +32,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange) if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); {
messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning);
/// \todo check, if the sound file exists }
if (sound.mSound.empty())
{
messages.add(id, "Sound file is missing", "", CSMDoc::Message::Severity_Error);
}
else if (mSoundFiles.searchId(sound.mSound) == -1)
{
messages.add(id, "Sound file '" + sound.mSound + "' does not exist", "", CSMDoc::Message::Severity_Error);
}
} }

@ -3,6 +3,7 @@
#include <components/esm/loadsoun.hpp> #include <components/esm/loadsoun.hpp>
#include "../world/resources.hpp"
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
@ -13,11 +14,13 @@ namespace CSMTools
class SoundCheckStage : public CSMDoc::Stage class SoundCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Sound>& mSounds; const CSMWorld::IdCollection<ESM::Sound>& mSounds;
const CSMWorld::Resources &mSoundFiles;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
public: public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds); SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds,
const CSMWorld::Resources &soundfiles);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

@ -1,7 +1,5 @@
#include "soundgencheck.hpp" #include "soundgencheck.hpp"
#include <sstream>
#include "../prefs/state.hpp" #include "../prefs/state.hpp"
#include "../world/refiddata.hpp" #include "../world/refiddata.hpp"
@ -9,10 +7,10 @@
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens, CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables) const CSMWorld::RefIdCollection &objects)
: mSoundGens(soundGens), : mSoundGens(soundGens),
mSounds(sounds), mSounds(sounds),
mReferenceables(referenceables) mObjects(objects)
{ {
mIgnoreBaseRecords = false; mIgnoreBaseRecords = false;
} }
@ -37,23 +35,23 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages
if (!soundGen.mCreature.empty()) if (!soundGen.mCreature.empty())
{ {
CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature);
if (creatureIndex.first == -1) if (creatureIndex.first == -1)
{ {
messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
} }
else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature)
{ {
messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error);
} }
} }
if (soundGen.mSound.empty()) if (soundGen.mSound.empty())
{ {
messages.push_back(std::make_pair(id, "Sound is not specified")); messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error);
} }
else if (mSounds.searchId(soundGen.mSound) == -1) else if (mSounds.searchId(soundGen.mSound) == -1)
{ {
messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error);
} }
} }

@ -12,13 +12,13 @@ namespace CSMTools
{ {
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens; const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
const CSMWorld::IdCollection<ESM::Sound> &mSounds; const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::RefIdCollection &mObjects;
bool mIgnoreBaseRecords; bool mIgnoreBaseRecords;
public: public:
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens, SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds, const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables); const CSMWorld::RefIdCollection &objects);
virtual int setup(); virtual int setup();
///< \return number of steps ///< \return number of steps

@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
// test for empty name and description // test for empty name
if (spell.mName.empty()) if (spell.mName.empty())
messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error);
// test for invalid cost values // test for invalid cost values
if (spell.mData.mCost<0) if (spell.mData.mCost<0)
messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error);
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view
} }

@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId);
if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1)
messages.push_back ( messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error);
std::make_pair (id, "Start script " + scriptId + " does not exist"));
} }
int CSMTools::StartScriptCheckStage::setup() int CSMTools::StartScriptCheckStage::setup()

@ -32,6 +32,7 @@
#include "gmstcheck.hpp" #include "gmstcheck.hpp"
#include "topicinfocheck.hpp" #include "topicinfocheck.hpp"
#include "journalcheck.hpp" #include "journalcheck.hpp"
#include "enchantmentcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
@ -74,15 +75,17 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces())); mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces()));
mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds())); mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));
mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions()));
mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures)));
mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells()));
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(),
mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons),
mData.getBodyParts()));
mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
@ -126,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos())); mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments()));
mVerifier.setOperation (mVerifierOperation); mVerifier.setOperation (mVerifierOperation);
} }

@ -133,8 +133,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
{ {
std::ostringstream stream; messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error);
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
} }
if (!topicInfo.mRace.empty()) if (!topicInfo.mRace.empty())
@ -166,23 +165,24 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id, bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Actor";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
if (index.first == -1) if (index.first == -1)
{ {
writeMissingIdError(specifier, actor, id, messages); messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (mReferencables.getRecord(index).isDeleted()) else if (mReferencables.getRecord(index).isDeleted())
{ {
writeDeletedRecordError(specifier, actor, id, messages); messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
{ {
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages); CSMWorld::UniversalId tempId(index.second, actor);
std::ostringstream stream;
stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
@ -192,11 +192,9 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id, bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Cell";
if (mCellNames.find(cell) == mCellNames.end()) if (mCellNames.find(cell) == mCellNames.end())
{ {
writeMissingIdError(specifier, cell, id, messages); messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
@ -209,9 +207,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank < -1) if (rank < -1)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
@ -229,8 +226,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
if (rank >= limit) if (rank >= limit)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1
<< " for the " << factionName << " faction"; << " for the '" << factionName << "' faction";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
@ -242,18 +239,16 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id, bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Item";
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
if (index.first == -1) if (index.first == -1)
{ {
writeMissingIdError(specifier, item, id, messages); messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (mReferencables.getRecord(index).isDeleted()) else if (mReferencables.getRecord(index).isDeleted())
{ {
writeDeletedRecordError(specifier, item, id, messages); messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else else
@ -276,8 +271,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS
break; break;
default: default:
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages); {
CSMWorld::UniversalId tempId(index.second, item);
std::ostringstream stream;
stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
}
} }
} }
@ -291,13 +291,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
{ {
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error); messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (!infoCondition.variantTypeIsValid()) else if (!infoCondition.variantTypeIsValid())
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of "; stream << "Value of condition '" << infoCondition.toString() << "' has invalid ";
switch (select.mValue.getType()) switch (select.mValue.getType())
{ {
@ -307,26 +307,21 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
case ESM::VT_Long: stream << "Long"; break; case ESM::VT_Long: stream << "Long"; break;
case ESM::VT_Float: stream << "Float"; break; case ESM::VT_Float: stream << "Float"; break;
case ESM::VT_String: stream << "String"; break; case ESM::VT_String: stream << "String"; break;
default: stream << "Unknown"; break; default: stream << "unknown"; break;
} }
stream << " type";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (infoCondition.conditionIsAlwaysTrue()) else if (infoCondition.conditionIsAlwaysTrue())
{ {
std::ostringstream stream; messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning);
stream << "Info Condition: " << infoCondition.toString() << " is always true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false; return false;
} }
else if (infoCondition.conditionIsNeverTrue()) else if (infoCondition.conditionIsNeverTrue())
{ {
std::ostringstream stream; messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning);
stream << "Info Condition: " << infoCondition.toString() << " is never true";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
return false; return false;
} }
@ -383,11 +378,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id, bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages) CSMDoc::Messages& messages)
{ {
const std::string specifier = "Sound File";
if (mSoundFiles.searchId(sound) == -1) if (mSoundFiles.searchId(sound) == -1)
{ {
writeMissingIdError(specifier, sound, id, messages); messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
@ -402,47 +395,14 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
if (index == -1) if (index == -1)
{ {
writeMissingIdError(T::getRecordType(), name, id, messages); messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (collection.getRecord(index).isDeleted()) else if (collection.getRecord(index).isDeleted())
{ {
writeDeletedRecordError(T::getRecordType(), name, id, messages); messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
return true; return true;
} }
// Error functions
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
std::ostringstream stream;
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages)
{
CSMWorld::UniversalId tempId(invalidType, invalidId);
std::ostringstream stream;
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
<< invalidId << "\" (can be of type " << expectedType << ")";
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
}

@ -80,17 +80,6 @@ namespace CSMTools
template <typename T> template <typename T>
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection, bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
// Common error messages
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
}; };
} }

@ -24,7 +24,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke
if (type!=' ') if (type!=' ')
{ {
/// \todo add option to make re-declared local variables an error /// \todo add option to make re-declared local variables an error
getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", getErrorHandler().warning ("ignoring local variable re-declaration",
loc); loc);
mState = State_End; mState = State_End;

@ -422,7 +422,7 @@ namespace Compiler
{ {
if (!hasExplicit) if (!hasExplicit)
{ {
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); getErrorHandler().warning ("ignoring stray explicit reference", loc);
mExplicit.clear(); mExplicit.clear();
} }
@ -791,7 +791,7 @@ namespace Compiler
++optionalCount; ++optionalCount;
} }
else else
getErrorHandler().warning ("Ignoring extra argument", getErrorHandler().warning ("ignoring extra argument",
stringParser.getTokenLoc()); stringParser.getTokenLoc());
} }
else if (*iter=='X') else if (*iter=='X')
@ -805,7 +805,7 @@ namespace Compiler
if (parser.isEmpty()) if (parser.isEmpty())
break; break;
else else
getErrorHandler().warning("Ignoring extra argument", parser.getTokenLoc()); getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc());
} }
else if (*iter=='z') else if (*iter=='z')
{ {
@ -817,7 +817,7 @@ namespace Compiler
if (discardParser.isEmpty()) if (discardParser.isEmpty())
break; break;
else else
getErrorHandler().warning("Ignoring extra argument", discardParser.getTokenLoc()); getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc());
} }
else if (*iter=='j') else if (*iter=='j')
{ {

@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l
bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{ {
if (keyword==mIgnoreKeyword) if (keyword==mIgnoreKeyword)
reportWarning ("found junk (ignoring it)", loc); reportWarning ("ignoring found junk", loc);
else else
scanner.putbackKeyword (keyword, loc); scanner.putbackKeyword (keyword, loc);
@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann
bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{ {
if (code==Scanner::S_member) if (code==Scanner::S_member)
reportWarning ("found junk (ignoring it)", loc); reportWarning ("ignoring found junk", loc);
else else
scanner.putbackSpecial (code, loc); scanner.putbackSpecial (code, loc);

@ -88,7 +88,7 @@ namespace Compiler
{ {
if (mState==PotentialEndState) if (mState==PotentialEndState)
{ {
getErrorHandler().warning ("stray string argument (ignoring it)", loc); getErrorHandler().warning ("ignoring stray string argument", loc);
mState = EndState; mState = EndState;
return true; return true;
} }
@ -233,7 +233,7 @@ namespace Compiler
if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to)
{ {
getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); getErrorHandler().warning ("unknown variable, ignoring set instruction", loc);
SkipParser skip (getErrorHandler(), getContext()); SkipParser skip (getErrorHandler(), getContext());
scanner.scan (skip); scanner.scan (skip);
return false; return false;
@ -286,7 +286,7 @@ namespace Compiler
{ {
if (!hasExplicit && mState==ExplicitState) if (!hasExplicit && mState==ExplicitState)
{ {
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); getErrorHandler().warning ("ignoring stray explicit reference", loc);
mExplicit.clear(); mExplicit.clear();
} }
@ -344,7 +344,7 @@ namespace Compiler
{ {
if (!hasExplicit && !mExplicit.empty()) if (!hasExplicit && !mExplicit.empty())
{ {
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); getErrorHandler().warning ("ignoring stray explicit reference", loc);
mExplicit.clear(); mExplicit.clear();
} }
@ -360,7 +360,7 @@ namespace Compiler
if (mState==ExplicitState) if (mState==ExplicitState)
{ {
// drop stray explicit reference // drop stray explicit reference
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); getErrorHandler().warning ("ignoring stray explicit reference", loc);
mState = BeginState; mState = BeginState;
mExplicit.clear(); mExplicit.clear();
} }
@ -412,19 +412,19 @@ namespace Compiler
case Scanner::K_else: case Scanner::K_else:
getErrorHandler().warning ("stray else (ignoring it)", loc); getErrorHandler().warning ("ignoring stray else", loc);
mState = EndState; mState = EndState;
return true; return true;
case Scanner::K_endif: case Scanner::K_endif:
getErrorHandler().warning ("stray endif (ignoring it)", loc); getErrorHandler().warning ("ignoring stray endif", loc);
mState = EndState; mState = EndState;
return true; return true;
case Scanner::K_begin: case Scanner::K_begin:
getErrorHandler().warning ("stray begin (ignoring it)", loc); getErrorHandler().warning ("ignoring stray begin", loc);
mState = EndState; mState = EndState;
return true; return true;
} }
@ -491,7 +491,7 @@ namespace Compiler
{ {
if (mState==EndState && code==Scanner::S_open) if (mState==EndState && code==Scanner::S_open)
{ {
getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line",
loc); loc);
return true; return true;
} }

Loading…
Cancel
Save