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