/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/debug-channels.h" #include "adl/console.h" #include "adl/display.h" #include "adl/graphics.h" #include "adl/adl.h" #include "adl/disk.h" namespace Adl { Console::Console(AdlEngine *engine) : GUI::Debugger() { _engine = engine; registerCmd("nouns", WRAP_METHOD(Console, Cmd_Nouns)); registerCmd("verbs", WRAP_METHOD(Console, Cmd_Verbs)); registerCmd("dump_scripts", WRAP_METHOD(Console, Cmd_DumpScripts)); registerCmd("valid_cmds", WRAP_METHOD(Console, Cmd_ValidCommands)); registerCmd("region", WRAP_METHOD(Console, Cmd_Region)); registerCmd("room", WRAP_METHOD(Console, Cmd_Room)); registerCmd("items", WRAP_METHOD(Console, Cmd_Items)); registerCmd("give_item", WRAP_METHOD(Console, Cmd_GiveItem)); registerCmd("vars", WRAP_METHOD(Console, Cmd_Vars)); registerCmd("var", WRAP_METHOD(Console, Cmd_Var)); registerCmd("convert_disk", WRAP_METHOD(Console, Cmd_ConvertDisk)); registerCmd("run_script", WRAP_METHOD(Console, Cmd_RunScript)); registerCmd("stop_script", WRAP_METHOD(Console, Cmd_StopScript)); registerCmd("set_script_delay", WRAP_METHOD(Console, Cmd_SetScriptDelay)); } Common::String Console::toAscii(const Common::String &str) { Common::String ascii(str); for (uint i = 0; i < ascii.size(); ++i) ascii.setChar(ascii[i] & 0x7f, i); return ascii; } Common::String Console::toNative(const Common::String &str) { Common::String native(str); if (native.size() > IDI_WORD_SIZE) native.erase(IDI_WORD_SIZE); native.toUppercase(); for (uint i = 0; i < native.size(); ++i) native.setChar(_engine->_display->asciiToNative(native[i]), i); while (native.size() < IDI_WORD_SIZE) native += _engine->_display->asciiToNative(' '); return native; } bool Console::Cmd_Verbs(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } debugPrintf("Verbs in alphabetical order:\n"); printWordMap(_engine->_verbs); return true; } bool Console::Cmd_Nouns(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } debugPrintf("Nouns in alphabetical order:\n"); printWordMap(_engine->_nouns); return true; } bool Console::Cmd_ValidCommands(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } WordMap::const_iterator verb, noun; bool is_any; for (verb = _engine->_verbs.begin(); verb != _engine->_verbs.end(); ++verb) { for (noun = _engine->_nouns.begin(); noun != _engine->_nouns.end(); ++noun) { if (_engine->isInputValid(verb->_value, noun->_value, is_any) && !is_any) debugPrintf("%s %s\n", toAscii(verb->_key).c_str(), toAscii(noun->_key).c_str()); } if (_engine->isInputValid(verb->_value, IDI_ANY, is_any)) debugPrintf("%s *\n", toAscii(verb->_key).c_str()); } if (_engine->isInputValid(IDI_ANY, IDI_ANY, is_any)) debugPrintf("* *\n"); return true; } void Console::dumpScripts(const Common::String &prefix) { for (byte roomNr = 1; roomNr <= _engine->_state.rooms.size(); ++roomNr) { _engine->loadRoom(roomNr); if (_engine->_roomData.commands.size() != 0) { _engine->_dumpFile->open(prefix + Common::String::format("%03d.ADL", roomNr).c_str()); _engine->doAllCommands(_engine->_roomData.commands, IDI_ANY, IDI_ANY); _engine->_dumpFile->close(); } } _engine->loadRoom(_engine->_state.room); _engine->_dumpFile->open(prefix + "GLOBAL.ADL"); _engine->doAllCommands(_engine->_globalCommands, IDI_ANY, IDI_ANY); _engine->_dumpFile->close(); _engine->_dumpFile->open(prefix + "RESPONSE.ADL"); _engine->doAllCommands(_engine->_roomCommands, IDI_ANY, IDI_ANY); _engine->_dumpFile->close(); } bool Console::Cmd_DumpScripts(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } bool oldFlag = DebugMan.isDebugChannelEnabled(kDebugChannelScript); DebugMan.enableDebugChannel("Script"); _engine->_dumpFile = new Common::DumpFile(); if (_engine->_state.regions.empty()) { dumpScripts(); } else { const byte oldRegion = _engine->_state.region; const byte oldPrevRegion = _engine->_state.prevRegion; const byte oldRoom = _engine->_state.room; for (byte regionNr = 1; regionNr <= _engine->_state.regions.size(); ++regionNr) { _engine->switchRegion(regionNr); dumpScripts(Common::String::format("%03d-", regionNr)); } _engine->switchRegion(oldRegion); _engine->_state.prevRegion = oldPrevRegion; _engine->_state.room = oldRoom; _engine->loadRoom(oldRoom); } delete _engine->_dumpFile; _engine->_dumpFile = nullptr; if (!oldFlag) DebugMan.disableDebugChannel("Script"); return true; } void Console::prepareGame() { _engine->_graphics->clearScreen(); _engine->loadRoom(_engine->_state.room); _engine->showRoom(); _engine->_display->renderText(); _engine->_display->renderGraphics(); } bool Console::Cmd_Region(int argc, const char **argv) { if (argc > 2) { debugPrintf("Usage: %s []\n", argv[0]); return true; } if (argc == 2) { if (!_engine->_canRestoreNow) { debugPrintf("Cannot change regions right now\n"); return true; } uint regionCount = _engine->_state.regions.size(); uint region = strtoul(argv[1], NULL, 0); if (region < 1 || region > regionCount) { debugPrintf("Region %u out of valid range [1, %u]\n", region, regionCount); return true; } _engine->switchRegion(region); prepareGame(); } debugPrintf("Current region: %u\n", _engine->_state.region); return true; } bool Console::Cmd_Room(int argc, const char **argv) { if (argc > 2) { debugPrintf("Usage: %s []\n", argv[0]); return true; } if (argc == 2) { if (!_engine->_canRestoreNow) { debugPrintf("Cannot change rooms right now\n"); return true; } uint roomCount = _engine->_state.rooms.size(); uint room = strtoul(argv[1], NULL, 0); if (room < 1 || room > roomCount) { debugPrintf("Room %u out of valid range [1, %u]\n", room, roomCount); return true; } _engine->switchRoom(room); prepareGame(); } debugPrintf("Current room: %u\n", _engine->_state.room); return true; } bool Console::Cmd_Items(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } Common::List::const_iterator item; for (item = _engine->_state.items.begin(); item != _engine->_state.items.end(); ++item) printItem(*item); return true; } bool Console::Cmd_GiveItem(int argc, const char **argv) { if (argc != 2) { debugPrintf("Usage: %s \n", argv[0]); return true; } Common::List::iterator item; char *end; uint id = strtoul(argv[1], &end, 0); if (*end != 0) { Common::Array matches; Common::String name = toNative(argv[1]); if (!_engine->_nouns.contains(name)) { debugPrintf("Item '%s' not found\n", argv[1]); return true; } byte noun = _engine->_nouns[name]; for (item = _engine->_state.items.begin(); item != _engine->_state.items.end(); ++item) { if (item->noun == noun) matches.push_back(&*item); } if (matches.size() == 0) { debugPrintf("Item '%s' not found\n", argv[1]); return true; } if (matches.size() > 1) { debugPrintf("Multiple matches found, please use item ID:\n"); for (uint i = 0; i < matches.size(); ++i) printItem(*matches[i]); return true; } matches[0]->room = IDI_ANY; debugPrintf("OK\n"); return true; } for (item = _engine->_state.items.begin(); item != _engine->_state.items.end(); ++item) if (item->id == id) { item->room = IDI_ANY; debugPrintf("OK\n"); return true; } debugPrintf("Item %i not found\n", id); return true; } bool Console::Cmd_Vars(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } Common::StringArray vars; for (uint i = 0; i < _engine->_state.vars.size(); ++i) vars.push_back(Common::String::format("%3d: %3d", i, _engine->_state.vars[i])); debugPrintf("Variables:\n"); debugPrintColumns(vars); return true; } bool Console::Cmd_Var(int argc, const char **argv) { if (argc < 2 || argc > 3) { debugPrintf("Usage: %s []\n", argv[0]); return true; } uint varCount = _engine->_state.vars.size(); uint var = strtoul(argv[1], NULL, 0); if (var >= varCount) { debugPrintf("Variable %u out of valid range [0, %u]\n", var, varCount - 1); return true; } if (argc == 3) { uint value = strtoul(argv[2], NULL, 0); _engine->_state.vars[var] = value; } debugPrintf("%3d: %3d\n", var, _engine->_state.vars[var]); return true; } void Console::printItem(const Item &item) { Common::String name, desc, state; if (item.noun > 0) name = _engine->_priNouns[item.noun - 1]; desc = toAscii(_engine->getItemDescription(item)); if (desc.lastChar() == '\r') desc.deleteLastChar(); switch (item.state) { case IDI_ITEM_NOT_MOVED: state = "PLACED"; break; case IDI_ITEM_DROPPED: state = "DROPPED"; break; case IDI_ITEM_DOESNT_MOVE: state = "FIXED"; break; } debugPrintf("%3d %s %-30s %-10s %-8s (%3d, %3d)\n", item.id, name.c_str(), desc.c_str(), _engine->itemRoomStr(item.room).c_str(), state.c_str(), item.position.x, item.position.y); } void Console::printWordMap(const WordMap &wordMap) { Common::StringArray words; WordMap::const_iterator verb; for (verb = wordMap.begin(); verb != wordMap.end(); ++verb) words.push_back(Common::String::format("%s: %3d", toAscii(verb->_key).c_str(), wordMap[verb->_key])); Common::sort(words.begin(), words.end()); debugPrintColumns(words); } bool Console::Cmd_ConvertDisk(int argc, const char **argv) { if (argc != 3) { debugPrintf("Usage: %s \n", argv[0]); return true; } DiskImage inDisk; if (!inDisk.open(argv[1])) { debugPrintf("Failed to open '%s' for reading\n", argv[1]); return true; } Common::DumpFile outDisk; if (!outDisk.open(argv[2])) { debugPrintf("Failed to open '%s' for writing\n", argv[2]); return true; } const uint sectors = inDisk.getTracks() * inDisk.getSectorsPerTrack(); const uint size = sectors * inDisk.getBytesPerSector(); byte *const buf = new byte[size]; StreamPtr stream(inDisk.createReadStream(0, 0, 0, sectors - 1)); if (stream->read(buf, size) < size) { debugPrintf("Failed to read from stream"); delete[] buf; return true; } if (outDisk.write(buf, size) < size) debugPrintf("Failed to write to '%s'", argv[2]); delete[] buf; return true; } bool Console::Cmd_RunScript(int argc, const char **argv) { if (argc != 2) { debugPrintf("Usage: %s \n", argv[0]); return true; } _engine->runScript(argv[1]); return false; } bool Console::Cmd_StopScript(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; } _engine->stopScript(); return true; } bool Console::Cmd_SetScriptDelay(int argc, const char **argv) { if (argc != 2) { debugPrintf("Usage: %s \n", argv[0]); debugPrintf("A delay of zero indicates wait-for-key\n"); return true; } Common::String value(argv[1]); _engine->setScriptDelay((uint)value.asUint64()); return true; } } // End of namespace Adl