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/myst3/console.h"
24 
25 #include "engines/myst3/archive.h"
26 #include "engines/myst3/database.h"
27 #include "engines/myst3/effects.h"
28 #include "engines/myst3/inventory.h"
29 #include "engines/myst3/script.h"
30 #include "engines/myst3/state.h"
31 
32 namespace Myst3 {
33 
Console(Myst3Engine * vm)34 Console::Console(Myst3Engine *vm) : GUI::Debugger(), _vm(vm) {
35 	registerCmd("infos",				WRAP_METHOD(Console, Cmd_Infos));
36 	registerCmd("lookAt",				WRAP_METHOD(Console, Cmd_LookAt));
37 	registerCmd("initScript",			WRAP_METHOD(Console, Cmd_InitScript));
38 	registerCmd("var",				WRAP_METHOD(Console, Cmd_Var));
39 	registerCmd("listNodes",			WRAP_METHOD(Console, Cmd_ListNodes));
40 	registerCmd("run",				WRAP_METHOD(Console, Cmd_Run));
41 	registerCmd("runOp",				WRAP_METHOD(Console, Cmd_RunOp));
42 	registerCmd("go",				WRAP_METHOD(Console, Cmd_Go));
43 	registerCmd("extract",				WRAP_METHOD(Console, Cmd_Extract));
44 	registerCmd("fillInventory",			WRAP_METHOD(Console, Cmd_FillInventory));
45 	registerCmd("dumpArchive",			WRAP_METHOD(Console, Cmd_DumpArchive));
46 	registerCmd("dumpMasks",			WRAP_METHOD(Console, Cmd_DumpMasks));
47 }
48 
~Console()49 Console::~Console() {
50 }
51 
describeScript(const Common::Array<Opcode> & script)52 void Console::describeScript(const Common::Array<Opcode> &script) {
53 	for (uint j = 0; j < script.size(); j++) {
54 		debugPrintf("%s", _vm->_scriptEngine->describeOpcode(script[j]).c_str());
55 	}
56 }
57 
Cmd_Infos(int argc,const char ** argv)58 bool Console::Cmd_Infos(int argc, const char **argv) {
59 	uint16 nodeId = _vm->_state->getLocationNode();
60 	uint32 roomId = _vm->_state->getLocationRoom();
61 	uint32 ageID = _vm->_state->getLocationAge();
62 
63 	if (argc >= 2) {
64 		nodeId = atoi(argv[1]);
65 	}
66 
67 	if (argc >= 3) {
68 		RoomKey roomKey = _vm->_db->getRoomKey(argv[2]);
69 		if (roomKey.roomID == 0 || roomKey.ageID == 0) {
70 			debugPrintf("Unknown room name %s\n", argv[2]);
71 			return true;
72 		}
73 
74 		roomId = roomKey.roomID;
75 		ageID = roomKey.ageID;
76 	}
77 
78 	NodePtr nodeData = _vm->_db->getNodeData(nodeId, roomId, ageID);
79 
80 	if (!nodeData) {
81 		debugPrintf("No node with id %d\n", nodeId);
82 		return true;
83 	}
84 
85 	Common::String roomName = _vm->_db->getRoomName(roomId, ageID);
86 
87 	debugPrintf("node: %s %d    ", roomName.c_str(), nodeId);
88 
89 	for (uint i = 0; i < nodeData->scripts.size(); i++) {
90 		debugPrintf("\ninit %d > %s (%s)\n", i,
91 				_vm->_state->describeCondition(nodeData->scripts[i].condition).c_str(),
92 				_vm->_state->evaluate(nodeData->scripts[i].condition) ? "true" : "false");
93 
94 		describeScript(nodeData->scripts[i].script);
95 	}
96 
97 	for (uint i = 0; i < nodeData->hotspots.size(); i++) {
98 		debugPrintf("\nhotspot %d > %s (%s)\n", i,
99 				_vm->_state->describeCondition(nodeData->hotspots[i].condition).c_str(),
100 				_vm->_state->evaluate(nodeData->hotspots[i].condition) ? "true" : "false");
101 
102 		for(uint j = 0; j < nodeData->hotspots[i].rects.size(); j++) {
103 			PolarRect &rect = nodeData->hotspots[i].rects[j];
104 
105 			debugPrintf("    rect > pitch: %d heading: %d width: %d height: %d\n",
106 					rect.centerPitch, rect.centerHeading, rect.width, rect.height);
107 		}
108 
109 		describeScript(nodeData->hotspots[i].script);
110 	}
111 
112 	for (uint i = 0; i < nodeData->soundScripts.size(); i++) {
113 		debugPrintf("\nsound %d > %s (%s)\n", i,
114 				_vm->_state->describeCondition(nodeData->soundScripts[i].condition).c_str(),
115 				_vm->_state->evaluate(nodeData->soundScripts[i].condition) ? "true" : "false");
116 
117 		describeScript(nodeData->soundScripts[i].script);
118 	}
119 
120 	for (uint i = 0; i < nodeData->backgroundSoundScripts.size(); i++) {
121 		debugPrintf("\nbackground sound %d > %s (%s)\n", i,
122 				_vm->_state->describeCondition(nodeData->backgroundSoundScripts[i].condition).c_str(),
123 				_vm->_state->evaluate(nodeData->backgroundSoundScripts[i].condition) ? "true" : "false");
124 
125 		describeScript(nodeData->backgroundSoundScripts[i].script);
126 	}
127 
128 	return true;
129 }
130 
Cmd_LookAt(int argc,const char ** argv)131 bool Console::Cmd_LookAt(int argc, const char **argv) {
132 	if (argc != 1 && argc != 3) {
133 		debugPrintf("Usage :\n");
134 		debugPrintf("lookAt pitch heading\n");
135 		return true;
136 	}
137 
138 	float pitch = _vm->_state->getLookAtPitch();
139 	float heading = _vm->_state->getLookAtHeading();
140 
141 	debugPrintf("pitch: %d heading: %d\n", (int)pitch, (int)heading);
142 
143 	if (argc >= 3){
144 		_vm->_state->lookAt(atof(argv[1]), atof(argv[2]));
145 		return false;
146 	}
147 
148 	return true;
149 }
150 
Cmd_InitScript(int argc,const char ** argv)151 bool Console::Cmd_InitScript(int argc, const char **argv) {
152 	describeScript(_vm->_db->getNodeInitScript());
153 
154 	return true;
155 }
156 
157 
Cmd_Var(int argc,const char ** argv)158 bool Console::Cmd_Var(int argc, const char **argv) {
159 
160 	if (argc != 2 && argc != 3) {
161 		debugPrintf("Usage :\n");
162 		debugPrintf("var variable : Display var value\n");
163 		debugPrintf("var variable value : Change var value\n");
164 		return true;
165 	}
166 
167 	uint16 var = atoi(argv[1]);
168 	uint32 value = _vm->_state->getVar(var);
169 
170 	if (argc == 3) {
171 		value = atoi(argv[2]);
172 		_vm->_state->setVar(var, value);
173 	}
174 
175 	debugPrintf("%s: %d\n", _vm->_state->describeVar(var).c_str(), value);
176 
177 	return true;
178 }
179 
Cmd_ListNodes(int argc,const char ** argv)180 bool Console::Cmd_ListNodes(int argc, const char **argv) {
181 	uint32 roomID = _vm->_state->getLocationRoom();
182 	uint32 ageID = _vm->_state->getLocationAge();
183 
184 	if (argc == 2) {
185 		RoomKey roomKey = _vm->_db->getRoomKey(argv[1]);
186 		if (roomKey.roomID == 0 || roomKey.ageID == 0) {
187 			debugPrintf("Unknown room name %s\n", argv[1]);
188 			return true;
189 		}
190 
191 		roomID = roomKey.roomID;
192 		ageID = roomKey.ageID;
193 	}
194 
195 	debugPrintf("Nodes:\n");
196 
197 	Common::Array<uint16> list = _vm->_db->listRoomNodes(roomID, ageID);
198 	for (uint i = 0; i < list.size(); i++) {
199 		debugPrintf("%d\n", list[i]);
200 	}
201 
202 	return true;
203 }
204 
Cmd_Run(int argc,const char ** argv)205 bool Console::Cmd_Run(int argc, const char **argv) {
206 	uint16 nodeId = _vm->_state->getLocationNode();
207 	uint32 roomId = _vm->_state->getLocationRoom();
208 	uint32 ageId = _vm->_state->getLocationAge();
209 
210 	if (argc >= 2) {
211 		nodeId = atoi(argv[1]);
212 	}
213 
214 	if (argc >= 3) {
215 		RoomKey roomKey = _vm->_db->getRoomKey(argv[2]);
216 
217 		if (roomKey.roomID == 0 || roomKey.ageID == 0) {
218 			debugPrintf("Unknown room name %s\n", argv[2]);
219 			return true;
220 		}
221 
222 		roomId = roomKey.roomID;
223 		ageId = roomKey.ageID;
224 	}
225 
226 	_vm->runScriptsFromNode(nodeId, roomId, ageId);
227 
228 	return false;
229 }
230 
Cmd_RunOp(int argc,const char ** argv)231 bool Console::Cmd_RunOp(int argc, const char **argv) {
232 	if (argc < 2) {
233 		debugPrintf("Usage :\n");
234 		debugPrintf("runOp [opcode] [argument 1] [argument 2] ... : Run specified command\n");
235 		return true;
236 	}
237 
238 	Opcode op;
239 	op.op = atoi(argv[1]);
240 
241 	for (int i = 2; i < argc; i++) {
242 		op.args.push_back(atoi(argv[i]));
243 	}
244 
245 	debugPrintf("Running opcode :\n");
246 	debugPrintf("%s\n", _vm->_scriptEngine->describeOpcode(op).c_str());
247 
248 	_vm->_scriptEngine->runSingleOp(op);
249 
250 	return false;
251 }
252 
Cmd_Go(int argc,const char ** argv)253 bool Console::Cmd_Go(int argc, const char **argv) {
254 	if (argc != 3) {
255 		debugPrintf("Usage :\n");
256 		debugPrintf("go [room name] [node id] : Go to node\n");
257 		return true;
258 	}
259 
260 	RoomKey roomKey = _vm->_db->getRoomKey(argv[1]);
261 	if (roomKey.roomID == 0 || roomKey.ageID == 0) {
262 		debugPrintf("Unknown room name %s\n", argv[1]);
263 		return true;
264 	}
265 
266 	uint16 nodeId = atoi(argv[2]);
267 
268 	_vm->_state->setLocationNextAge(roomKey.ageID);
269 	_vm->_state->setLocationNextRoom(roomKey.roomID);
270 	_vm->_state->setLocationNextNode(nodeId);
271 
272 	_vm->goToNode(0, kTransitionFade);
273 
274 	return false;
275 }
276 
Cmd_Extract(int argc,const char ** argv)277 bool Console::Cmd_Extract(int argc, const char **argv) {
278 	if (argc != 5) {
279 		debugPrintf("Extract a file from the game's archives\n");
280 		debugPrintf("Usage :\n");
281 		debugPrintf("extract [room] [node id] [face number] [object type]\n");
282 		return true;
283 	}
284 
285 	// Room names are uppercase
286 	Common::String room = Common::String(argv[1]);
287 	room.toUppercase();
288 
289 	uint16 id = atoi(argv[2]);
290 	uint16 face = atoi(argv[3]);
291 	DirectorySubEntry::ResourceType type = (DirectorySubEntry::ResourceType) atoi(argv[4]);
292 
293 	const DirectorySubEntry *desc = _vm->getFileDescription(room, id, face, type);
294 
295 	if (!desc) {
296 		debugPrintf("File with room %s, id %d, face %d and type %d does not exist\n", room.c_str(), id, face, type);
297 		return true;
298 	}
299 
300 	Common::MemoryReadStream *s = desc->getData();
301 	Common::String filename = Common::String::format("node%s_%d_face%d.%d", room.c_str(), id, face, type);
302 	Common::DumpFile f;
303 	f.open(filename);
304 
305 	uint8 *buf = new uint8[s->size()];
306 
307 	s->read(buf, s->size());
308 	f.write(buf, s->size());
309 
310 	delete[] buf;
311 
312 	f.close();
313 
314 	delete s;
315 
316 	debugPrintf("File '%s' successfully written\n", filename.c_str());
317 
318 	return true;
319 }
320 
Cmd_FillInventory(int argc,const char ** argv)321 bool Console::Cmd_FillInventory(int argc, const char **argv) {
322 	_vm->_inventory->addAll();
323 	return false;
324 }
325 
Cmd_DumpArchive(int argc,const char ** argv)326 bool Console::Cmd_DumpArchive(int argc, const char **argv) {
327 	if (argc != 2) {
328 		debugPrintf("Extract all the files from a game archive.\n");
329 		debugPrintf("The destination folder, named 'dump', must exist.\n");
330 		debugPrintf("Usage :\n");
331 		debugPrintf("dumpArchive [file name]\n");
332 		return true;
333 	}
334 
335 	// Is the archive multi-room
336 	Common::String temp = Common::String(argv[1]);
337 	temp.toUppercase();
338 
339 	bool multiRoom = !temp.hasSuffix(".M3A");
340 	if (!multiRoom) {
341 		temp = Common::String(argv[1], 4);
342 		temp.toUppercase();
343 	}
344 
345 	Archive archive;
346 	if (!archive.open(argv[1], multiRoom ? 0 : temp.c_str())) {
347 		debugPrintf("Can't open archive with name '%s'\n", argv[1]);
348 		return true;
349 	}
350 
351 	archive.dumpToFiles();
352 	archive.close();
353 
354 	return true;
355 }
356 
Cmd_DumpMasks(int argc,const char ** argv)357 bool Console::Cmd_DumpMasks(int argc, const char **argv) {
358 	if (argc != 1 && argc != 2) {
359 		debugPrintf("Extract the masks of the faces of a cube node.\n");
360 		debugPrintf("The destination folder, named 'dump', must exist.\n");
361 		debugPrintf("Usage :\n");
362 		debugPrintf("dumpMasks [node]\n");
363 		return true;
364 	}
365 
366 	uint16 nodeId = _vm->_state->getLocationNode();
367 
368 	if (argc >= 2) {
369 		nodeId = atoi(argv[1]);
370 	}
371 
372 	debugPrintf("Extracting masks for node %d:\n", nodeId);
373 
374 	for (uint i = 0; i < 6; i++) {
375 		bool water = dumpFaceMask(nodeId, i, DirectorySubEntry::kWaterEffectMask);
376 		if (water)
377 			debugPrintf("Face %d: water OK\n", i);
378 
379 		bool effect2 = dumpFaceMask(nodeId, i, DirectorySubEntry::kLavaEffectMask);
380 		if (effect2)
381 			debugPrintf("Face %d: effect 2 OK\n", i);
382 
383 		bool magnet = dumpFaceMask(nodeId, i, DirectorySubEntry::kMagneticEffectMask);
384 		if (magnet)
385 			debugPrintf("Face %d: magnet OK\n", i);
386 
387 		if (!water && !effect2 && !magnet)
388 			debugPrintf("Face %d: No mask found\n", i);
389 	}
390 
391 	return true;
392 }
393 
dumpFaceMask(uint16 index,int face,DirectorySubEntry::ResourceType type)394 bool Console::dumpFaceMask(uint16 index, int face, DirectorySubEntry::ResourceType type) {
395 	const DirectorySubEntry *maskDesc = _vm->getFileDescription("", index, face, type);
396 
397 	if (!maskDesc)
398 		return false;
399 
400 	Common::MemoryReadStream *maskStream = maskDesc->getData();
401 
402 	Effect::FaceMask *mask = Effect::loadMask(maskStream);
403 
404 	delete maskStream;
405 
406 	Common::DumpFile outFile;
407 	outFile.open(Common::String::format("dump/%d-%d.masku_%d", index, face, type));
408 	outFile.write(mask->surface->getPixels(), mask->surface->pitch * mask->surface->h);
409 	outFile.close();
410 
411 	delete mask;
412 
413 	return true;
414 }
415 
416 } // End of namespace Myst3
417