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 "neverhood/console.h"
24 #include "gui/debugger.h"
25 #include "neverhood/neverhood.h"
26 #include "neverhood/gamemodule.h"
27 #include "neverhood/navigationscene.h"
28 #include "neverhood/scene.h"
29 #include "neverhood/smackerscene.h"
30 #include "neverhood/sound.h"
31 #include "neverhood/modules/module1600.h"
32 #include "neverhood/modules/module3000_sprites.h"
33 
34 namespace Neverhood {
35 
Console(NeverhoodEngine * vm)36 Console::Console(NeverhoodEngine *vm) : GUI::Debugger(), _vm(vm) {
37 	registerCmd("cheat",			WRAP_METHOD(Console, Cmd_Cheat));
38 	registerCmd("checkresource",	WRAP_METHOD(Console, Cmd_CheckResource));
39 	registerCmd("dumpresource",	WRAP_METHOD(Console, Cmd_DumpResource));
40 	registerCmd("dumpvars",		WRAP_METHOD(Console, Cmd_Dumpvars));
41 	registerCmd("playsound",		WRAP_METHOD(Console, Cmd_PlaySound));
42 	registerCmd("scene",			WRAP_METHOD(Console, Cmd_Scene));
43 	registerCmd("surfaces",		WRAP_METHOD(Console, Cmd_Surfaces));
44 }
45 
~Console()46 Console::~Console() {
47 }
48 
Cmd_Scene(int argc,const char ** argv)49 bool Console::Cmd_Scene(int argc, const char **argv) {
50 	if (argc != 3) {
51 		int currentModule = _vm->_gameModule->getCurrentModuleNum();
52 		int previousModule = _vm->_gameModule->getPreviousModuleNum();
53 		int scenenNum = _vm->gameState().sceneNum;
54 		SceneType sceneType = ((GameModule *)_vm->_gameModule->_childObject)->getSceneType();
55 
56 		const char *sceneTypes[] = { "normal", "smacker", "navigation" };
57 
58 		debugPrintf("Current module: %d, previous module: %d, scene %d (%s scene)\n", currentModule, previousModule, scenenNum, sceneTypes[sceneType]);
59 
60 		if (sceneType == kSceneTypeNormal) {
61 			Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
62 			// Normal scenes have a background and a cursor file hash
63 			debugPrintf("Background hash: 0x%x, cursor hash: 0x%x\n", scene->getBackgroundFileHash(), scene->getCursorFileHash());
64 		} else if (sceneType == kSceneTypeSmacker) {
65 			SmackerScene *scene = (SmackerScene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
66 			// Smacker scenes have a file hash, or a list of hashes
67 			// TODO: Only the first file hash is shown - any additional hashes, found in
68 			// scenes with a list of hashes (two scenes in module 1100 and the making of
69 			// video) aren't shown yet
70 			debugPrintf("File hash: 0x%x\n", scene->getSmackerFileHash());
71 		} else if (sceneType == kSceneTypeNavigation) {
72 			NavigationScene *scene = (NavigationScene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
73 			// Navigation scenes have a navigation list and its index
74 			NavigationList *navigationList = _vm->_staticData->getNavigationList(scene->getNavigationListId());
75 			int navigationIndex = scene->getGlobalVar(V_NAVIGATION_INDEX);
76 			NavigationItem curNavigation = (*navigationList)[navigationIndex];
77 			debugPrintf("Navigation list ID: 0x%x, index: %d\n", scene->getNavigationListId(), navigationIndex);
78 			debugPrintf("File hash: 0x%x, cursor hash: 0x%x, Smacker hashes: [left: 0x%x, middle: 0x%x, right: 0x%x\n",
79 				curNavigation.fileHash, curNavigation.mouseCursorFileHash,
80 				curNavigation.leftSmackerFileHash, curNavigation.middleSmackerFileHash, curNavigation.rightSmackerFileHash);
81 		}
82 
83 		debugPrintf("Use %s <module> <scene> to change scenes\n", argv[0]);
84 		debugPrintf("Modules are incremental by 100, from 1000 to 3000\n");
85 	} else {
86 		int newModule = atoi(argv[1]);
87 		int newScene  = atoi(argv[2]);
88 
89 		_vm->gameState().sceneNum = newScene;
90 		_vm->_gameModule->createModule(newModule, -1);
91 	}
92 
93 	return true;
94 }
95 
Cmd_Surfaces(int argc,const char ** argv)96 bool Console::Cmd_Surfaces(int argc, const char **argv) {
97 	if (_vm->_gameModule->_childObject) {
98 		((Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject)->printSurfaces(this);
99 	}
100 	return true;
101 }
102 
Cmd_Cheat(int argc,const char ** argv)103 bool Console::Cmd_Cheat(int argc, const char **argv) {
104 	if (argc < 2) {
105 		debugPrintf("Cheats for various puzzles in the game\n");
106 		debugPrintf("Use %s <cheatname> to use a cheat.\n", argv[0]);
107 		debugPrintf("Cheats:\n-------\n");
108 		debugPrintf("  buttons - enables all 3 buttons on the door in the purple building, module 3000, scene 9\n");
109 		debugPrintf("  cannon  - sets the correct cannon combination in module 3000, scene 8\n");
110 		debugPrintf("  dice    - shows the correct dice combination in the teddy bear puzzle, module 1100, scene 6\n");
111 		debugPrintf("  memory  - solves the memory puzzle, module 1400, scene 4\n");
112 		debugPrintf("  music   - shows the correct index in the radio music puzzle, module 2800, scene 1\n");
113 		debugPrintf("  radio   - enables the radio, module 3000, scene 9 - same as pulling the rightmost cord in the flytrap room\n");
114 		debugPrintf("  symbols - solves the symbols puzzle, module 1600, scene 8. Only available in that room\n");
115 		debugPrintf("  tubes   - shows the correct test tube combination in module 2800, scenes 7 and 10\n");
116 		return true;
117 	}
118 
119 	Common::String cheatName = argv[1];
120 	int moduleNum = _vm->_gameModule->getCurrentModuleNum();
121 	int sceneNum = _vm->gameState().sceneNum;
122 
123 	if (cheatName == "buttons") {
124 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
125 
126 		scene->setSubVar(VA_LOCKS_DISABLED, 0x304008D2, 1);	// kScene3010ButtonNameHashes[0]
127 		scene->setSubVar(VA_LOCKS_DISABLED, 0x40119852, 1);	// kScene3010ButtonNameHashes[1]
128 		scene->setSubVar(VA_LOCKS_DISABLED, 0x01180951, 1);	// kScene3010ButtonNameHashes[2]
129 
130 		debugPrintf("All 3 door buttons have been enabled\n");
131 	} else if (cheatName == "cannon") {
132 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
133 
134 		for (int i = 0; i < 3; i++)
135 			scene->setSubVar(VA_CURR_CANNON_SYMBOLS, i,	scene->getSubVar(VA_GOOD_CANNON_SYMBOLS_1, i));
136 
137 		for (int i = 3; i < 6; i++)
138 			scene->setSubVar(VA_CURR_CANNON_SYMBOLS, i,	scene->getSubVar(VA_GOOD_CANNON_SYMBOLS_2, i - 3));
139 
140 		debugPrintf("Puzzle solved\n");
141 	} else if (cheatName == "dice") {
142 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
143 		debugPrintf("Good: (%d %d %d), current: (%d %d %d)\n",
144 			scene->getSubVar(VA_GOOD_DICE_NUMBERS, 0), scene->getSubVar(VA_GOOD_DICE_NUMBERS, 1), scene->getSubVar(VA_GOOD_DICE_NUMBERS, 2),
145 			scene->getSubVar(VA_CURR_DICE_NUMBERS, 0), scene->getSubVar(VA_CURR_DICE_NUMBERS, 1), scene->getSubVar(VA_CURR_DICE_NUMBERS, 2)
146 		);
147 	} else if (cheatName == "memory") {
148 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
149 
150 		// Autosolve all tiles and leave only two matching tiles closed
151 		for (int i = 0; i < 48; i++)
152 			scene->setSubVar(VA_IS_TILE_MATCH, i, 1);
153 
154 		// Close the top left tile
155 		scene->setSubVar(VA_IS_TILE_MATCH, 0, 0);
156 
157 		// Find and close the pair of the top left tile
158 		for (int i = 0; i < 48; i++) {
159 			if (i != 0 && scene->getSubVar(VA_TILE_SYMBOLS, i) == scene->getSubVar(VA_TILE_SYMBOLS, 0)) {
160 				scene->setSubVar(VA_IS_TILE_MATCH, i, 0);
161 				break;
162 			}
163 		}
164 
165 		debugPrintf("Puzzle solved\n");
166 	} else if (cheatName == "music") {
167 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
168 		debugPrintf("Good music index: %d, current radio music index: %d\n", scene->getGlobalVar(V_CURR_RADIO_MUSIC_INDEX), scene->getGlobalVar(V_GOOD_RADIO_MUSIC_INDEX));
169 	} else if (cheatName == "radio") {
170 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
171 		scene->setGlobalVar(V_RADIO_ENABLED, 1);
172 
173 		debugPrintf("The radio has been enabled\n");
174 	} else if (cheatName == "symbols") {
175 		if (moduleNum == 1600 && sceneNum == 8) {
176 			Scene1609 *scene = ((Scene1609 *)((Module1600 *)_vm->_gameModule->_childObject)->_childObject);
177 
178 			for (int index = 0; index < 12; index++) {
179 				scene->_asSymbols[index]->change((int)scene->getSubVar(VA_CODE_SYMBOLS, index) + 12, index == (int)scene->getSubVar(VA_CODE_SYMBOLS, scene->_noisySymbolIndex));
180 			}
181 
182 			scene->_changeCurrentSymbol = false;
183 			scene->_symbolPosition = 11;
184 			scene->_countdown1 = 36;
185 
186 			debugPrintf("Puzzle solved\n");
187 		} else {
188 			debugPrintf("Only available in module 1600, scene 8\n");
189 		}
190 	} else if (cheatName == "tubes") {
191 		Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject;
192 		debugPrintf("Tube set 1: %d %d %d\n", scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 0), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 1), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_1, 2));
193 		debugPrintf("Tube set 2: %d %d %d\n", scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 0), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 1), scene->getSubVar(VA_GOOD_TEST_TUBES_LEVEL_2, 2));
194 	}
195 
196 	return true;
197 }
198 
Cmd_Dumpvars(int argc,const char ** argv)199 bool Console::Cmd_Dumpvars(int argc, const char **argv) {
200 	_vm->_gameVars->dumpVars(this);
201 
202 	return true;
203 }
204 
Cmd_PlaySound(int argc,const char ** argv)205 bool Console::Cmd_PlaySound(int argc, const char **argv) {
206 	if (argc < 2) {
207 		debugPrintf("Usage: %s <sound hash>\n", argv[0]);
208 	} else {
209 		uint32 soundHash = strtol(argv[1], NULL, 0);
210 		AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(_vm, soundHash);
211 		soundItem->setVolume(100);
212 		soundItem->playSound(false);
213 		while (soundItem->isPlaying()) {
214 			_vm->_system->delayMillis(10);
215 		}
216 		delete soundItem;
217 	}
218 
219 	return true;
220 }
221 
Cmd_CheckResource(int argc,const char ** argv)222 bool Console::Cmd_CheckResource(int argc, const char **argv) {
223 	const char *resourceNames[] = { "unknown", "unknown", "bitmap", "palette", "animation", "data", "text", "sound", "music", "unknown", "video" };
224 
225 	if (argc < 2) {
226 		debugPrintf("Gets information about a resource\n");
227 		debugPrintf("Usage: %s <resource hash>\n", argv[0]);
228 	} else {
229 		uint32 resourceHash = strtol(argv[1], NULL, 0);
230 		ResourceHandle handle;
231 
232 		_vm->_res->queryResource(resourceHash, handle);
233 		if (!handle.isValid()) {
234 			debugPrintf("Invalid resource hash\n");
235 		} else {
236 			debugPrintf("Resource type: %d (%s). Size: %d bytes\n", handle.type(), resourceNames[handle.type()], handle.size());
237 		}
238 	}
239 
240 	return true;
241 }
242 
Cmd_DumpResource(int argc,const char ** argv)243 bool Console::Cmd_DumpResource(int argc, const char **argv) {
244 	if (argc < 3) {
245 		debugPrintf("Dumps a resource to disk\n");
246 		debugPrintf("Usage: %s <resource hash> <output file>\n", argv[0]);
247 	} else {
248 		uint32 resourceHash = strtol(argv[1], NULL, 0);
249 		const char *outFileName = argv[2];
250 		ResourceHandle handle;
251 
252 		_vm->_res->queryResource(resourceHash, handle);
253 		if (!handle.isValid()) {
254 			debugPrintf("Invalid resource hash\n");
255 		} else {
256 			_vm->_res->loadResource(handle, _vm->applyResourceFixes());
257 			Common::DumpFile outFile;
258 			outFile.open(outFileName);
259 			outFile.write(handle.data(), handle.size());
260 			outFile.finalize();
261 			outFile.close();
262 			_vm->_res->unloadResource(handle);
263 		}
264 	}
265 
266 	return true;
267 }
268 
269 } // End of namespace Neverhood
270