Example #1
0
void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::BirthSign& birthsign = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);

    // test for empty name, description and texture
    if (birthsign.mName.empty())
        messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name"));

    if (birthsign.mDescription.empty())
        messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description"));

    if (birthsign.mTexture.empty())
        messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture"));

    /// \todo test if the texture exists

    /// \todo check data members that can't be edited in the table view
}
Example #2
0
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
    const CSMWorld::Record<ESM::MagicEffect> &record = mMagicEffects.getRecord(stage);

    // Skip "Base" records (setting!) and "Deleted" records
    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
        return;

    ESM::MagicEffect effect = record.get();
    CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
    
    if (effect.mData.mBaseCost < 0.0f)
    {
        messages.push_back(std::make_pair(id, "Base Cost is negative"));
    }

    if (effect.mIcon.empty())
    {
        messages.push_back(std::make_pair(id, "Icon is not specified"));
    }
    else if (!isTextureExists(effect.mIcon, true))
    {
        messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'"));
    }

    if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false))
    {
        messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'"));
    }

    addMessageIfNotEmpty(messages, 
                         id, 
                         checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
    addMessageIfNotEmpty(messages, 
                         id,
                         checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
    addMessageIfNotEmpty(messages,
                         id,
                         checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
    addMessageIfNotEmpty(messages,
                         id,
                         checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));

    addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
    addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
    addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
    addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));

    if (effect.mDescription.empty())
    {
        messages.push_back(std::make_pair(id, "Description is empty"));
    }
}
Example #3
0
void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Class& class_ = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);

    // test for empty name and description
    if (class_.mName.empty())
        messages.push_back (std::make_pair (id, class_.mId + " has an empty name"));

    if (class_.mDescription.empty())
        messages.push_back (std::make_pair (id, class_.mId + " has an empty description"));

    // test for invalid attributes
    for (int i=0; i<2; ++i)
        if (class_.mData.mAttribute[i]==-1)
        {
            std::ostringstream stream;

            stream << "Attribute #" << i << " of " << class_.mId << " is not set";

            messages.push_back (std::make_pair (id, stream.str()));
        }

    if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
    {
        messages.push_back (std::make_pair (id, "Class lists same attribute twice"));
    }

    // test for non-unique skill
    std::map<int, int> skills; // ID, number of occurrences

    for (int i=0; i<5; ++i)
        for (int i2=0; i2<2; ++i2)
            ++skills[class_.mData.mSkills[i][i2]];

    for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
        if (iter->second>1)
        {
            messages.push_back (std::make_pair (id,
                ESM::Skill::indexToId (iter->first) + " is listed more than once"));
        }
}
Example #4
0
void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
{
    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);

    if (!mPlayable)
        messages.push_back (std::make_pair (id, "No playable race"));
}
Example #5
0
void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Spell& spell = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);

    // test for empty name and description
    if (spell.mName.empty())
        messages.push_back (std::make_pair (id, spell.mId + " has an empty name"));

    // test for invalid cost values
    if (spell.mData.mCost<0)
        messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs"));

    /// \todo check data members that can't be edited in the table view
}
Example #6
0
void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Faction& faction = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId);

    // test for empty name
    if (faction.mName.empty())
        messages.push_back (std::make_pair (id, faction.mId + " has an empty name"));

    // test for invalid attributes
    if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)
    {
        messages.push_back (std::make_pair (id , "Faction lists same attribute twice"));
    }

    // test for non-unique skill
    std::map<int, int> skills; // ID, number of occurrences

    for (int i=0; i<7; ++i)
        if (faction.mData.mSkills[i]!=-1)
            ++skills[faction.mData.mSkills[i]];

    for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
        if (iter->second>1)
        {
            messages.push_back (std::make_pair (id,
                ESM::Skill::indexToId (iter->first) + " is listed more than once"));
        }

    /// \todo check data members that can't be edited in the table view
}
Example #7
0
void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Sound& sound = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);

    if (sound.mData.mMinRange>sound.mData.mMaxRange)
        messages.push_back (std::make_pair (id, "Maximum range larger than minimum range"));

    /// \todo check, if the sound file exists
}
Example #8
0
void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage);

    // Skip "Base" records (setting!) and "Deleted" records
    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
        return;

    const ESM::Sound& sound = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);

    if (sound.mData.mMinRange>sound.mData.mMaxRange)
        messages.push_back (std::make_pair (id, "Minimum range larger than maximum range"));

    /// \todo check, if the sound file exists
}
Example #9
0
void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Region& region = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId);

    // test for empty name
    if (region.mName.empty())
        messages.push_back (std::make_pair (id, region.mId + " has an empty name"));

    /// \todo test that the ID in mSleeplist exists

    /// \todo check data members that can't be edited in the table view
}
Example #10
0
void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    mId = mDocument.getData().getScripts().getId (stage);

    if (mDocument.isBlacklisted (
        CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId)))
        return;

    mMessages = &messages;

    try
    {
        const CSMWorld::Data& data = mDocument.getData();

        mFile = data.getScripts().getRecord (stage).get().mId;
        std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText);

        Compiler::Scanner scanner (*this, input, mContext.getExtensions());

        Compiler::FileParser parser (*this, mContext);

        scanner.scan (parser);
    }
    catch (const Compiler::SourceException&)
    {
        // error has already been reported via error handler
    }
    catch (const std::exception& error)
    {
        CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);

        messages.push_back (std::make_pair (id,
            std::string ("Critical compile error: ") + error.what()));
    }

    mMessages = 0;
}
Example #11
0
void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &messages)
{
    const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage);

    // Skip "Base" records (setting!) and "Deleted" records
    if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
        return;

    const ESM::BodyPart &bodyPart = record.get();

    CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId );

    // Check BYDT
    if (bodyPart.mData.mPart > 14 )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." ));

    if (bodyPart.mData.mFlags > 3 )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." ));

    if (bodyPart.mData.mType > 2 )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." ));

    // Check MODL

    if ( bodyPart.mModel.empty() )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." ));
    else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." ));

    // Check FNAM

    if ( bodyPart.mRace.empty() )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." ));
    else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
        messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." ));
}
Example #12
0
void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<ESM::Race>& record = mRaces.getRecord (stage);

    if (record.isDeleted())
        return;

    const ESM::Race& race = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);

    // test for empty name and description
    if (race.mName.empty())
        messages.push_back (std::make_pair (id, race.mId + " has an empty name"));

    if (race.mDescription.empty())
        messages.push_back (std::make_pair (id, race.mId + " has an empty description"));

    // test for positive height
    if (race.mData.mHeight.mMale<=0)
        messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height"));

    if (race.mData.mHeight.mFemale<=0)
        messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height"));

    // test for non-negative weight
    if (race.mData.mWeight.mMale<0)
        messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight"));

    if (race.mData.mWeight.mFemale<0)
        messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight"));

    // remember playable flag
    if (race.mData.mFlags & 0x1)
        mPlayable = true;

    /// \todo check data members that can't be edited in the table view
}
Example #13
0
void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
    const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);

    if (record.isDeleted())
        return;

    const CSMWorld::Pathgrid& pathgrid = record.get();

    CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId);

    // check the number of pathgrid points
    if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
        messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected"));
    else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
        messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected"));

    std::vector<Point> pointList(pathgrid.mPoints.size());
    std::vector<int> duplList;

    for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i)
    {
        if (pathgrid.mEdges[i].mV0 < static_cast<int>(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0)
        {
            pointList[pathgrid.mEdges[i].mV0].mConnectionNum++;
            // first check for duplicate edges
            unsigned int j = 0;
            for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j)
            {
                if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
                {
                    std::ostringstream ss;
                    ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0
                        << " and " << pathgrid.mEdges[i].mV1;
                    messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
                    break;
                }
            }

            // only add if not a duplicate
            if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size())
                pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1);
        }
        else
        {
            std::ostringstream ss;
            ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0;
            messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
        }
    }

    for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i)
    {
        // check the connection number for each point matches the edge connections
        if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum)
        {
            std::ostringstream ss;
            ss << " has has less edges than expected for point " << i;
            messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
        }
        else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum)
        {
            std::ostringstream ss;
            ss << " has has more edges than expected for point " << i;
            messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
        }

        // check that edges are bidirectional
        bool foundReverse = false;
        for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j)
        {
            for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k)
            {
                if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast<int>(i))
                {
                    foundReverse = true;
                    break;
                }
            }

            if (!foundReverse)
            {
                std::ostringstream ss;
                ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j];
                messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
            }
        }

        // check duplicate points
        // FIXME: how to do this efficiently?
        for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j)
        {
            if (j == i)
                continue;

            if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&
                pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
                pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)
            {
                std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), i);
                if (it == duplList.end())
                {
                    std::ostringstream ss;
                    ss << " has a duplicated point (" << i
                        << ") x=" << pathgrid.mPoints[i].mX
                        << ", y=" << pathgrid.mPoints[i].mY
                        << ", z=" << pathgrid.mPoints[i].mZ;
                    messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));

                    duplList.push_back(i);
                    break;
                }
            }
        }
    }

    // check pathgrid points that are not connected to anything
    for (unsigned int i = 0; i < pointList.size(); ++i)
    {
        if (pointList[i].mConnectionNum == 0)
        {
            std::ostringstream ss;
            ss << " has an orphaned point (" << i
                << ") x=" << pathgrid.mPoints[i].mX
                << ", y=" << pathgrid.mPoints[i].mY
                << ", z=" << pathgrid.mPoints[i].mZ;
            messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
        }
    }

    // TODO: check whether there are disconnected graphs
}