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