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/command.h"
24 
25 #include "common/debug.h"
26 #include "common/tokenizer.h"
27 
28 namespace Stark {
29 namespace Tools {
30 
Command(Command * command)31 Command::Command(Command *command) {
32 	_index = command->_index;
33 	_subType = command->_subType;
34 	_subTypeDesc = command->_subTypeDesc;
35 	_arguments = command->_arguments;
36 }
37 
Command(Resources::Command * resource)38 Command::Command(Resources::Command *resource) {
39 	_index = resource->getIndex();
40 	_subType = (Resources::Command::SubType) resource->getSubType();
41 	_subTypeDesc = searchSubTypeDesc(_subType);
42 	_arguments = resource->getArguments();
43 }
44 
searchSubTypeDesc(Resources::Command::SubType subType)45 const Command::SubTypeDesc *Command::searchSubTypeDesc(Resources::Command::SubType subType) {
46 	static const SubTypeDesc typeNames[] = {
47 			{ Resources::Command::kCommandBegin,               "begin",                      kFlowNormal },
48 			{ Resources::Command::kCommandEnd,                 "end",                        kFlowEnd    },
49 			{ Resources::Command::kScriptCall,                 "scriptCall",                 kFlowNormal },
50 			{ Resources::Command::kDialogCall,                 "dialogCall",                 kFlowNormal },
51 			{ Resources::Command::kSetInteractiveMode,         "setInteractiveMode",         kFlowNormal },
52 			{ Resources::Command::kLocationGoTo,               "locationGoTo",               kFlowEnd    },
53 			{ Resources::Command::kWalkTo,                     "walkTo",                     kFlowBranch },
54 			{ Resources::Command::kGameLoop,                   "gameLoop",                   kFlowNormal },
55 			{ Resources::Command::kScriptPause,                "scriptPause",                kFlowNormal },
56 			{ Resources::Command::kScriptPauseRandom,          "scriptPauseRandom",          kFlowNormal },
57 			{ Resources::Command::kScriptPauseSkippable,       "scriptPauseSkippable",       kFlowNormal },
58 			{ Resources::Command::kScriptAbort,                "scriptAbort",                kFlowNormal },
59 			{ Resources::Command::kExit2DLocation,             "exit2DLocation",             kFlowEnd    },
60 			{ Resources::Command::kGoto2DLocation,             "goto2DLocation",             kFlowEnd    },
61 			{ Resources::Command::kRumbleScene,                "rumbleScene",                kFlowNormal },
62 			{ Resources::Command::kFadeScene,                  "fadeScene",                  kFlowNormal },
63 			{ Resources::Command::kSwayScene,                  "swayScene",                  kFlowNormal },
64 			{ Resources::Command::kLocationGoToNewCD,          "locationGoToNewCD",          kFlowEnd    },
65 			{ Resources::Command::kGameEnd,                    "gameEnd",                    kFlowNormal },
66 			{ Resources::Command::kInventoryOpen,              "inventoryOpen",              kFlowNormal },
67 			{ Resources::Command::kFloatScene,                 "floatScene",                 kFlowNormal },
68 			{ Resources::Command::kBookOfSecretsOpen,          "bookOfSecretsOpen",          kFlowNormal },
69 			{ Resources::Command::kDoNothing,                  "doNothing",                  kFlowNormal },
70 			{ Resources::Command::kItem3DPlaceOn,              "item3DPlaceOn",              kFlowNormal },
71 			{ Resources::Command::kItem3DWalkTo,               "item3DWalkTo",               kFlowNormal },
72 			{ Resources::Command::kItem3DFollowPath,           "item3DFollowPath",           kFlowNormal },
73 			{ Resources::Command::kItemLookAt,                 "itemLookAt",                 kFlowNormal },
74 			{ Resources::Command::kItem2DFollowPath,           "item2DFollowPath",           kFlowNormal },
75 			{ Resources::Command::kItemEnable,                 "itemEnable",                 kFlowNormal },
76 			{ Resources::Command::kItemSetActivity,            "itemSetActivity",            kFlowNormal },
77 			{ Resources::Command::kItemSelectInInventory,      "itemSelectInInventory",      kFlowNormal },
78 			{ Resources::Command::kUseAnimHierarchy,           "useAnimHierarchy",           kFlowNormal },
79 			{ Resources::Command::kPlayAnimation,              "playAnimation",              kFlowNormal },
80 			{ Resources::Command::kScriptEnable,               "scriptEnable",               kFlowNormal },
81 			{ Resources::Command::kShowPlay,                   "showPlay",                   kFlowNormal },
82 			{ Resources::Command::kKnowledgeSetBoolean,        "knowledgeSetBoolean",        kFlowNormal },
83 			{ Resources::Command::kKnowledgeSetInteger,        "knowledgeSetInteger",        kFlowNormal },
84 			{ Resources::Command::kKnowledgeAddInteger,        "knowledgeAddInteger",        kFlowNormal },
85 			{ Resources::Command::kEnableFloorField,           "enableFloorField",           kFlowNormal },
86 			{ Resources::Command::kPlayAnimScriptItem,         "playAnimScriptItem",         kFlowNormal },
87 			{ Resources::Command::kItemAnimFollowPath,         "itemAnimFollowPath",         kFlowNormal },
88 			{ Resources::Command::kKnowledgeAssignBool,        "knowledgeAssignBool",        kFlowNormal },
89 			{ Resources::Command::kKnowledgeAssignInteger,     "knowledgeAssignInteger",     kFlowNormal },
90 			{ Resources::Command::kLocationScrollTo,           "locationScrollTo",           kFlowNormal },
91 			{ Resources::Command::kSoundPlay,                  "soundPlay",                  kFlowNormal },
92 			{ Resources::Command::kKnowledgeSetIntRandom,      "knowledgeSetIntRandom",      kFlowNormal },
93 			{ Resources::Command::kKnowledgeSubValue,          "knowledgeSubValue",          kFlowNormal },
94 			{ Resources::Command::kItemLookDirection,          "itemLookDirection",          kFlowNormal },
95 			{ Resources::Command::kStopPlayingSound,           "stopPlayingSound",           kFlowNormal },
96 			{ Resources::Command::kLayerGoTo,                  "layerGoTo",                  kFlowNormal },
97 			{ Resources::Command::kLayerEnable,                "layerEnable",                kFlowNormal },
98 			{ Resources::Command::kLocationScrollSet,          "locationScrollSet",          kFlowNormal },
99 			{ Resources::Command::kFullMotionVideoPlay,        "fullMotionVideoPlay",        kFlowNormal },
100 			{ Resources::Command::kAnimSetFrame,               "animSetFrame",               kFlowNormal },
101 			{ Resources::Command::kKnowledgeAssignNegatedBool, "knowledgeAssignNegatedBool", kFlowNormal },
102 			{ Resources::Command::kDiaryEnableEntry,           "diaryEnableEntry",           kFlowNormal },
103 			{ Resources::Command::kPATChangeTooltip,           "pATChangeTooltip",           kFlowNormal },
104 			{ Resources::Command::kSoundChange,                "soundChange",                kFlowNormal },
105 			{ Resources::Command::kLightSetColor,              "lightSetColor",              kFlowNormal },
106 			{ Resources::Command::kLightFollowPath,            "lightFollowPath",            kFlowNormal },
107 			{ Resources::Command::kItem3DRunTo,                "item3DRunTo",                kFlowNormal },
108 			{ Resources::Command::kItemPlaceDirection,         "itemPlaceDirection",         kFlowNormal },
109 			{ Resources::Command::kItemRotateDirection,        "itemRotateDirection",        kFlowNormal },
110 			{ Resources::Command::kActivateTexture,            "activateTexture",            kFlowNormal },
111 			{ Resources::Command::kActivateMesh,               "activateMesh",               kFlowNormal },
112 			{ Resources::Command::kItem3DSetWalkTarget,        "item3DSetWalkTarget",        kFlowNormal },
113 			{ Resources::Command::kSpeakWithoutTalking,        "speakWithoutTalking",        kFlowNormal },
114 			{ Resources::Command::kIsOnFloorField,             "isOnFloorField",             kFlowBranch },
115 			{ Resources::Command::kIsItemEnabled,              "isItemEnabled",              kFlowBranch },
116 			{ Resources::Command::kIsScriptEnabled,            "isScriptEnabled",            kFlowBranch },
117 			{ Resources::Command::kIsKnowledgeBooleanSet,      "isKnowledgeBooleanSet",      kFlowBranch },
118 			{ Resources::Command::kIsKnowledgeIntegerInRange,  "isKnowledgeIntegerInRange",  kFlowBranch },
119 			{ Resources::Command::kIsKnowledgeIntegerAbove,    "isKnowledgeIntegerAbove",    kFlowBranch },
120 			{ Resources::Command::kIsKnowledgeIntegerEqual,    "isKnowledgeIntegerEqual",    kFlowBranch },
121 			{ Resources::Command::kIsKnowledgeIntegerLower,    "isKnowledgeIntegerLower",    kFlowBranch },
122 			{ Resources::Command::kIsScriptActive,             "isScriptActive",             kFlowBranch },
123 			{ Resources::Command::kIsRandom,                   "isRandom",                   kFlowBranch },
124 			{ Resources::Command::kIsAnimScriptItemReached,    "isAnimScriptItemReached",    kFlowBranch },
125 			{ Resources::Command::kIsItemOnPlace,              "isItemOnPlace",              kFlowBranch },
126 			{ Resources::Command::kIsAnimPlaying,              "isAnimPlaying",              kFlowBranch },
127 			{ Resources::Command::kIsItemActivity,             "isItemActivity",             kFlowBranch },
128 			{ Resources::Command::kIsItemNearPlace,            "isItemNearPlace",            kFlowBranch },
129 			{ Resources::Command::kIsAnimAtTime,               "isAnimAtTime",               kFlowBranch },
130 			{ Resources::Command::kIsLocation2D,               "isLocation2D",               kFlowBranch },
131 			{ Resources::Command::kIsInventoryOpen,            "isInventoryOpen",            kFlowBranch }
132 	};
133 
134 	for (uint i = 0; i < ARRAYSIZE(typeNames); i++) {
135 		if (typeNames[i].subType == subType) {
136 			return &typeNames[i];
137 		}
138 	}
139 
140 	return nullptr;
141 }
142 
getEffectiveArguments() const143 Command::ArgumentArray Command::getEffectiveArguments() const {
144 	uint effectiveArgumentsStart;
145 	switch (_subTypeDesc->controlFlowType) {
146 		case kFlowEnd:
147 			effectiveArgumentsStart = 0;
148 			break;
149 		case kFlowNormal:
150 			effectiveArgumentsStart = 1;
151 			break;
152 		case kFlowBranch:
153 			effectiveArgumentsStart = 2;
154 			break;
155 		default:
156 			error("Unhandled control flow type '%d'", _subTypeDesc->controlFlowType);
157 	}
158 
159 	ArgumentArray effectiveArguments;
160 	for (uint i = effectiveArgumentsStart; i < _arguments.size(); i++) {
161 		effectiveArguments.push_back(_arguments[i]);
162 	}
163 
164 	return  effectiveArguments;
165 }
166 
describeArguments(DefinitionRegistry * definitions) const167 Common::String Command::describeArguments(DefinitionRegistry *definitions) const {
168 	Common::String desc;
169 
170 	for (uint i = 0; i < _arguments.size(); i++) {
171 		switch (_arguments[i].type) {
172 			case Resources::Command::Argument::kTypeInteger1:
173 			case Resources::Command::Argument::kTypeInteger2:
174 				desc += Common::String::format("%d", _arguments[i].intValue);
175 				break;
176 
177 			case Resources::Command::Argument::kTypeResourceReference: {
178 				if (definitions) {
179 					desc += definitions->getFromReference(_arguments[i].referenceValue);
180 				} else {
181 					desc += _arguments[i].referenceValue.describe();
182 				}
183 			}
184 				break;
185 			case Resources::Command::Argument::kTypeString:
186 				desc += _arguments[i].stringValue;
187 				break;
188 			default:
189 				error("Unknown argument type %d", _arguments[i].type);
190 		}
191 
192 		if (i != _arguments.size() - 1) {
193 			desc += ", ";
194 		}
195 	}
196 
197 	return desc;
198 }
199 
printCall() const200 void Command::printCall() const {
201 	debug("%d: %s(%s)", _index, _subTypeDesc->name, describeArguments(nullptr).c_str());
202 }
203 
getIndex() const204 uint16 Command::getIndex() const {
205 	return _index;
206 }
207 
hasSubtypeDescription() const208 bool Command::hasSubtypeDescription() const {
209 	return _subTypeDesc != nullptr;
210 }
211 
getSubType() const212 Resources::Command::SubType Command::getSubType() const {
213 	return _subType;
214 }
215 
CFGCommand(Resources::Command * resource)216 CFGCommand::CFGCommand(Resources::Command *resource) :
217 		Command(resource),
218 		_followerIndex(-1),
219 		_trueBranchIndex(-1),
220 		_falseBranchIndex(-1),
221 		_follower(nullptr),
222 		_trueBranch(nullptr),
223 		_falseBranch(nullptr),
224 		_block(nullptr) {
225 	if (_subTypeDesc) {
226 		initBranches();
227 	}
228 }
229 
initBranches()230 void CFGCommand::initBranches() {
231 	switch (_subTypeDesc->controlFlowType) {
232 		case kFlowNormal:
233 			_followerIndex = _arguments[0].intValue;
234 			break;
235 		case kFlowBranch:
236 			if (_arguments[0].intValue == _arguments[1].intValue) {
237 				// Degenerate conditions are handled here so that blocks are not split after them
238 				_followerIndex = _arguments[0].intValue;
239 			} else {
240 				_falseBranchIndex = _arguments[0].intValue;
241 				_trueBranchIndex = _arguments[1].intValue;
242 			}
243 			break;
244 		case kFlowEnd:
245 			// No followers
246 			break;
247 	}
248 }
249 
isEntryPoint() const250 bool CFGCommand::isEntryPoint() const {
251 	return _subType == Resources::Command::kCommandBegin;
252 }
253 
isBranch() const254 bool CFGCommand::isBranch() const {
255 	return _trueBranchIndex >= 0 && _falseBranchIndex >= 0;
256 }
257 
isBranchTarget() const258 bool CFGCommand::isBranchTarget() const {
259 	return _predecessors.size() > 1;
260 }
261 
getBlock() const262 Block *CFGCommand::getBlock() const {
263 	return _block;
264 }
265 
setBlock(Block * block)266 void CFGCommand::setBlock(Block *block) {
267 	_block = block;
268 }
269 
getFollower() const270 CFGCommand *CFGCommand::getFollower() const {
271 	return _follower;
272 }
273 
getTrueBranch() const274 CFGCommand *CFGCommand::getTrueBranch() const {
275 	return _trueBranch;
276 }
277 
getFalseBranch() const278 CFGCommand *CFGCommand::getFalseBranch() const {
279 	return _falseBranch;
280 }
281 
linkBranches(const Common::Array<CFGCommand * > & commands)282 void CFGCommand::linkBranches(const Common::Array<CFGCommand *> &commands) {
283 	if (_followerIndex >= 0) {
284 		_follower = findCommandWithIndex(commands, _followerIndex);
285 		_follower->_predecessors.push_back(this);
286 	}
287 
288 	if (_falseBranchIndex >= 0) {
289 		_falseBranch = findCommandWithIndex(commands, _falseBranchIndex);
290 		_falseBranch->_predecessors.push_back(this);
291 	}
292 
293 	if (_trueBranchIndex >= 0) {
294 		_trueBranch = findCommandWithIndex(commands, _trueBranchIndex);
295 		_trueBranch->_predecessors.push_back(this);
296 	}
297 }
298 
findCommandWithIndex(const Common::Array<CFGCommand * > & commands,int32 index)299 CFGCommand *CFGCommand::findCommandWithIndex(const Common::Array<CFGCommand *> &commands, int32 index) {
300 	for (uint i = 0; i < commands.size(); i++) {
301 		CFGCommand *command = commands[i];
302 
303 		if (command->_index == index) {
304 			return command;
305 		}
306 	}
307 
308 	error("Unable to find command with index %d", index);
309 }
310 
registerReference(const ResourceReference & reference)311 void DefinitionRegistry::registerReference(const ResourceReference &reference) {
312 	if (!reference.canResolve()) {
313 		// The reference uses archives that are not currently loaded
314 		return;
315 	}
316 
317 	Resources::Object *object = reference.resolve<Resources::Object>();
318 	if (!_definitions.contains(object)) {
319 		// TODO: There is no guarantee the definition is unique
320 		_definitions[object] = object->getType().getName() + stringToCamelCase(object->getName());
321 	}
322 }
323 
getFromReference(const ResourceReference & reference) const324 Common::String DefinitionRegistry::getFromReference(const ResourceReference &reference) const {
325 	if (!reference.canResolve()) {
326 		// The reference uses archives that are not currently loaded
327 		return reference.describe();
328 	}
329 
330 	Resources::Object *object = reference.resolve<Resources::Object>();
331 
332 	if (_definitions.contains(object)) {
333 		return _definitions.getVal(object);
334 	} else {
335 		return reference.describe();
336 	}
337 }
338 
stringToCamelCase(const Common::String & input)339 Common::String DefinitionRegistry::stringToCamelCase(const Common::String &input) {
340 	Common::String clean = input;
341 
342 	// First replace all non alphanumerical characters with spaces
343 	for (uint i = 0; i < clean.size(); i++) {
344 		if (!Common::isAlnum(clean[i])) {
345 			clean.setChar(' ', i);
346 		}
347 	}
348 
349 	// Then turn the string into camel case
350 	Common::String output;
351 	Common::StringTokenizer tokens = Common::StringTokenizer(clean);
352 	while (!tokens.empty()) {
353 		Common::String token = tokens.nextToken();
354 
355 		char upperFirstLetter = toupper(token[0]);
356 		token.setChar(upperFirstLetter, 0);
357 
358 		output += token;
359 	}
360 
361 	return output;
362 }
363 
printAll() const364 void DefinitionRegistry::printAll() const {
365 	DefinitionMap::const_iterator it = _definitions.begin();
366 	while (it != _definitions.end()) {
367 		ResourceReference reference;
368 		reference.buildFromResource(it->_key);
369 
370 		debug("let %s = %s", it->_value.c_str(), reference.describe().c_str());
371 
372 		it++;
373 	}
374 }
375 
376 } // End of namespace Tools
377 } // End of namespace Stark
378