1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "engines/stark/tools/block.h"
24 
25 #include "engines/stark/tools/command.h"
26 
27 #include "common/debug.h"
28 
29 namespace Stark {
30 namespace Tools {
31 
Block()32 Block::Block() :
33 		_follower(nullptr),
34 		_trueBranch(nullptr),
35 		_falseBranch(nullptr),
36 		_controlStructure(nullptr),
37 		_infiniteLoopStart(false) {
38 
39 }
40 
appendCommand(CFGCommand * command)41 void Block::appendCommand(CFGCommand *command) {
42 	_commands.push_back(command);
43 	command->setBlock(this);
44 }
45 
isEmpty() const46 bool Block::isEmpty() const {
47 	return _commands.empty();
48 }
49 
print() const50 void Block::print() const {
51 	for (uint i = 0; i < _commands.size(); i++) {
52 		_commands[i]->printCall();
53 	}
54 
55 	if (_controlStructure) {
56 		switch (_controlStructure->type) {
57 			case ControlStructure::kTypeIf:
58 				debug("if%s: %d else: %d next: %d",
59 				      _controlStructure->invertedCondition ? " not" : "",
60 				      _controlStructure->thenHead->getFirstCommandIndex(),
61 				      _controlStructure->elseHead ? _controlStructure->elseHead->getFirstCommandIndex() : -1,
62 				      _controlStructure->next ? _controlStructure->next->getFirstCommandIndex() : -1);
63 				break;
64 			case ControlStructure::kTypeWhile:
65 				debug("while%s: %d next: %d",
66 				      _controlStructure->invertedCondition ? " not" : "",
67 				      _controlStructure->loopHead->getFirstCommandIndex(),
68 				      _controlStructure->next->getFirstCommandIndex());
69 				break;
70 		}
71 	}
72 
73 	if (_infiniteLoopStart) {
74 		debug("infinite loop start: %d", getFirstCommandIndex());
75 	}
76 
77 	if (isCondition() && !_controlStructure) {
78 		debug("unrecognized control flow");
79 	}
80 }
81 
setBranches(Block * trueBranch,Block * falseBranch)82 void Block::setBranches(Block *trueBranch, Block *falseBranch) {
83 	_trueBranch = trueBranch;
84 	_falseBranch = falseBranch;
85 
86 	trueBranch->addPredecessor(this);
87 	falseBranch->addPredecessor(this);
88 }
89 
setFollower(Block * follower)90 void Block::setFollower(Block *follower) {
91 	_follower = follower;
92 	follower->addPredecessor(this);
93 }
94 
addPredecessor(Block * predecessor)95 void Block::addPredecessor(Block *predecessor) {
96 	_predecessors.push_back(predecessor);
97 }
98 
isCondition() const99 bool Block::isCondition() const {
100 	return _trueBranch != nullptr && _falseBranch != nullptr;
101 }
102 
hasPredecessor(Block * predecessor) const103 bool Block::hasPredecessor(Block *predecessor) const {
104 	Common::Array<const Block *> visited;
105 	return hasPredecessorIntern(visited, predecessor);
106 }
107 
hasPredecessorIntern(Common::Array<const Block * > & visited,Block * predecessor) const108 bool Block::hasPredecessorIntern(Common::Array<const Block *> &visited, Block *predecessor) const {
109 	visited.push_back(this);
110 
111 	if (isInfiniteLoopStart()) {
112 		// Don't follow infinite loops
113 		return false;
114 	}
115 
116 	for (uint i = 0; i < _predecessors.size(); i++) {
117 		if (_predecessors[i] == predecessor) {
118 			return true;
119 		}
120 
121 		bool alreadyVisited = Common::find(visited.begin(), visited.end(), _predecessors[i]) != visited.end();
122 		if (!alreadyVisited && _predecessors[i]->hasPredecessorIntern(visited, predecessor)) {
123 			return true;
124 		}
125 	}
126 
127 	return false;
128 }
129 
hasSuccessor(Block * successor) const130 bool Block::hasSuccessor(Block *successor) const {
131 	Common::Array<const Block *> visited;
132 	return hasSuccessorIntern(visited, successor);
133 }
134 
hasSuccessorIntern(Common::Array<const Block * > & visited,Block * successor) const135 bool Block::hasSuccessorIntern(Common::Array<const Block *> &visited, Block *successor) const {
136 	visited.push_back(this);
137 
138 	if (this == successor) {
139 		return true;
140 	}
141 
142 	bool followerHasSuccessor = hasChildSuccessorIntern(visited, _follower, successor);
143 	bool trueBranchHasSuccessor = hasChildSuccessorIntern(visited, _trueBranch, successor);
144 	bool falseBranchHasSuccessor = hasChildSuccessorIntern(visited, _falseBranch, successor);
145 
146 	return followerHasSuccessor || trueBranchHasSuccessor || falseBranchHasSuccessor;
147 }
148 
hasChildSuccessorIntern(Common::Array<const Block * > & visited,Block * child,Block * successor) const149 bool Block::hasChildSuccessorIntern(Common::Array<const Block *> &visited, Block *child, Block *successor) const {
150 	if (!child) {
151 		return false;
152 	}
153 
154 	bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end();
155 	return !alreadyVisited && child->hasSuccessorIntern(visited, successor);
156 }
157 
findMergePoint(Block * other)158 Block *Block::findMergePoint(Block *other) {
159 	// TODO: Find the closest merge point? How to define that notion?
160 	Common::Array<const Block *> visited;
161 	return findMergePointIntern(visited, other);
162 }
163 
findMergePointIntern(Common::Array<const Block * > & visited,Block * other)164 Block *Block::findMergePointIntern(Common::Array<const Block *> &visited, Block *other) {
165 	visited.push_back(this);
166 
167 	if (other == this) {
168 		return this;
169 	}
170 
171 	if (hasPredecessor(other)) {
172 		return this;
173 	}
174 
175 	Block *mergePoint = findChildMergePoint(visited, _follower, other);
176 	if (mergePoint) {
177 		return mergePoint;
178 	}
179 
180 	mergePoint = findChildMergePoint(visited, _trueBranch, other);
181 	if (mergePoint) {
182 		return mergePoint;
183 	}
184 
185 	mergePoint = findChildMergePoint(visited, _falseBranch, other);
186 	if (mergePoint) {
187 		return mergePoint;
188 	}
189 
190 	return nullptr;
191 }
192 
findChildMergePoint(Common::Array<const Block * > & visited,Block * child,Block * other) const193 Block *Block::findChildMergePoint(Common::Array<const Block *> &visited, Block *child, Block *other) const {
194 	if (child) {
195 		bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end();
196 		if (!alreadyVisited) {
197 			return child->findMergePointIntern(visited, other);
198 		}
199 	}
200 
201 	return nullptr;
202 }
203 
checkAllBranchesConverge(Block * junction) const204 bool Block::checkAllBranchesConverge(Block *junction) const {
205 	// Check if there is at least one path to the junction node
206 	if (!hasSuccessor(junction)) {
207 		return false;
208 	}
209 
210 	// Check all the successors go to the junction point or loop infinitely
211 	Common::Array<const Block *> visited;
212 	return checkAllBranchesConvergeIntern(visited, junction);
213 }
214 
checkAllBranchesConvergeIntern(Common::Array<const Block * > & visited,Block * junction) const215 bool Block::checkAllBranchesConvergeIntern(Common::Array<const Block *> &visited, Block *junction) const {
216 	visited.push_back(this);
217 
218 	if (this == junction) {
219 		return true;
220 	}
221 
222 	if (!_follower && !_trueBranch && !_falseBranch) {
223 		return false;
224 	}
225 
226 	if (isInfiniteLoopStart()) {
227 		// Don't follow infinite loops
228 		return false;
229 	}
230 
231 	bool followerConverges = checkChildConvergeIntern(visited, _follower, junction);
232 	bool trueBranchConverges = checkChildConvergeIntern(visited, _trueBranch, junction);
233 	bool falseBranchConverges = checkChildConvergeIntern(visited, _falseBranch, junction);
234 
235 	return followerConverges && trueBranchConverges && falseBranchConverges;
236 }
237 
checkChildConvergeIntern(Common::Array<const Block * > & visited,Block * child,Block * junction) const238 bool Block::checkChildConvergeIntern(Common::Array<const Block *> &visited, Block *child, Block *junction) const {
239 	if (child) {
240 		bool alreadyVisited = Common::find(visited.begin(), visited.end(), child) != visited.end();
241 		if (!alreadyVisited) {
242 			return child->checkAllBranchesConvergeIntern(visited, junction);
243 		}
244 	}
245 
246 	return true;
247 }
248 
getFollower() const249 Block *Block::getFollower() const {
250 	return _follower;
251 }
252 
getTrueBranch() const253 Block *Block::getTrueBranch() const {
254 	return _trueBranch;
255 }
256 
getFalseBranch() const257 Block *Block::getFalseBranch() const {
258 	return _falseBranch;
259 }
260 
getFirstCommandIndex() const261 uint16 Block::getFirstCommandIndex() const {
262 	return _commands[0]->getIndex();
263 }
264 
hasControlStructure() const265 bool Block::hasControlStructure() const {
266 	return _controlStructure != nullptr;
267 }
268 
setControlStructure(ControlStructure * controlStructure)269 void Block::setControlStructure(ControlStructure *controlStructure) {
270 	_controlStructure = controlStructure;
271 }
272 
getControlStructure() const273 ControlStructure *Block::getControlStructure() const {
274 	return _controlStructure;
275 }
276 
setInfiniteLoopStart(bool infiniteLoopStart)277 void Block::setInfiniteLoopStart(bool infiniteLoopStart) {
278 	_infiniteLoopStart = infiniteLoopStart;
279 }
280 
isInfiniteLoopStart() const281 bool Block::isInfiniteLoopStart() const {
282 	return _infiniteLoopStart;
283 }
284 
getLinearCommands() const285 Common::Array<CFGCommand *> Block::getLinearCommands() const {
286 	if (!hasControlStructure()) {
287 		return _commands;
288 	} else {
289 		Common::Array<CFGCommand *> commands;
290 
291 		// The last command in the block is the condition.
292 		// Exclude it from the linear commands.
293 		for (uint i = 0; i < _commands.size() - 1; i ++) {
294 			commands.push_back(_commands[i]);
295 		}
296 
297 		return commands;
298 	}
299 }
300 
getConditionCommand() const301 CFGCommand *Block::getConditionCommand() const {
302 	if (hasControlStructure()) {
303 		// The condition command is always the last one
304 		return _commands.back();
305 	} else {
306 		return nullptr;
307 	}
308 }
309 
allowDuplication() const310 bool Block::allowDuplication() const {
311 	// Allow simple termination blocks to be duplicated in the decompiled output
312 	bool isScriptEnd = !_follower && !_trueBranch && !_falseBranch;
313 	bool isShort = _commands.size() < 5;
314 
315 	return isScriptEnd && isShort;
316 }
317 
ControlStructure(ControlStructureType t)318 ControlStructure::ControlStructure(ControlStructureType t) :
319 		type(t),
320 		condition(nullptr),
321 		invertedCondition(false),
322 		loopHead(nullptr),
323 		thenHead(nullptr),
324 		elseHead(nullptr),
325 		next(nullptr) {
326 }
327 
328 } // End of namespace Tools
329 } // End of namespace Stark
330