Skip to content

Commit

Permalink
Merge branch 'filterview' into 'master'
Browse files Browse the repository at this point in the history
Validate INFO filters when loading the record

See merge request OpenMW/openmw!4003
  • Loading branch information
psi29a committed Apr 17, 2024
2 parents b91ff63 + b016f41 commit 3600c6c
Show file tree
Hide file tree
Showing 17 changed files with 1,114 additions and 1,384 deletions.
11 changes: 7 additions & 4 deletions apps/esmtool/labels.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "labels.hpp"

#include <components/esm3/dialoguecondition.hpp>
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadbody.hpp>
#include <components/esm3/loadcell.hpp>
Expand Down Expand Up @@ -572,13 +573,14 @@ std::string_view enchantTypeLabel(int idx)

std::string_view ruleFunction(int idx)
{
if (idx >= 0 && idx <= 72)
if (idx >= ESM::DialogueCondition::Function_FacReactionLowest
&& idx <= ESM::DialogueCondition::Function_PcWerewolfKills)
{
static constexpr std::string_view ruleFunctions[] = {
"Reaction Low",
"Reaction High",
"Lowest Faction Reaction",
"Highest Faction Reaction",
"Rank Requirement",
"NPC? Reputation",
"NPC Reputation",
"Health Percent",
"Player Reputation",
"NPC Level",
Expand Down Expand Up @@ -648,6 +650,7 @@ std::string_view ruleFunction(int idx)
"Flee",
"Should Attack",
"Werewolf",
"Werewolf Kills",
};
return ruleFunctions[idx];
}
Expand Down
128 changes: 49 additions & 79 deletions apps/esmtool/record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,120 +57,90 @@ namespace
std::cout << " Cell Name: " << p.mCellName << std::endl;
}

std::string ruleString(const ESM::DialInfo::SelectStruct& ss)
std::string ruleString(const ESM::DialogueCondition& ss)
{
std::string rule = ss.mSelectRule;
std::string_view type_str = "INVALID";
std::string_view func_str;

if (rule.length() < 5)
return "INVALID";

char type = rule[1];
char indicator = rule[2];

std::string type_str = "INVALID";
std::string func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3));
int func = Misc::StringUtils::toNumeric<int>(rule.substr(2, 2), 0);

switch (type)
switch (ss.mFunction)
{
case '1':
type_str = "Function";
func_str = std::string(ruleFunction(func));
case ESM::DialogueCondition::Function_Global:
type_str = "Global";
func_str = ss.mVariable;
break;
case '2':
if (indicator == 's')
type_str = "Global short";
else if (indicator == 'l')
type_str = "Global long";
else if (indicator == 'f')
type_str = "Global float";
case ESM::DialogueCondition::Function_Local:
type_str = "Local";
func_str = ss.mVariable;
break;
case '3':
if (indicator == 's')
type_str = "Local short";
else if (indicator == 'l')
type_str = "Local long";
else if (indicator == 'f')
type_str = "Local float";
case ESM::DialogueCondition::Function_Journal:
type_str = "Journal";
func_str = ss.mVariable;
break;
case '4':
if (indicator == 'J')
type_str = "Journal";
case ESM::DialogueCondition::Function_Item:
type_str = "Item count";
func_str = ss.mVariable;
break;
case '5':
if (indicator == 'I')
type_str = "Item type";
case ESM::DialogueCondition::Function_Dead:
type_str = "Dead";
func_str = ss.mVariable;
break;
case '6':
if (indicator == 'D')
type_str = "NPC Dead";
case ESM::DialogueCondition::Function_NotId:
type_str = "Not ID";
func_str = ss.mVariable;
break;
case '7':
if (indicator == 'X')
type_str = "Not ID";
case ESM::DialogueCondition::Function_NotFaction:
type_str = "Not Faction";
func_str = ss.mVariable;
break;
case '8':
if (indicator == 'F')
type_str = "Not Faction";
case ESM::DialogueCondition::Function_NotClass:
type_str = "Not Class";
func_str = ss.mVariable;
break;
case '9':
if (indicator == 'C')
type_str = "Not Class";
case ESM::DialogueCondition::Function_NotRace:
type_str = "Not Race";
func_str = ss.mVariable;
break;
case 'A':
if (indicator == 'R')
type_str = "Not Race";
case ESM::DialogueCondition::Function_NotCell:
type_str = "Not Cell";
func_str = ss.mVariable;
break;
case 'B':
if (indicator == 'L')
type_str = "Not Cell";
break;
case 'C':
if (indicator == 's')
type_str = "Not Local";
case ESM::DialogueCondition::Function_NotLocal:
type_str = "Not Local";
func_str = ss.mVariable;
break;
default:
type_str = "Function";
func_str = ruleFunction(ss.mFunction);
break;
}

// Append the variable name to the function string if any.
if (type != '1')
func_str = rule.substr(5);

// In the previous switch, we assumed that the second char was X
// for all types not qual to one. If this wasn't true, go back to
// the error message.
if (type != '1' && rule[3] != 'X')
func_str = Misc::StringUtils::format("INVALID=%s", rule.substr(1, 3));

char oper = rule[4];
std::string oper_str = "??";
switch (oper)
std::string_view oper_str = "??";
switch (ss.mComparison)
{
case '0':
case ESM::DialogueCondition::Comp_Eq:
oper_str = "==";
break;
case '1':
case ESM::DialogueCondition::Comp_Ne:
oper_str = "!=";
break;
case '2':
case ESM::DialogueCondition::Comp_Gt:
oper_str = "> ";
break;
case '3':
case ESM::DialogueCondition::Comp_Ge:
oper_str = ">=";
break;
case '4':
case ESM::DialogueCondition::Comp_Ls:
oper_str = "< ";
break;
case '5':
case ESM::DialogueCondition::Comp_Le:
oper_str = "<=";
break;
default:
break;
}

std::ostringstream stream;
stream << ss.mValue;
std::visit([&](auto value) { stream << value; }, ss.mValue);

std::string result
= Misc::StringUtils::format("%-12s %-32s %2s %s", type_str, func_str, oper_str, stream.str());
Expand Down Expand Up @@ -842,7 +812,7 @@ namespace EsmTool
<< std::endl;
std::cout << " Type: " << dialogTypeLabel(mData.mData.mType) << std::endl;

for (const ESM::DialInfo::SelectStruct& rule : mData.mSelects)
for (const auto& rule : mData.mSelects)
std::cout << " Select Rule: " << ruleString(rule) << std::endl;

if (!mData.mResultScript.empty())
Expand Down
79 changes: 22 additions & 57 deletions apps/opencs/model/tools/topicinfocheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,9 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message

// Check info conditions

for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();
it != topicInfo.mSelects.end(); ++it)
for (const auto& select : topicInfo.mSelects)
{
verifySelectStruct((*it), id, messages);
verifySelectStruct(select, id, messages);
}
}

Expand Down Expand Up @@ -308,49 +307,15 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(
}

bool CSMTools::TopicInfoCheckStage::verifySelectStruct(
const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
const ESM::DialogueCondition& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
{
CSMWorld::ConstInfoSelectWrapper infoCondition(select);

if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
if (select.mFunction == ESM::DialogueCondition::Function_None)
{
messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error);
return false;
}
else if (!infoCondition.variantTypeIsValid())
{
std::ostringstream stream;
stream << "Value of condition '" << infoCondition.toString() << "' has invalid ";

switch (select.mValue.getType())
{
case ESM::VT_None:
stream << "None";
break;
case ESM::VT_Short:
stream << "Short";
break;
case ESM::VT_Int:
stream << "Int";
break;
case ESM::VT_Long:
stream << "Long";
break;
case ESM::VT_Float:
stream << "Float";
break;
case ESM::VT_String:
stream << "String";
break;
default:
stream << "unknown";
break;
}
stream << " type";

messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
return false;
}
else if (infoCondition.conditionIsAlwaysTrue())
{
messages.add(
Expand All @@ -365,48 +330,48 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(
}

// Id checks
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mGlobals, id, messages))
if (select.mFunction == ESM::DialogueCondition::Function_Global
&& !verifyId(ESM::RefId::stringRefId(select.mVariable), mGlobals, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mJournals, id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_Journal
&& !verifyId(ESM::RefId::stringRefId(select.mVariable), mJournals, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item
&& !verifyItem(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_Item
&& !verifyItem(ESM::RefId::stringRefId(select.mVariable), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead
&& !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_Dead
&& !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId
&& !verifyActor(ESM::RefId::stringRefId(infoCondition.getVariableName()), id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_NotId
&& !verifyActor(ESM::RefId::stringRefId(select.mVariable), id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mFactions, id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_NotFaction
&& !verifyId(ESM::RefId::stringRefId(select.mVariable), mFactions, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mClasses, id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_NotClass
&& !verifyId(ESM::RefId::stringRefId(select.mVariable), mClasses, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace
&& !verifyId(ESM::RefId::stringRefId(infoCondition.getVariableName()), mRaces, id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_NotRace
&& !verifyId(ESM::RefId::stringRefId(select.mVariable), mRaces, id, messages))
{
return false;
}
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell
&& !verifyCell(infoCondition.getVariableName(), id, messages))
else if (select.mFunction == ESM::DialogueCondition::Function_NotCell
&& !verifyCell(select.mVariable, id, messages))
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/opencs/model/tools/topicinfocheck.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ namespace CSMTools
const ESM::RefId& name, int rank, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyItem(const ESM::RefId& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifySelectStruct(
const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
const ESM::DialogueCondition& select, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);

template <typename T>
Expand Down
Loading

0 comments on commit 3600c6c

Please sign in to comment.