Initial commit
This commit is contained in:
328
engines/stark/tools/block.cpp
Normal file
328
engines/stark/tools/block.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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<const Block *> visited;
|
||||
return hasPredecessorIntern(visited, predecessor);
|
||||
}
|
||||
|
||||
bool Block::hasPredecessorIntern(Common::Array<const Block *> &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<const Block *> visited;
|
||||
return hasSuccessorIntern(visited, successor);
|
||||
}
|
||||
|
||||
bool Block::hasSuccessorIntern(Common::Array<const Block *> &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<const Block *> &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<const Block *> visited;
|
||||
return findMergePointIntern(visited, other);
|
||||
}
|
||||
|
||||
Block *Block::findMergePointIntern(Common::Array<const Block *> &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<const Block *> &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<const Block *> visited;
|
||||
return checkAllBranchesConvergeIntern(visited, junction);
|
||||
}
|
||||
|
||||
bool Block::checkAllBranchesConvergeIntern(Common::Array<const Block *> &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<const Block *> &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<CFGCommand *> Block::getLinearCommands() const {
|
||||
if (!hasControlStructure()) {
|
||||
return _commands;
|
||||
} else {
|
||||
Common::Array<CFGCommand *> 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
|
||||
Reference in New Issue
Block a user