/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "engines/stark/tools/block.h" #include "engines/stark/tools/command.h" #include "common/debug.h" namespace Stark { namespace Tools { Block::Block() : _follower(nullptr), _trueBranch(nullptr), _falseBranch(nullptr), _controlStructure(nullptr), _infiniteLoopStart(false) { } void Block::appendCommand(CFGCommand *command) { _commands.push_back(command); command->setBlock(this); } bool Block::isEmpty() const { return _commands.empty(); } void Block::print() const { for (uint i = 0; i < _commands.size(); i++) { _commands[i]->printCall(); } if (_controlStructure) { switch (_controlStructure->type) { case ControlStructure::kTypeIf: debug("if%s: %d else: %d next: %d", _controlStructure->invertedCondition ? " not" : "", _controlStructure->thenHead->getFirstCommandIndex(), _controlStructure->elseHead ? _controlStructure->elseHead->getFirstCommandIndex() : -1, _controlStructure->next ? _controlStructure->next->getFirstCommandIndex() : -1); break; case ControlStructure::kTypeWhile: debug("while%s: %d next: %d", _controlStructure->invertedCondition ? " not" : "", _controlStructure->loopHead->getFirstCommandIndex(), _controlStructure->next->getFirstCommandIndex()); break; } } if (_infiniteLoopStart) { debug("infinite loop start: %d", getFirstCommandIndex()); } if (isCondition() && !_controlStructure) { debug("unrecognized control flow"); } } void Block::setBranches(Block *trueBranch, Block *falseBranch) { _trueBranch = trueBranch; _falseBranch = falseBranch; trueBranch->addPredecessor(this); falseBranch->addPredecessor(this); } void Block::setFollower(Block *follower) { _follower = follower; follower->addPredecessor(this); } void Block::addPredecessor(Block *predecessor) { _predecessors.push_back(predecessor); } bool Block::isCondition() const { return _trueBranch != nullptr && _falseBranch != nullptr; } bool Block::hasPredecessor(Block *predecessor) const { Common::Array visited; return hasPredecessorIntern(visited, predecessor); } bool Block::hasPredecessorIntern(Common::Array &visited, Block *predecessor) const { visited.push_back(this); if (isInfiniteLoopStart()) { // Don't follow infinite loops return false; } for (uint i = 0; i < _predecessors.size(); i++) { if (_predecessors[i] == predecessor) { return true; } bool alreadyVisited = Common::find(visited.begin(), visited.end(), _predecessors[i]) != visited.end(); if (!alreadyVisited && _predecessors[i]->hasPredecessorIntern(visited, predecessor)) { return true; } } return false; } bool Block::hasSuccessor(Block *successor) const { Common::Array visited; return hasSuccessorIntern(visited, successor); } bool Block::hasSuccessorIntern(Common::Array &visited, Block *successor) const { visited.push_back(this); if (this == successor) { return true; } bool followerHasSuccessor = hasChildSuccessorIntern(visited, _follower, successor); bool trueBranchHasSuccessor = hasChildSuccessorIntern(visited, _trueBranch, successor); bool falseBranchHasSuccessor = hasChildSuccessorIntern(visited, _falseBranch, successor); return followerHasSuccessor || trueBranchHasSuccessor || falseBranchHasSuccessor; } bool Block::hasChildSuccessorIntern(Common::Array &visited, Block *child, Block *successor) const { if (!child) { return false; } bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end(); return !alreadyVisited && child->hasSuccessorIntern(visited, successor); } Block *Block::findMergePoint(Block *other) { // TODO: Find the closest merge point? How to define that notion? Common::Array visited; return findMergePointIntern(visited, other); } Block *Block::findMergePointIntern(Common::Array &visited, Block *other) { visited.push_back(this); if (other == this) { return this; } if (hasPredecessor(other)) { return this; } Block *mergePoint = findChildMergePoint(visited, _follower, other); if (mergePoint) { return mergePoint; } mergePoint = findChildMergePoint(visited, _trueBranch, other); if (mergePoint) { return mergePoint; } mergePoint = findChildMergePoint(visited, _falseBranch, other); if (mergePoint) { return mergePoint; } return nullptr; } Block *Block::findChildMergePoint(Common::Array &visited, Block *child, Block *other) const { if (child) { bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end(); if (!alreadyVisited) { return child->findMergePointIntern(visited, other); } } return nullptr; } bool Block::checkAllBranchesConverge(Block *junction) const { // Check if there is at least one path to the junction node if (!hasSuccessor(junction)) { return false; } // Check all the successors go to the junction point or loop infinitely Common::Array visited; return checkAllBranchesConvergeIntern(visited, junction); } bool Block::checkAllBranchesConvergeIntern(Common::Array &visited, Block *junction) const { visited.push_back(this); if (this == junction) { return true; } if (!_follower && !_trueBranch && !_falseBranch) { return false; } if (isInfiniteLoopStart()) { // Don't follow infinite loops return false; } bool followerConverges = checkChildConvergeIntern(visited, _follower, junction); bool trueBranchConverges = checkChildConvergeIntern(visited, _trueBranch, junction); bool falseBranchConverges = checkChildConvergeIntern(visited, _falseBranch, junction); return followerConverges && trueBranchConverges && falseBranchConverges; } bool Block::checkChildConvergeIntern(Common::Array &visited, Block *child, Block *junction) const { if (child) { bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end(); if (!alreadyVisited) { return child->checkAllBranchesConvergeIntern(visited, junction); } } return true; } Block *Block::getFollower() const { return _follower; } Block *Block::getTrueBranch() const { return _trueBranch; } Block *Block::getFalseBranch() const { return _falseBranch; } uint16 Block::getFirstCommandIndex() const { return _commands[0]->getIndex(); } bool Block::hasControlStructure() const { return _controlStructure != nullptr; } void Block::setControlStructure(ControlStructure *controlStructure) { _controlStructure = controlStructure; } ControlStructure *Block::getControlStructure() const { return _controlStructure; } void Block::setInfiniteLoopStart(bool infiniteLoopStart) { _infiniteLoopStart = infiniteLoopStart; } bool Block::isInfiniteLoopStart() const { return _infiniteLoopStart; } Common::Array Block::getLinearCommands() const { if (!hasControlStructure()) { return _commands; } else { Common::Array commands; // The last command in the block is the condition. // Exclude it from the linear commands. for (uint i = 0; i < _commands.size() - 1; i ++) { commands.push_back(_commands[i]); } return commands; } } CFGCommand *Block::getConditionCommand() const { if (hasControlStructure()) { // The condition command is always the last one return _commands.back(); } else { return nullptr; } } bool Block::allowDuplication() const { // Allow simple termination blocks to be duplicated in the decompiled output bool isScriptEnd = !_follower && !_trueBranch && !_falseBranch; bool isShort = _commands.size() < 5; return isScriptEnd && isShort; } ControlStructure::ControlStructure(ControlStructureType t) : type(t), condition(nullptr), invertedCondition(false), loopHead(nullptr), thenHead(nullptr), elseHead(nullptr), next(nullptr) { } } // End of namespace Tools } // End of namespace Stark