/* 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. * * $URL$ * $Id$ * */ // Console module #include "sci/sci.h" #include "sci/console.h" #include "sci/debug.h" #include "sci/event.h" #include "sci/resource.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" #include "sci/engine/selector.h" #include "sci/engine/savegame.h" #include "sci/engine/gc.h" #include "sci/engine/features.h" #include "sci/sound/midiparser_sci.h" #include "sci/sound/music.h" #include "sci/sound/drivers/mididriver.h" #include "sci/graphics/cursor.h" #include "sci/graphics/screen.h" #include "sci/graphics/paint.h" #include "sci/graphics/paint16.h" #include "sci/graphics/paint32.h" #include "sci/graphics/palette.h" #include "sci/parser/vocabulary.h" #include "graphics/video/avi_decoder.h" #include "sci/video/seq_decoder.h" #ifdef ENABLE_SCI32 #include "graphics/video/coktel_decoder.h" #endif #include "common/file.h" #include "common/savefile.h" namespace Sci { int g_debug_sleeptime_factor = 1; int g_debug_simulated_key = 0; bool g_debug_track_mouse_clicks = false; // Refer to the "addresses" command on how to pass address parameters static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeValue); Console::Console(SciEngine *engine) : GUI::Debugger(), _engine(engine), _debugState(engine->_debugState), _enterTime(0) { // Variables DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0); DVar_Register("gc_interval", &engine->_gamestate->scriptGCInterval, DVAR_INT, 0); DVar_Register("simulated_key", &g_debug_simulated_key, DVAR_INT, 0); DVar_Register("track_mouse_clicks", &g_debug_track_mouse_clicks, DVAR_BOOL, 0); DVar_Register("script_abort_flag", &_engine->_gamestate->abortScriptProcessing, DVAR_INT, 0); // General DCmd_Register("help", WRAP_METHOD(Console, cmdHelp)); // Kernel // DCmd_Register("classes", WRAP_METHOD(Console, cmdClasses)); // TODO DCmd_Register("opcodes", WRAP_METHOD(Console, cmdOpcodes)); DCmd_Register("selector", WRAP_METHOD(Console, cmdSelector)); DCmd_Register("selectors", WRAP_METHOD(Console, cmdSelectors)); DCmd_Register("functions", WRAP_METHOD(Console, cmdKernelFunctions)); DCmd_Register("class_table", WRAP_METHOD(Console, cmdClassTable)); // Parser DCmd_Register("suffixes", WRAP_METHOD(Console, cmdSuffixes)); DCmd_Register("parse_grammar", WRAP_METHOD(Console, cmdParseGrammar)); DCmd_Register("parser_nodes", WRAP_METHOD(Console, cmdParserNodes)); DCmd_Register("parser_words", WRAP_METHOD(Console, cmdParserWords)); DCmd_Register("sentence_fragments", WRAP_METHOD(Console, cmdSentenceFragments)); DCmd_Register("parse", WRAP_METHOD(Console, cmdParse)); DCmd_Register("set_parse_nodes", WRAP_METHOD(Console, cmdSetParseNodes)); DCmd_Register("said", WRAP_METHOD(Console, cmdSaid)); // Resources DCmd_Register("diskdump", WRAP_METHOD(Console, cmdDiskDump)); DCmd_Register("hexdump", WRAP_METHOD(Console, cmdHexDump)); DCmd_Register("resource_id", WRAP_METHOD(Console, cmdResourceId)); DCmd_Register("resource_size", WRAP_METHOD(Console, cmdResourceSize)); DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes)); DCmd_Register("list", WRAP_METHOD(Console, cmdList)); DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep)); DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts)); DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments)); // Game DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame)); DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame)); DCmd_Register("restart_game", WRAP_METHOD(Console, cmdRestartGame)); DCmd_Register("version", WRAP_METHOD(Console, cmdGetVersion)); DCmd_Register("room", WRAP_METHOD(Console, cmdRoomNumber)); DCmd_Register("quit", WRAP_METHOD(Console, cmdQuit)); DCmd_Register("list_saves", WRAP_METHOD(Console, cmdListSaves)); // Graphics DCmd_Register("show_map", WRAP_METHOD(Console, cmdShowMap)); DCmd_Register("set_palette", WRAP_METHOD(Console, cmdSetPalette)); DCmd_Register("draw_pic", WRAP_METHOD(Console, cmdDrawPic)); DCmd_Register("draw_cel", WRAP_METHOD(Console, cmdDrawCel)); #ifdef ENABLE_SCI32 DCmd_Register("draw_robot", WRAP_METHOD(Console, cmdDrawRobot)); #endif DCmd_Register("undither", WRAP_METHOD(Console, cmdUndither)); DCmd_Register("pic_visualize", WRAP_METHOD(Console, cmdPicVisualize)); DCmd_Register("play_video", WRAP_METHOD(Console, cmdPlayVideo)); // Segments DCmd_Register("segment_table", WRAP_METHOD(Console, cmdPrintSegmentTable)); DCmd_Register("segtable", WRAP_METHOD(Console, cmdPrintSegmentTable)); // alias DCmd_Register("segment_info", WRAP_METHOD(Console, cmdSegmentInfo)); DCmd_Register("seginfo", WRAP_METHOD(Console, cmdSegmentInfo)); // alias DCmd_Register("segment_kill", WRAP_METHOD(Console, cmdKillSegment)); DCmd_Register("segkill", WRAP_METHOD(Console, cmdKillSegment)); // alias // Garbage collection DCmd_Register("gc", WRAP_METHOD(Console, cmdGCInvoke)); DCmd_Register("gc_objects", WRAP_METHOD(Console, cmdGCObjects)); DCmd_Register("gc_reachable", WRAP_METHOD(Console, cmdGCShowReachable)); DCmd_Register("gc_freeable", WRAP_METHOD(Console, cmdGCShowFreeable)); DCmd_Register("gc_normalize", WRAP_METHOD(Console, cmdGCNormalize)); // Music/SFX DCmd_Register("songlib", WRAP_METHOD(Console, cmdSongLib)); DCmd_Register("songinfo", WRAP_METHOD(Console, cmdSongInfo)); DCmd_Register("is_sample", WRAP_METHOD(Console, cmdIsSample)); DCmd_Register("startsound", WRAP_METHOD(Console, cmdStartSound)); DCmd_Register("togglesound", WRAP_METHOD(Console, cmdToggleSound)); DCmd_Register("stopallsounds", WRAP_METHOD(Console, cmdStopAllSounds)); DCmd_Register("sfx01_header", WRAP_METHOD(Console, cmdSfx01Header)); DCmd_Register("sfx01_track", WRAP_METHOD(Console, cmdSfx01Track)); // Script DCmd_Register("addresses", WRAP_METHOD(Console, cmdAddresses)); DCmd_Register("registers", WRAP_METHOD(Console, cmdRegisters)); DCmd_Register("dissect_script", WRAP_METHOD(Console, cmdDissectScript)); DCmd_Register("backtrace", WRAP_METHOD(Console, cmdBacktrace)); DCmd_Register("bt", WRAP_METHOD(Console, cmdBacktrace)); // alias DCmd_Register("trace", WRAP_METHOD(Console, cmdTrace)); DCmd_Register("t", WRAP_METHOD(Console, cmdTrace)); // alias DCmd_Register("s", WRAP_METHOD(Console, cmdTrace)); // alias DCmd_Register("stepover", WRAP_METHOD(Console, cmdStepOver)); DCmd_Register("p", WRAP_METHOD(Console, cmdStepOver)); // alias DCmd_Register("step_ret", WRAP_METHOD(Console, cmdStepRet)); DCmd_Register("pret", WRAP_METHOD(Console, cmdStepRet)); // alias DCmd_Register("step_event", WRAP_METHOD(Console, cmdStepEvent)); DCmd_Register("se", WRAP_METHOD(Console, cmdStepEvent)); // alias DCmd_Register("step_global", WRAP_METHOD(Console, cmdStepGlobal)); DCmd_Register("sg", WRAP_METHOD(Console, cmdStepGlobal)); // alias DCmd_Register("step_callk", WRAP_METHOD(Console, cmdStepCallk)); DCmd_Register("snk", WRAP_METHOD(Console, cmdStepCallk)); // alias DCmd_Register("disasm", WRAP_METHOD(Console, cmdDisassemble)); DCmd_Register("disasm_addr", WRAP_METHOD(Console, cmdDisassembleAddress)); DCmd_Register("send", WRAP_METHOD(Console, cmdSend)); DCmd_Register("go", WRAP_METHOD(Console, cmdGo)); DCmd_Register("logkernel", WRAP_METHOD(Console, cmdLogKernel)); // Breakpoints DCmd_Register("bp_list", WRAP_METHOD(Console, cmdBreakpointList)); DCmd_Register("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias DCmd_Register("bl", WRAP_METHOD(Console, cmdBreakpointList)); // alias DCmd_Register("bp_del", WRAP_METHOD(Console, cmdBreakpointDelete)); DCmd_Register("bpdel", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias DCmd_Register("bc", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias DCmd_Register("bp_method", WRAP_METHOD(Console, cmdBreakpointMethod)); DCmd_Register("bpx", WRAP_METHOD(Console, cmdBreakpointMethod)); // alias DCmd_Register("bp_read", WRAP_METHOD(Console, cmdBreakpointRead)); DCmd_Register("bpr", WRAP_METHOD(Console, cmdBreakpointRead)); // alias DCmd_Register("bp_write", WRAP_METHOD(Console, cmdBreakpointWrite)); DCmd_Register("bpw", WRAP_METHOD(Console, cmdBreakpointWrite)); // alias DCmd_Register("bp_kernel", WRAP_METHOD(Console, cmdBreakpointKernel)); DCmd_Register("bpk", WRAP_METHOD(Console, cmdBreakpointKernel)); // alias DCmd_Register("bp_function", WRAP_METHOD(Console, cmdBreakpointFunction)); DCmd_Register("bpe", WRAP_METHOD(Console, cmdBreakpointFunction)); // alias // VM DCmd_Register("script_steps", WRAP_METHOD(Console, cmdScriptSteps)); DCmd_Register("vm_varlist", WRAP_METHOD(Console, cmdVMVarlist)); DCmd_Register("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias DCmd_Register("vl", WRAP_METHOD(Console, cmdVMVarlist)); // alias DCmd_Register("vm_vars", WRAP_METHOD(Console, cmdVMVars)); DCmd_Register("vmvars", WRAP_METHOD(Console, cmdVMVars)); // alias DCmd_Register("vv", WRAP_METHOD(Console, cmdVMVars)); // alias DCmd_Register("stack", WRAP_METHOD(Console, cmdStack)); DCmd_Register("value_type", WRAP_METHOD(Console, cmdValueType)); DCmd_Register("view_listnode", WRAP_METHOD(Console, cmdViewListNode)); DCmd_Register("view_reference", WRAP_METHOD(Console, cmdViewReference)); DCmd_Register("vr", WRAP_METHOD(Console, cmdViewReference)); // alias DCmd_Register("view_object", WRAP_METHOD(Console, cmdViewObject)); DCmd_Register("vo", WRAP_METHOD(Console, cmdViewObject)); // alias DCmd_Register("active_object", WRAP_METHOD(Console, cmdViewActiveObject)); DCmd_Register("acc_object", WRAP_METHOD(Console, cmdViewAccumulatorObject)); _debugState.seeking = kDebugSeekNothing; _debugState.seekLevel = 0; _debugState.runningStep = 0; _debugState.stopOnEvent = false; _debugState.debugging = false; _debugState.breakpointWasHit = false; _debugState._breakpoints.clear(); // No breakpoints defined _debugState._activeBreakpointTypes = 0; } Console::~Console() { } void Console::preEnter() { if (g_sci && g_sci->_soundCmd) g_sci->_soundCmd->pauseAll(true); _enterTime = g_system->getMillis(); } void Console::postEnter() { if (g_sci && g_sci->_soundCmd) g_sci->_soundCmd->pauseAll(false); if (!_videoFile.empty()) { _engine->_gfxCursor->kernelHide(); Graphics::VideoDecoder *videoDecoder = 0; if (_videoFile.hasSuffix(".seq")) { SeqDecoder *seqDecoder = new SeqDecoder(); seqDecoder->setFrameDelay(_videoFrameDelay); videoDecoder = seqDecoder; #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { videoDecoder = new Graphics::VMDDecoder(g_system->getMixer()); #endif } else if (_videoFile.hasSuffix(".avi")) { videoDecoder = new Graphics::AviDecoder(g_system->getMixer()); } if (videoDecoder && videoDecoder->loadFile(_videoFile)) { uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; bool skipVideo = false; if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); g_system->updateScreen(); } } Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) skipVideo = true; } g_system->delayMillis(10); } delete videoDecoder; } else warning("Could not play video %s\n", _videoFile.c_str()); _engine->_gfxCursor->kernelShow(); _videoFile.clear(); _videoFrameDelay = 0; } // Subtract the time we were running the debugger from the game running time _engine->_gamestate->gameStartTime += g_system->getMillis() - _enterTime; } bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf("\n"); DebugPrintf("Variables\n"); DebugPrintf("---------\n"); DebugPrintf("sleeptime_factor: Factor to multiply with wait times in kWait()\n"); DebugPrintf("gc_interval: Number of kernel calls in between garbage collections\n"); DebugPrintf("simulated_key: Add a key with the specified scan code to the event list\n"); DebugPrintf("track_mouse_clicks: Toggles mouse click tracking to the console\n"); DebugPrintf("weak_validations: Turns some validation errors into warnings\n"); DebugPrintf("script_abort_flag: Set to 1 to abort script execution. Set to 2 to force a replay afterwards\n"); DebugPrintf("\n"); DebugPrintf("Debug flags\n"); DebugPrintf("-----------\n"); DebugPrintf("debugflag_list - Lists the available debug flags and their status\n"); DebugPrintf("debugflag_enable - Enables a debug flag\n"); DebugPrintf("debugflag_disable - Disables a debug flag\n"); DebugPrintf("\n"); DebugPrintf("Commands\n"); DebugPrintf("--------\n"); DebugPrintf("Kernel:\n"); DebugPrintf(" opcodes - Lists the opcode names\n"); DebugPrintf(" selectors - Lists the selector names\n"); DebugPrintf(" selector - Attempts to find the requested selector by name\n"); DebugPrintf(" functions - Lists the kernel functions\n"); DebugPrintf(" class_table - Shows the available classes\n"); DebugPrintf("\n"); DebugPrintf("Parser:\n"); DebugPrintf(" suffixes - Lists the vocabulary suffixes\n"); DebugPrintf(" parse_grammar - Shows the parse grammar, in strict GNF\n"); DebugPrintf(" parser_nodes - Shows the specified number of nodes from the parse node tree\n"); DebugPrintf(" parser_words - Shows the words from the parse node tree\n"); DebugPrintf(" sentence_fragments - Shows the sentence fragments (used to build Parse trees)\n"); DebugPrintf(" parse - Parses a sequence of words and prints the resulting parse tree\n"); DebugPrintf(" set_parse_nodes - Sets the contents of all parse nodes\n"); DebugPrintf(" said - Match a string against a said spec\n"); DebugPrintf("\n"); DebugPrintf("Resources:\n"); DebugPrintf(" diskdump - Dumps the specified resource to disk as a patch file\n"); DebugPrintf(" hexdump - Dumps the specified resource to standard output\n"); DebugPrintf(" resource_id - Identifies a resource number by splitting it up in resource type and resource number\n"); DebugPrintf(" resource_size - Shows the size of a resource\n"); DebugPrintf(" resource_types - Shows the valid resource types\n"); DebugPrintf(" list - Lists all the resources of a given type\n"); DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n"); DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n"); DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n"); DebugPrintf("\n"); DebugPrintf("Game:\n"); DebugPrintf(" save_game - Saves the current game state to the hard disk\n"); DebugPrintf(" restore_game - Restores a saved game from the hard disk\n"); DebugPrintf(" list_saves - List all saved games including filenames\n"); DebugPrintf(" restart_game - Restarts the game\n"); DebugPrintf(" version - Shows the resource and interpreter versions\n"); DebugPrintf(" room - Gets or sets the current room number\n"); DebugPrintf(" quit - Quits the game\n"); DebugPrintf("\n"); DebugPrintf("Graphics:\n"); DebugPrintf(" show_map - Switches to visual, priority, control or display screen\n"); DebugPrintf(" set_palette - Sets a palette resource\n"); DebugPrintf(" draw_pic - Draws a pic resource\n"); DebugPrintf(" draw_cel - Draws a cel from a view resource\n"); DebugPrintf(" pic_visualize - Enables visualization of the drawing process of EGA pictures\n"); DebugPrintf(" undither - Enable/disable undithering\n"); DebugPrintf("\n"); DebugPrintf("Segments:\n"); DebugPrintf(" segment_table / segtable - Lists all segments\n"); DebugPrintf(" segment_info / seginfo - Provides information on the specified segment\n"); DebugPrintf(" segment_kill / segkill - Deletes the specified segment\n"); DebugPrintf("\n"); DebugPrintf("Garbage collection:\n"); DebugPrintf(" gc - Invokes the garbage collector\n"); DebugPrintf(" gc_objects - Lists all reachable objects, normalized\n"); DebugPrintf(" gc_reachable - Lists all addresses directly reachable from a given memory object\n"); DebugPrintf(" gc_freeable - Lists all addresses freeable in a given segment\n"); DebugPrintf(" gc_normalize - Prints the \"normal\" address of a given address\n"); DebugPrintf("\n"); DebugPrintf("Music/SFX:\n"); DebugPrintf(" songlib - Shows the song library\n"); DebugPrintf(" songinfo - Shows information about a specified song in the song library\n"); DebugPrintf(" togglesound - Starts/stops a sound in the song library\n"); DebugPrintf(" stopallsounds - Stops all sounds in the playlist\n"); DebugPrintf(" startsound - Starts the specified sound resource, replacing the first song in the song library\n"); DebugPrintf(" is_sample - Shows information on a given sound resource, if it's a PCM sample\n"); DebugPrintf(" sfx01_header - Dumps the header of a SCI01 song\n"); DebugPrintf(" sfx01_track - Dumps a track of a SCI01 song\n"); DebugPrintf("\n"); DebugPrintf("Script:\n"); DebugPrintf(" addresses - Provides information on how to pass addresses\n"); DebugPrintf(" registers - Shows the current register values\n"); DebugPrintf(" dissect_script - Examines a script\n"); DebugPrintf(" backtrace / bt - Dumps the send/self/super/call/calle/callb stack\n"); DebugPrintf(" trace / t / s - Executes one operation (no parameters) or several operations (specified as a parameter) \n"); DebugPrintf(" stepover / p - Executes one operation, skips over call/send\n"); DebugPrintf(" step_ret / pret - Steps forward until ret is called on the current execution stack level.\n"); DebugPrintf(" step_event / se - Steps forward until a SCI event is received.\n"); DebugPrintf(" step_global / sg - Steps until the global variable with the specified index is modified.\n"); DebugPrintf(" step_callk / snk - Steps forward until it hits the next callk operation, or a specific callk (specified as a parameter)\n"); DebugPrintf(" disasm - Disassembles a method by name\n"); DebugPrintf(" disasm_addr - Disassembles one or more commands\n"); DebugPrintf(" send - Sends a message to an object\n"); DebugPrintf(" go - Executes the script\n"); DebugPrintf(" logkernel - Logs kernel calls\n"); DebugPrintf("\n"); DebugPrintf("Breakpoints:\n"); DebugPrintf(" bp_list / bplist / bl - Lists the current breakpoints\n"); DebugPrintf(" bp_del / bpdel / bc - Deletes a breakpoint with the specified index\n"); DebugPrintf(" bp_method / bpx - Sets a breakpoint on the execution of a specified method/selector\n"); DebugPrintf(" bp_read / bpr - Sets a breakpoint on reading of a specified selector\n"); DebugPrintf(" bp_write / bpw - Sets a breakpoint on writing to a specified selector\n"); DebugPrintf(" bp_kernel / bpk - Sets a breakpoint on execution of a kernel function\n"); DebugPrintf(" bp_function / bpe - Sets a breakpoint on the execution of the specified exported function\n"); DebugPrintf("\n"); DebugPrintf("VM:\n"); DebugPrintf(" script_steps - Shows the number of executed SCI operations\n"); DebugPrintf(" vm_varlist / vmvarlist / vl - Shows the addresses of variables in the VM\n"); DebugPrintf(" vm_vars / vmvars / vv - Displays or changes variables in the VM\n"); DebugPrintf(" stack - Lists the specified number of stack elements\n"); DebugPrintf(" value_type - Determines the type of a value\n"); DebugPrintf(" view_listnode - Examines the list node at the given address\n"); DebugPrintf(" view_reference / vr - Examines an arbitrary reference\n"); DebugPrintf(" view_object / vo - Examines the object at the given address\n"); DebugPrintf(" active_object - Shows information on the currently active object or class\n"); DebugPrintf(" acc_object - Shows information on the object or class at the address indexed by the accumulator\n"); DebugPrintf("\n"); return true; } ResourceType parseResourceType(const char *resid) { // Gets the resource number of a resource string, or returns -1 ResourceType res = kResourceTypeInvalid; for (int i = 0; i < kResourceTypeInvalid; i++) if (strcmp(getResourceTypeName((ResourceType)i), resid) == 0) res = (ResourceType)i; return res; } bool Console::cmdGetVersion(int argc, const char **argv) { const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" }; bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false; Common::String gameVersion = "N/A"; Common::File versionFile; if (versionFile.open("VERSION")) { gameVersion = versionFile.readLine(); versionFile.close(); } DebugPrintf("Game ID: %s\n", _engine->getGameIdStr()); DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion())); DebugPrintf("\n"); DebugPrintf("Detected features:\n"); DebugPrintf("------------------\n"); DebugPrintf("Sound type: %s\n", getSciVersionDesc(_engine->_features->detectDoSoundType())); DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(_engine->_features->detectGfxFunctionsType())); DebugPrintf("Lofs type: %s\n", getSciVersionDesc(_engine->_features->detectLofsType())); DebugPrintf("Move count type: %s\n", (_engine->_features->handleMoveCount()) ? "increment" : "ignore"); DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(_engine->_features->detectSetCursorType())); DebugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]); DebugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette->isMerging() ? "yes" : "no"); DebugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc()); DebugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc()); DebugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no"); DebugPrintf("Has CantBeHere selector: %s\n", g_sci->getKernel()->_selectorCache.cantBeHere != -1 ? "yes" : "no"); DebugPrintf("Game version (VERSION file): %s\n", gameVersion.c_str()); DebugPrintf("\n"); return true; } bool Console::cmdOpcodes(int argc, const char **argv) { // Load the opcode table from vocab.998 if it exists, to obtain the opcode names Resource *r = _engine->getResMan()->findResource(ResourceId(kResourceTypeVocab, 998), 0); // If the resource couldn't be loaded, leave if (!r) { DebugPrintf("unable to load vocab.998"); return true; } int count = READ_LE_UINT16(r->data); DebugPrintf("Opcode names in numeric order [index: type name]:\n"); for (int i = 0; i < count; i++) { int offset = READ_LE_UINT16(r->data + 2 + i * 2); int len = READ_LE_UINT16(r->data + offset) - 2; int type = READ_LE_UINT16(r->data + offset + 2); // QFG3 has empty opcodes Common::String name = len > 0 ? Common::String((const char *)r->data + offset + 4, len) : "Dummy"; DebugPrintf("%03x: %03x %20s | ", i, type, name.c_str()); if ((i % 3) == 2) DebugPrintf("\n"); } DebugPrintf("\n"); return true; } bool Console::cmdSelector(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Attempts to find the requested selector by name.\n"); DebugPrintf("Usage: %s \n", argv[0]); return true; } Common::String name = argv[1]; int seeker = _engine->getKernel()->findSelector(name.c_str()); if (seeker >= 0) { DebugPrintf("Selector %s found at %03x (%d)\n", name.c_str(), seeker, seeker); return true; } DebugPrintf("Selector %s wasn't found\n", name.c_str()); return true; } bool Console::cmdSelectors(int argc, const char **argv) { DebugPrintf("Selector names in numeric order:\n"); Common::String selectorName; for (uint seeker = 0; seeker < _engine->getKernel()->getSelectorNamesSize(); seeker++) { selectorName = _engine->getKernel()->getSelectorName(seeker); if (selectorName != "BAD SELECTOR") DebugPrintf("%03x: %20s | ", seeker, selectorName.c_str()); else continue; if ((seeker % 3) == 2) DebugPrintf("\n"); } DebugPrintf("\n"); #if 0 // For debug/development // If we ever need to modify static_selectors.cpp, this code will print the selectors // in a ready to use format Common::DumpFile *outFile = new Common::DumpFile(); outFile->open("selectors.txt"); char buf[50]; Common::String selName; uint totalSize = _engine->getKernel()->getSelectorNamesSize(); uint seeker = 0; while (seeker < totalSize) { selName = "\"" + _engine->getKernel()->getSelectorName(seeker) + "\""; sprintf(buf, "%15s, ", selName.c_str()); outFile->writeString(buf); if (!((seeker + 1) % 5) && seeker) outFile->writeByte('\n'); seeker++; } outFile->finalize(); outFile->close(); #endif return true; } bool Console::cmdKernelFunctions(int argc, const char **argv) { DebugPrintf("Kernel function names in numeric order:\n"); for (uint seeker = 0; seeker < _engine->getKernel()->getKernelNamesSize(); seeker++) { DebugPrintf("%03x: %20s | ", seeker, _engine->getKernel()->getKernelName(seeker).c_str()); if ((seeker % 3) == 2) DebugPrintf("\n"); } DebugPrintf("\n"); return true; } bool Console::cmdSuffixes(int argc, const char **argv) { _engine->getVocabulary()->printSuffixes(); return true; } bool Console::cmdParserWords(int argc, const char **argv) { _engine->getVocabulary()->printParserWords(); return true; } bool Console::cmdSetParseNodes(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Sets the contents of all parse nodes.\n"); DebugPrintf("Usage: %s ... \n", argv[0]); DebugPrintf("Tokens should be separated by blanks and enclosed in parentheses\n"); return true; } int i = 0; int pos = -1; int nextToken = 0, nextValue = 0; const char *token = argv[i++]; if (!strcmp(token, "(")) { nextToken = kParseOpeningParenthesis; } else if (!strcmp(token, ")")) { nextToken = kParseClosingParenthesis; } else if (!strcmp(token, "nil")) { nextToken = kParseNil; } else { nextValue = strtol(token, NULL, 0); nextToken = kParseNumber; } if (_engine->getVocabulary()->parseNodes(&i, &pos, nextToken, nextValue, argc, argv) == -1) return 1; _engine->getVocabulary()->dumpParseTree(); return true; } bool Console::cmdRegisters(int argc, const char **argv) { EngineState *s = _engine->_gamestate; DebugPrintf("Current register values:\n"); DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(s->r_acc), PRINT_REG(s->r_prev), s->restAdjust); if (!s->_executionStack.empty()) { DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n", PRINT_REG(s->xs->addr.pc), PRINT_REG(s->xs->objp), (unsigned)(s->xs->fp - s->stack_base), (unsigned)(s->xs->sp - s->stack_base)); } else DebugPrintf("\n"); return true; } bool Console::cmdDiskDump(int argc, const char **argv) { if (argc != 3) { DebugPrintf("Dumps the specified resource to disk as a patch file\n"); DebugPrintf("Usage: %s \n", argv[0]); cmdResourceTypes(argc, argv); return true; } int resNum = atoi(argv[2]); ResourceType res = parseResourceType(argv[1]); if (res == kResourceTypeInvalid) DebugPrintf("Resource type '%s' is not valid\n", argv[1]); else { Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNum), 0); if (resource) { char outFileName[50]; sprintf(outFileName, "%s.%03d", getResourceTypeName(res), resNum); Common::DumpFile *outFile = new Common::DumpFile(); outFile->open(outFileName); resource->writeToStream(outFile); outFile->finalize(); outFile->close(); delete outFile; DebugPrintf("Resource %s.%03d has been dumped to disk\n", argv[1], resNum); } else { DebugPrintf("Resource %s.%03d not found\n", argv[1], resNum); } } return true; } bool Console::cmdHexDump(int argc, const char **argv) { if (argc != 3) { DebugPrintf("Dumps the specified resource to standard output\n"); DebugPrintf("Usage: %s \n", argv[0]); cmdResourceTypes(argc, argv); return true; } int resNum = atoi(argv[2]); ResourceType res = parseResourceType(argv[1]); if (res == kResourceTypeInvalid) DebugPrintf("Resource type '%s' is not valid\n", argv[1]); else { Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNum), 0); if (resource) { Common::hexdump(resource->data, resource->size, 16, 0); DebugPrintf("Resource %s.%03d has been dumped to standard output\n", argv[1], resNum); } else { DebugPrintf("Resource %s.%03d not found\n", argv[1], resNum); } } return true; } bool Console::cmdResourceId(int argc, const char **argv) { if (argc != 2) { DebugPrintf("Identifies a resource number by splitting it up in resource type and resource number\n"); DebugPrintf("Usage: %s \n", argv[0]); return true; } int id = atoi(argv[1]); DebugPrintf("%s.%d (0x%x)\n", getResourceTypeName((ResourceType)(id >> 11)), id & 0x7ff, id & 0x7ff); return true; } bool Console::cmdDissectScript(int argc, const char **argv) { if (argc != 2) { DebugPrintf("Examines a script\n"); DebugPrintf("Usage: %s