1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
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 "titanic/debugger.h"
24 #include "titanic/core/node_item.h"
25 #include "titanic/core/room_item.h"
26 #include "titanic/core/tree_item.h"
27 #include "titanic/core/view_item.h"
28 #include "titanic/game_manager.h"
29 #include "titanic/game/movie_tester.h"
30 #include "titanic/main_game_window.h"
31 #include "titanic/pet_control/pet_control.h"
32 #include "titanic/support/movie.h"
33 #include "titanic/titanic.h"
34 #include "common/str-array.h"
35 
36 namespace Titanic {
37 
Debugger(TitanicEngine * vm)38 Debugger::Debugger(TitanicEngine *vm) : GUI::Debugger(), _vm(vm) {
39 	registerCmd("continue",		WRAP_METHOD(Debugger, cmdExit));
40 	registerCmd("dump",			WRAP_METHOD(Debugger, cmdDump));
41 	registerCmd("room",			WRAP_METHOD(Debugger, cmdRoom));
42 	registerCmd("pet",			WRAP_METHOD(Debugger, cmdPET));
43 	registerCmd("item",			WRAP_METHOD(Debugger, cmdItem));
44 	registerCmd("movie",		WRAP_METHOD(Debugger, cmdMovie));
45 	registerCmd("sound",		WRAP_METHOD(Debugger, cmdSound));
46 	registerCmd("cheat",        WRAP_METHOD(Debugger, cmdCheat));
47 	registerCmd("frame",        WRAP_METHOD(Debugger, cmdFrame));
48 }
49 
strToInt(const char * s)50 int Debugger::strToInt(const char *s) {
51 	if (!*s)
52 		// No string at all
53 		return 0;
54 	else if (toupper(s[strlen(s) - 1]) != 'H')
55 		// Standard decimal string
56 		return atoi(s);
57 
58 	// Hexadecimal string
59 	uint tmp = 0;
60 	int read = sscanf(s, "%xh", &tmp);
61 	if (read < 1)
62 		error("strToInt failed on string \"%s\"", s);
63 	return (int)tmp;
64 }
65 
findRoom(const char * name)66 CRoomItem *Debugger::findRoom(const char *name) {
67 	CTreeItem *root = g_vm->_window->_gameManager->_project;
68 	CRoomItem *roomItem = dynamic_cast<CRoomItem *>(root->findByName(name));
69 	if (roomItem)
70 		return roomItem;
71 
72 	int roomNumber = strToInt(name);
73 	for (CTreeItem *treeItem = root; treeItem; treeItem = treeItem->scan(root)) {
74 		roomItem = dynamic_cast<CRoomItem *>(treeItem);
75 		if (roomItem && roomItem->_roomNumber == roomNumber)
76 			return roomItem;
77 	}
78 
79 	return nullptr;
80 }
81 
findNode(CRoomItem * room,const char * name)82 CNodeItem *Debugger::findNode(CRoomItem *room, const char *name) {
83 	CNodeItem *nodeItem = dynamic_cast<CNodeItem *>(room->findByName(name));
84 	if (nodeItem)
85 		return nodeItem;
86 
87 	int nodeNumber = strToInt(name);
88 	nodeItem = dynamic_cast<CNodeItem *>(room->findChildInstanceOf(CNodeItem::_type));
89 	while (nodeItem) {
90 		if (nodeItem->_nodeNumber == nodeNumber)
91 			return nodeItem;
92 
93 		nodeItem = dynamic_cast<CNodeItem *>(room->findNextInstanceOf(CNodeItem::_type, nodeItem));
94 	}
95 
96 	return nullptr;
97 }
98 
findView(CNodeItem * node,const char * name)99 CViewItem *Debugger::findView(CNodeItem *node, const char *name) {
100 	CViewItem *viewItem = dynamic_cast<CViewItem *>(node->findByName(name));
101 	if (viewItem)
102 		return viewItem;
103 
104 	int viewNumber = strToInt(name);
105 	viewItem = dynamic_cast<CViewItem *>(node->findChildInstanceOf(CViewItem::_type));
106 	while (viewItem) {
107 		if (viewItem->_viewNumber == viewNumber)
108 			return viewItem;
109 
110 		viewItem = dynamic_cast<CViewItem *>(node->findNextInstanceOf(CViewItem::_type, viewItem));
111 	}
112 
113 	return nullptr;
114 }
115 
listRooms()116 void Debugger::listRooms() {
117 	CGameManager &gm = *g_vm->_window->_gameManager;
118 	CTreeItem *root = gm._project;
119 	CViewItem *view = gm._gameState._gameLocation.getView();
120 	CNodeItem *node = gm._gameState._gameLocation.getNode();
121 	CRoomItem *room = gm._gameState._gameLocation.getRoom();
122 	debugPrintf("Current location: %s, %s, %s\n", room->getName().c_str(),
123 		node->getName().c_str(), view->getName().c_str());
124 
125 	debugPrintf("Available rooms:\n");
126 	for (CTreeItem *treeItem = root; treeItem; treeItem = treeItem->scan(root)) {
127 		CRoomItem *roomItem = dynamic_cast<CRoomItem *>(treeItem);
128 		if (roomItem)
129 			debugPrintf("%d - %s\n", roomItem->_roomNumber, roomItem->_name.c_str());
130 	}
131 }
132 
listRoom(CRoomItem * room)133 void Debugger::listRoom(CRoomItem *room) {
134 	for (CTreeItem *treeItem = room; treeItem; treeItem = treeItem->scan(room)) {
135 		CNodeItem *nodeItem = dynamic_cast<CNodeItem *>(treeItem);
136 		if (nodeItem)
137 			debugPrintf("%s\n", nodeItem->_name.c_str());
138 	}
139 }
140 
listNode(CNodeItem * node)141 void Debugger::listNode(CNodeItem *node) {
142 	for (CTreeItem *treeItem = node; treeItem; treeItem = treeItem->scan(node)) {
143 		CViewItem *viewItem = dynamic_cast<CViewItem *>(treeItem);
144 		if (viewItem)
145 			debugPrintf("%s\n", viewItem->_name.c_str());
146 	}
147 }
148 
cmdDump(int argc,const char ** argv)149 bool Debugger::cmdDump(int argc, const char **argv) {
150 	// Get the starting node
151 	CTreeItem *root = g_vm->_window->_gameManager->_project;
152 	if (argc == 2)
153 		root = root->findByName(argv[1]);
154 
155 	if (root == nullptr) {
156 		debugPrintf("Could not find item\n");
157 	} else {
158 		root->dump(0);
159 		debugPrintf("Item and it's content were dumped to stdout\n");
160 	}
161 
162 	return true;
163 }
164 
cmdRoom(int argc,const char ** argv)165 bool Debugger::cmdRoom(int argc, const char **argv) {
166 	if (argc == 1) {
167 		listRooms();
168 	} else if (argc >= 2) {
169 		CRoomItem *roomItem = findRoom(argv[1]);
170 
171 		if (!roomItem && argc == 2) {
172 			// Presume it's a full view specified
173 			CProjectItem *project = g_vm->_window->_project;
174 			CViewItem *view = project->parseView(argv[1]);
175 
176 			if (view) {
177 				project->changeView(argv[1]);
178 				return false;
179 			} else {
180 				debugPrintf("Could not find view - %s\n", argv[1]);
181 			}
182 		} else if (argc == 2)
183 			listRoom(roomItem);
184 		else {
185 			CNodeItem *nodeItem = findNode(roomItem, argv[2]);
186 
187 			if (!nodeItem)
188 				debugPrintf("Could not find node - %s\n", argv[2]);
189 			else if (argc == 3)
190 				listNode(nodeItem);
191 			else {
192 				CViewItem *viewItem = findView(nodeItem, argv[3]);
193 
194 				if (!viewItem) {
195 					debugPrintf("Could not find view - %s\n", argv[3]);
196 				} else {
197 					// Change to the specified view
198 					g_vm->_window->_gameManager->_gameState.changeView(viewItem, nullptr);
199 					return false;
200 				}
201 			}
202 		}
203 	}
204 
205 	return true;
206 }
207 
cmdPET(int argc,const char ** argv)208 bool Debugger::cmdPET(int argc, const char **argv) {
209 	CGameManager &gameManager = *g_vm->_window->_gameManager;
210 	CGameState &gameState = gameManager._gameState;
211 
212 	if (argc == 2) {
213 		CString s(argv[1]);
214 		s.toLowercase();
215 
216 		if (s == "on") {
217 			gameState._petActive = true;
218 			gameManager.markAllDirty();
219 			debugPrintf("PET is now on\n");
220 			return true;
221 		} else if (s == "off") {
222 			gameState._petActive = false;
223 			gameManager.update();
224 			debugPrintf("PET is now off\n");
225 			return true;
226 		}
227 	}
228 
229 	debugPrintf("%s [on | off]\n", argv[0]);
230 	return true;
231 }
232 
cmdItem(int argc,const char ** argv)233 bool Debugger::cmdItem(int argc, const char **argv) {
234 	CGameManager &gameManager = *g_vm->_window->_gameManager;
235 	CGameState &gameState = gameManager._gameState;
236 
237 	if (argc == 1) {
238 		// No parameters, so list the available items
239 		debugPrintf("item [<name> [ add ]]\n");
240 		for (int idx = 0; idx < 40; ++idx)
241 			debugPrintf("%s\n", g_vm->_itemIds[idx].c_str());
242 	} else {
243 		// Ensure the specified name is a valid inventory item
244 		int itemIndex;
245 		for (itemIndex = 0; itemIndex < 40; ++itemIndex) {
246 			if (g_vm->_itemIds[itemIndex] == argv[1])
247 				break;
248 		}
249 		if (itemIndex == 40) {
250 			debugPrintf("Could not find item with that name\n");
251 			return true;
252 		}
253 
254 		// Get the item
255 		CCarry *item = dynamic_cast<CCarry *>(
256 			g_vm->_window->_project->findByName(argv[1]));
257 		assert(item);
258 
259 		if (argc == 2) {
260 			// List it's details
261 			CTreeItem *treeItem = item;
262 			CString fullName;
263 			while ((treeItem = treeItem->getParent()) != nullptr) {
264 				if (!treeItem->getName().empty())
265 					fullName = treeItem->getName() + "." + fullName;
266 			}
267 
268 			debugPrintf("Current location: %s\n", fullName.c_str());
269 		} else if (CString(argv[2]) == "add") {
270 			// Ensure the PET is active and add the item to the inventory
271 			gameState._petActive = true;
272 			gameManager.markAllDirty();
273 			item->petAddToInventory();
274 
275 			return false;
276 		} else {
277 			debugPrintf("Unknown command\n");
278 		}
279 	}
280 
281 	return true;
282 }
283 
cmdMovie(int argc,const char ** argv)284 bool Debugger::cmdMovie(int argc, const char **argv) {
285 	if (argc < 2) {
286 		debugPrintf("movie filename.avi [startFrame endFrame]\n");
287 		return true;
288 	}
289 
290 	CViewItem *view = g_vm->_window->_gameManager->getView();
291 	CMovieTester *tester = static_cast<CMovieTester *>(
292 		view->findChildInstanceOf(CMovieTester::_type));
293 	if (!tester) {
294 		// No movie tester present, so create one
295 		tester = new CMovieTester();
296 		tester->addUnder(view);
297 	}
298 
299 	CString filename(argv[1]);
300 
301 	if (filename == "reverse" || filename == "doubletake") {
302 		// Tests reverse playback transparency frames
303 		tester->loadMovie("y457.avi");
304 		if (filename == "reverse") {
305 			tester->playMovie(436, 0, MOVIE_STOP_PREVIOUS);
306 		} else {
307 			tester->playMovie(436, 432, MOVIE_STOP_PREVIOUS);
308 			tester->playMovie(432, 436, 0);
309 			tester->playMovie(436, 432, 0);
310 			tester->playMovie(432, 436, 0);
311 		}
312 
313 		return false;
314 	}
315 
316 	if (!filename.hasSuffix(".avi"))
317 		filename += ".avi";
318 	tester->loadMovie(filename);
319 
320 	if (argc == 2) {
321 		tester->playMovie(MOVIE_STOP_PREVIOUS);
322 	} else {
323 		uint startFrame = strToInt(argv[2]);
324 		uint endFrame = (argc == 3) ? startFrame : strToInt(argv[3]);
325 		tester->playMovie(startFrame, endFrame, MOVIE_STOP_PREVIOUS);
326 	}
327 
328 	return false;
329 }
330 
cmdSound(int argc,const char ** argv)331 bool Debugger::cmdSound(int argc, const char **argv) {
332 	if (argc == 2) {
333 		Common::String name = argv[1];
334 		if (!name.contains("#"))
335 			name = "z#" + name;
336 
337 		CGameManager *gameManager = g_vm->_window->_gameManager;
338 		CProximity prox;
339 		gameManager->_sound.playSound(name, prox);
340 		return false;
341 	} else {
342 		debugPrintf("sound <name>\n");
343 		return true;
344 	}
345 }
346 
cmdCheat(int argc,const char ** argv)347 bool Debugger::cmdCheat(int argc, const char **argv) {
348 	CGameManager *gameManager = g_vm->_window->_gameManager;
349 	CProjectItem *project = g_vm->_window->_project;
350 
351 	CViewItem *newView = project->parseView("Cheat Room.Node 1.Cheat Rooms View");
352 	gameManager->_gameState.changeView(newView, nullptr);
353 	return false;
354 }
355 
cmdFrame(int argc,const char ** argv)356 bool Debugger::cmdFrame(int argc, const char **argv) {
357 	if (argc == 3) {
358 		CGameObject *obj = dynamic_cast<CGameObject *>(
359 			g_vm->_window->_project->findByName(argv[1]));
360 
361 		if (obj) {
362 			obj->loadFrame(strToInt(argv[2]));
363 			return false;
364 		} else {
365 			debugPrintf("Object not found\n");
366 			return true;
367 		}
368 	} else {
369 		debugPrintf("frame <object> <frame number>");
370 		return true;
371 	}
372 }
373 
374 } // End of namespace Titanic
375