diff --git a/FalloutScriptDecompile.cpp b/FalloutScriptDecompile.cpp index b19fb17..144270f 100644 --- a/FalloutScriptDecompile.cpp +++ b/FalloutScriptDecompile.cpp @@ -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,8 +790,20 @@ void CFalloutScript::SetBordersOfBlocks(CNodeArray& NodeArray) { if (NodeArray[i].m_Type == CNode::TYPE_CONDITIONAL_EXPRESSION) { - printf("Error: Conditional expression left in stack\n"); - throw std::exception(); + 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]; diff --git a/FalloutScriptStore.cpp b/FalloutScriptStore.cpp index 48635cb..41174a3 100644 --- a/FalloutScriptStore.cpp +++ b/FalloutScriptStore.cpp @@ -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; } diff --git a/Node.cpp b/Node.cpp index 5f3de75..8fadf2b 100644 --- a/Node.cpp +++ b/Node.cpp @@ -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); } diff --git a/Opcode.h b/Opcode.h index 2de00b2..57947da 100644 --- a/Opcode.h +++ b/Opcode.h @@ -800,7 +800,8 @@ public: public: enum Type { TYPE_STATEMENT, - TYPE_EXPRESSION + TYPE_EXPRESSION, + TYPE_EXPRESSIONSTATEMENT, }; enum Category { diff --git a/OpcodeAttributes.cpp b/OpcodeAttributes.cpp index 5615c32..303832b 100644 --- a/OpcodeAttributes.cpp +++ b/OpcodeAttributes.cpp @@ -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));