Handle cases where if/else block with single exprstatement was incorrectly identified as ternary operator (closes #4)

This commit is contained in:
Vlad
2023-06-30 20:51:02 +02:00
committed by GitHub
parent ac48069a21
commit 557e243968
5 changed files with 41 additions and 20 deletions
+27 -12
View File
@@ -539,27 +539,30 @@ uint32_t CFalloutScript::BuildTreeBranch(CNodeArray& NodeArray, uint32_t nStartI
if (wOperator == COpcode::O_IF)
{
// process possible conditional expression - this may be either normal IF statement or (x IF y ELSE z) expression
uint32_t ulElseOffset = NodeArray[j].m_Arguments[0].m_Opcode.GetArgument();
int32_t ulElseIndex, ulSkipIndex = -1;
CNodeArray& arguments = NodeArray[j].m_Arguments;
uint32_t ulElseOffset = arguments[0].m_Opcode.GetArgument();
uint32_t ulElseIndex, ulSkipIndex = 0;
ulElseIndex = BuildTreeBranch(NodeArray, j + 1, ulElseOffset); // true branch
if (NodeArray[ulElseIndex - 1].m_Opcode.GetOperator() == COpcode::O_JMP)
CNode& jumpNode = NodeArray[ulElseIndex - 1];
if (jumpNode.m_Opcode.GetOperator() == COpcode::O_JMP)
{
uint32_t ulSkipOffset = NodeArray[ulElseIndex - 1].m_Opcode.GetArgument();
uint32_t ulSkipOffset = jumpNode.m_Opcode.GetArgument();
if (ulSkipOffset > NodeArray[j].m_ulOffset)
{
ulSkipIndex = BuildTreeBranch(NodeArray, ulElseIndex, ulSkipOffset); // false branch
if (NodeArray[ulElseIndex - 2].IsExpression() && NodeArray[ulSkipIndex - 1].IsExpression())
if (ulElseIndex == j + 3 && ulSkipIndex == ulElseIndex + 1 &&
NodeArray[ulElseIndex - 2].IsExpression() && NodeArray[ulSkipIndex - 1].IsExpression())
{ // conditional expression
NodeArray[j].m_Type = CNode::TYPE_CONDITIONAL_EXPRESSION;
NodeArray[j].m_Arguments.erase(NodeArray[j].m_Arguments.begin() + 0); // address not needed anymore
NodeArray[j].m_Arguments.insert(NodeArray[j].m_Arguments.begin() + 0, NodeArray[ulElseIndex - 2]); // true expression
NodeArray[j].m_Arguments.insert(NodeArray[j].m_Arguments.begin() + 2, NodeArray[ulSkipIndex - 1]); // false expression
NodeArray.erase(NodeArray.begin() + j + 1, NodeArray.begin() + j + 1 + ulSkipIndex - j - 1);
arguments.insert(arguments.begin() + 2, NodeArray[ulElseIndex - 2]); // true expression
arguments.insert(arguments.begin() + 3, NodeArray[ulSkipIndex - 1]); // false expression
arguments.insert(arguments.begin() + 4, jumpNode); // jump node
NodeArray.erase(NodeArray.begin() + j + 1, NodeArray.begin() + ulSkipIndex);
continue;
}
}
}
j = ((ulSkipIndex != -1) ? ulSkipIndex : ulElseIndex) - 1; // skip already built
j = ((ulSkipIndex > 0) ? ulSkipIndex : ulElseIndex) - 1; // skip already built
}
}
@@ -644,7 +647,7 @@ void CFalloutScript::ExtractAndReduceCondition(CNodeArray& Source, CNodeArray& D
}
else
{
if (Destination[0].m_Opcode.GetAttributes().m_Type != COpcode::COpcodeAttributes::TYPE_EXPRESSION)
if (!Destination[0].IsExpression())
{
printf("Error: Invalid condition. Expression required\n");
throw std::exception();
@@ -787,9 +790,21 @@ void CFalloutScript::SetBordersOfBlocks(CNodeArray& NodeArray)
{
if (NodeArray[i].m_Type == CNode::TYPE_CONDITIONAL_EXPRESSION)
{
printf("Error: Conditional expression left in stack\n");
CNodeArray& args = NodeArray[i].m_Arguments;
// If both true and false expressions can be statements, we can safely decompile this
if (args[2].m_Opcode.GetAttributes().m_Type != COpcode::COpcodeAttributes::Type::TYPE_EXPRESSIONSTATEMENT ||
args[3].m_Opcode.GetAttributes().m_Type != COpcode::COpcodeAttributes::Type::TYPE_EXPRESSIONSTATEMENT)
{
printf("Error: Conditional expression left in stack.\n");
throw std::exception();
}
printf("Warning: Conditional expression left in stack. Restoring as IF.\n");
NodeArray[i].m_Type = CNode::TYPE_NORMAL;
NodeArray.insert(NodeArray.begin() + i + 1, args[2]); // true branch
NodeArray.insert(NodeArray.begin() + i + 2, args[4]); // jump
NodeArray.insert(NodeArray.begin() + i + 3, args[3]); // false branch
args.erase(args.begin() + 2, args.end());
}
CNode node = NodeArray[i].m_Arguments[0];
if (node.m_Opcode.GetOperator() != COpcode::O_INTOP)
+6 -4
View File
@@ -911,17 +911,19 @@ std::string CFalloutScript::GetSource(CNode& node, bool bLabel, uint32_t ulNumAr
}
if (node.m_Type == CNode::TYPE_CONDITIONAL_EXPRESSION)
{
if (node.m_Arguments.size() != 3)
if (node.m_Arguments.size() != 5)
{
printf("Error: Invalid number of arguments in conditional expression\n");
throw std::exception();
}
std::string sPostfix[] = { " if ", " else ", ""};
uint16_t argIdx[] = {2, 1, 3};
strResult = "";
for(uint32_t i = 0; i < node.m_Arguments.size(); i++)
for (uint32_t i = 0; i < 3; i++)
{
bool bParens = ArgNeedParens(node, node.m_Arguments[i], CFalloutScript::RIGHT_ASSOC);
strResult += (bParens ? "(" : "") + GetSource(node.m_Arguments[i], bLabel, ulNumArgs) + (bParens ? ")" : "") + sPostfix[i];
CNode& arg = node.m_Arguments[argIdx[i]];
bool bParens = ArgNeedParens(node, arg, CFalloutScript::RIGHT_ASSOC);
strResult += (bParens ? "(" : "") + GetSource(arg, bLabel, ulNumArgs) + (bParens ? ")" : "") + sPostfix[i];
}
break;
}
+3 -1
View File
@@ -130,7 +130,9 @@ uint32_t CNode::GetTopOffset()
bool CNode::IsExpression() const
{
return (m_Opcode.GetAttributes().m_Type == COpcode::COpcodeAttributes::TYPE_EXPRESSION
COpcode::COpcodeAttributes::Type type = m_Opcode.GetAttributes().m_Type;
return (type == COpcode::COpcodeAttributes::TYPE_EXPRESSION
|| type == COpcode::COpcodeAttributes::TYPE_EXPRESSIONSTATEMENT
|| m_Type == TYPE_CONDITIONAL_EXPRESSION);
}
+2 -1
View File
@@ -800,7 +800,8 @@ public:
public:
enum Type {
TYPE_STATEMENT,
TYPE_EXPRESSION
TYPE_EXPRESSION,
TYPE_EXPRESSIONSTATEMENT,
};
enum Category {
+2 -1
View File
@@ -79,6 +79,7 @@ uint32_t procArgs[] = {1, 2};
COpcode::CF2OpcodeAttributesMap::CF2OpcodeAttributesMap()
{
COpcode::COpcodeAttributes::Type expression = COpcode::COpcodeAttributes::TYPE_EXPRESSION;
COpcode::COpcodeAttributes::Type exprStatement = COpcode::COpcodeAttributes::TYPE_EXPRESSIONSTATEMENT;
COpcode::COpcodeAttributes::Category infix = COpcode::COpcodeAttributes::CATEGORY_INFIX;
SetAt(O_NOOP, COpcodeAttributes("O_NOOP", "/* O_NOOP */", 0));
@@ -339,7 +340,7 @@ COpcode::CF2OpcodeAttributesMap::CF2OpcodeAttributesMap()
SetAt(O_CRITTER_ATTEMPT_PLACEMENT, COpcodeAttributes("O_CRITTER_ATTEMPT_PLACEMENT", "critter_attempt_placement", 3, expression));
SetAt(O_OBJ_PID, COpcodeAttributes("O_OBJ_PID", "obj_pid", 1, expression));
SetAt(O_CUR_MAP_INDEX, COpcodeAttributes("O_CUR_MAP_INDEX", "cur_map_index", 0, expression));
SetAt(O_CRITTER_ADD_TRAIT, COpcodeAttributes("O_CRITTER_ADD_TRAIT", "critter_add_trait", 4, expression));
SetAt(O_CRITTER_ADD_TRAIT, COpcodeAttributes("O_CRITTER_ADD_TRAIT", "critter_add_trait", 4, exprStatement));
SetAt(O_CRITTER_RM_TRAIT, COpcodeAttributes("O_CRITTER_RM_TRAIT", "critter_rm_trait", 4, expression));
SetAt(O_PROTO_DATA, COpcodeAttributes("O_PROTO_DATA", "proto_data", 2, expression));
SetAt(O_MESSAGE_STR, COpcodeAttributes("O_MESSAGE_STR", "message_str", 2, expression));