1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-30 16:20:21 +00:00

Validate INFO filters when loading the record

This commit is contained in:
Evil Eye 2024-04-04 19:45:29 +02:00
parent c6921d5292
commit fb4edda45d
4 changed files with 94 additions and 12 deletions

View File

@ -30,7 +30,7 @@ namespace
{
bool matchesStaticFilters(const MWDialogue::SelectWrapper& select, const MWWorld::Ptr& actor)
{
const ESM::RefId selectId = ESM::RefId::stringRefId(select.getName());
const ESM::RefId selectId = select.getId();
if (select.getFunction() == MWDialogue::SelectWrapper::Function_NotId)
return actor.getCellRef().getRefId() != selectId;
if (actor.getClass().isNpc())
@ -356,19 +356,18 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
{
case SelectWrapper::Function_Journal:
return MWBase::Environment::get().getJournal()->getJournalIndex(ESM::RefId::stringRefId(select.getName()));
return MWBase::Environment::get().getJournal()->getJournalIndex(select.getId());
case SelectWrapper::Function_Item:
{
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
return store.count(ESM::RefId::stringRefId(select.getName()));
return store.count(select.getId());
}
case SelectWrapper::Function_Dead:
return MWBase::Environment::get().getMechanicsManager()->countDeaths(
ESM::RefId::stringRefId(select.getName()));
return MWBase::Environment::get().getMechanicsManager()->countDeaths(select.getId());
case SelectWrapper::Function_Choice:
@ -546,24 +545,24 @@ bool MWDialogue::Filter::getSelectStructBoolean(const SelectWrapper& select) con
case SelectWrapper::Function_NotId:
return !(mActor.getCellRef().getRefId() == ESM::RefId::stringRefId(select.getName()));
return mActor.getCellRef().getRefId() != select.getId();
case SelectWrapper::Function_NotFaction:
return !(mActor.getClass().getPrimaryFaction(mActor) == ESM::RefId::stringRefId(select.getName()));
return mActor.getClass().getPrimaryFaction(mActor) != select.getId();
case SelectWrapper::Function_NotClass:
return !(mActor.get<ESM::NPC>()->mBase->mClass == ESM::RefId::stringRefId(select.getName()));
return mActor.get<ESM::NPC>()->mBase->mClass != select.getId();
case SelectWrapper::Function_NotRace:
return !(mActor.get<ESM::NPC>()->mBase->mRace == ESM::RefId::stringRefId(select.getName()));
return mActor.get<ESM::NPC>()->mBase->mRace != select.getId();
case SelectWrapper::Function_NotCell:
{
std::string_view actorCell = MWBase::Environment::get().getWorld()->getCellName(mActor.getCell());
return !Misc::StringUtils::ciStartsWith(actorCell, select.getName());
return !Misc::StringUtils::ciStartsWith(actorCell, select.getCellName());
}
case SelectWrapper::Function_SameGender:

View File

@ -454,5 +454,15 @@ bool MWDialogue::SelectWrapper::selectCompare(bool value) const
std::string MWDialogue::SelectWrapper::getName() const
{
return Misc::StringUtils::lowerCase(std::string_view(mSelect.mSelectRule).substr(5));
return Misc::StringUtils::lowerCase(getCellName());
}
std::string_view MWDialogue::SelectWrapper::getCellName() const
{
return std::string_view(mSelect.mSelectRule).substr(5);
}
ESM::RefId MWDialogue::SelectWrapper::getId() const
{
return ESM::RefId::stringRefId(getCellName());
}

View File

@ -95,6 +95,10 @@ namespace MWDialogue
std::string getName() const;
///< Return case-smashed name.
std::string_view getCellName() const;
ESM::RefId getId() const;
};
}

View File

@ -3,7 +3,65 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include <components/debug/debuglog.hpp>
#include <components/misc/concepts.hpp>
#include <components/misc/strings/conversion.hpp>
namespace
{
enum class SelectRuleStatus
{
Valid,
Invalid,
Ignorable
};
SelectRuleStatus isValidSelectRule(std::string_view rule)
{
if (rule.size() < 5)
return SelectRuleStatus::Invalid;
if (rule[4] < '0' || rule[4] > '5') // Comparison operators
return SelectRuleStatus::Invalid;
if (rule[1] == '1') // Function
{
int function = Misc::StringUtils::toNumeric<int>(rule.substr(2, 2), -1);
if (function >= 0 && function <= 73)
return SelectRuleStatus::Valid;
return SelectRuleStatus::Invalid;
}
if (rule.size() == 5) // Missing ID
return SelectRuleStatus::Invalid;
if (rule[3] != 'X')
return SelectRuleStatus::Ignorable;
constexpr auto ignorable
= [](bool valid) { return valid ? SelectRuleStatus::Valid : SelectRuleStatus::Ignorable; };
switch (rule[1])
{
case '2':
case '3':
case 'C':
return ignorable(rule[2] == 's' || rule[2] == 'l' || rule[2] == 'f');
case '4':
return ignorable(rule[2] == 'J');
case '5':
return ignorable(rule[2] == 'I');
case '6':
return ignorable(rule[2] == 'D');
case '7':
return ignorable(rule[2] == 'X');
case '8':
return ignorable(rule[2] == 'F');
case '9':
return ignorable(rule[2] == 'C');
case 'A':
return ignorable(rule[2] == 'R');
case 'B':
return ignorable(rule[2] == 'L');
default:
return SelectRuleStatus::Invalid;
}
}
}
namespace ESM
{
@ -69,7 +127,18 @@ namespace ESM
SelectStruct ss;
ss.mSelectRule = esm.getHString();
ss.mValue.read(esm, Variant::Format_Info);
mSelects.push_back(ss);
auto valid = isValidSelectRule(ss.mSelectRule);
if (ss.mValue.getType() != VT_Int && ss.mValue.getType() != VT_Float)
valid = SelectRuleStatus::Invalid;
if (valid == SelectRuleStatus::Invalid)
Log(Debug::Warning) << "Skipping invalid SCVR for INFO " << mId;
else
{
mSelects.push_back(ss);
if (valid == SelectRuleStatus::Ignorable)
Log(Debug::Info)
<< "Found malformed SCVR for INFO " << mId << " at index " << ss.mSelectRule[0];
}
break;
}
case fourCC("BNAM"):