/* 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/resource.h"
#include "sci/vocabulary.h"
#include "sci/engine/savegame.h"
#include "sci/engine/state.h"
#include "sci/engine/gc.h"
#include "sci/engine/kernel_types.h"	// for determine_reg_type
#include "sci/gfx/gfx_gui.h"	// for sciw_set_status_bar
#include "sci/gfx/gfx_state_internal.h"
#include "sci/gfx/gfx_widgets.h"	// for getPort
#include "sci/sfx/songlib.h"	// for SongLibrary
#include "sci/sfx/iterator.h"	// for SCI_SONG_ITERATOR_TYPE_SCI0
#include "sci/sfx/sci_midi.h"
#include "sci/vocabulary.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;
bool g_debug_weak_validations = true;
// Script related variables
int g_debug_seeking = 0; // Stepping forward until some special condition is met
int g_debug_seek_special = 0;  // Used for special seeks
int g_debug_seek_level = 0; // Used for seekers that want to check their exec stack depth

enum DebugSeeking {
	kDebugSeekNothing = 0,
	kDebugSeekCallk = 1,        // Step forward until callk is found
	kDebugSeekLevelRet = 2,     // Step forward until returned from this level
	kDebugSeekSpecialCallk = 3, // Step forward until a /special/ callk is found
	kDebugSeekSO = 4,           // Step forward until specified PC (after the send command) and stack depth
	kDebugSeekGlobal = 5        // Step forward until one specified global variable is modified
};

Console::Console(SciEngine *vm) : GUI::Debugger() {
	_vm = vm;

	// Variables
	DVar_Register("sleeptime_factor",	&g_debug_sleeptime_factor, DVAR_INT, 0);
	DVar_Register("gc_interval",		&script_gc_interval, 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("weak_validations",	&g_debug_weak_validations, DVAR_BOOL, 0);
	DVar_Register("script_abort_flag",	&script_abort_flag, 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("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));
	// Resources
	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));
	// 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("exit",				WRAP_METHOD(Console, cmdExit));
	// Screen
	DCmd_Register("sci0_palette",		WRAP_METHOD(Console, cmdSci0Palette));
	DCmd_Register("clear_screen",		WRAP_METHOD(Console, cmdClearScreen));
	DCmd_Register("redraw_screen",		WRAP_METHOD(Console, cmdRedrawScreen));
	DCmd_Register("fill_screen",		WRAP_METHOD(Console, cmdFillScreen));
	DCmd_Register("show_map",			WRAP_METHOD(Console, cmdShowMap));
	DCmd_Register("update_zone",		WRAP_METHOD(Console, cmdUpdateZone));
	DCmd_Register("propagate_zone",		WRAP_METHOD(Console, cmdPropagateZone));
	DCmd_Register("priority_bands",		WRAP_METHOD(Console, cmdPriorityBands));
	// Graphics
	DCmd_Register("draw_pic",			WRAP_METHOD(Console, cmdDrawPic));
	DCmd_Register("draw_rect",			WRAP_METHOD(Console, cmdDrawRect));
	DCmd_Register("draw_cel",			WRAP_METHOD(Console, cmdDrawCel));
	DCmd_Register("view_info",			WRAP_METHOD(Console, cmdViewInfo));
	// GUI
	DCmd_Register("current_port",		WRAP_METHOD(Console, cmdCurrentPort));
	DCmd_Register("print_port",			WRAP_METHOD(Console, cmdPrintPort));
	DCmd_Register("visual_state",		WRAP_METHOD(Console, cmdVisualState));
	DCmd_Register("flush_visual",		WRAP_METHOD(Console, cmdFlushPorts));
	DCmd_Register("dynamic_views",		WRAP_METHOD(Console, cmdDynamicViews));
	DCmd_Register("dropped_views",		WRAP_METHOD(Console, cmdDroppedViews));
	DCmd_Register("status_bar",			WRAP_METHOD(Console, cmdStatusBarColors));
#ifdef GFXW_DEBUG_WIDGETS
	DCmd_Register("print_widget",		WRAP_METHOD(Console, cmdPrintWidget));
#endif
	// Segments
	DCmd_Register("segment_table",		WRAP_METHOD(Console, cmdPrintSegmentTable));
	DCmd_Register("segment_info",		WRAP_METHOD(Console, cmdSegmentInfo));
	DCmd_Register("segment_kill",		WRAP_METHOD(Console, cmdKillSegment));
	// 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("is_sample",			WRAP_METHOD(Console, cmdIsSample));
	DCmd_Register("sfx01_header",		WRAP_METHOD(Console, cmdSfx01Header));
	DCmd_Register("sfx01_track",		WRAP_METHOD(Console, cmdSfx01Track));
	DCmd_Register("stop_sfx",			WRAP_METHOD(Console, cmdStopSfx));
	// 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("set_acc",			WRAP_METHOD(Console, cmdSetAccumulator));
	DCmd_Register("backtrace",			WRAP_METHOD(Console, cmdBacktrace));
	DCmd_Register("step",				WRAP_METHOD(Console, cmdStep));
	DCmd_Register("step_event",			WRAP_METHOD(Console, cmdStepEvent));
	DCmd_Register("step_ret",			WRAP_METHOD(Console, cmdStepRet));
	DCmd_Register("step_global",		WRAP_METHOD(Console, cmdStepGlobal));
	DCmd_Register("step_callk",			WRAP_METHOD(Console, cmdStepCallk));
	DCmd_Register("disasm",				WRAP_METHOD(Console, cmdDissassemble));
	DCmd_Register("disasm_addr",		WRAP_METHOD(Console, cmdDissassembleAddress));
	DCmd_Register("send",				WRAP_METHOD(Console, cmdSend));
	DCmd_Register("go",					WRAP_METHOD(Console, cmdGo));
	// Breakpoints
	DCmd_Register("bp_list",			WRAP_METHOD(Console, cmdBreakpointList));
	DCmd_Register("bp_del",				WRAP_METHOD(Console, cmdBreakpointDelete));
	DCmd_Register("bp_exec_method",		WRAP_METHOD(Console, cmdBreakpointExecMethod));
	DCmd_Register("bp_exec_function",	WRAP_METHOD(Console, cmdBreakpointExecFunction));
	// VM
	DCmd_Register("script_steps",		WRAP_METHOD(Console, cmdScriptSteps));
	DCmd_Register("vm_varlist",			WRAP_METHOD(Console, cmdVMVarlist));
	DCmd_Register("vm_vars",			WRAP_METHOD(Console, cmdVMVars));
	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("view_object",		WRAP_METHOD(Console, cmdViewObject));
	DCmd_Register("active_object",		WRAP_METHOD(Console, cmdViewActiveObject));
	DCmd_Register("acc_object",			WRAP_METHOD(Console, cmdViewAccumulatorObject));

	// These were in sci.cpp
	/*
	con_hook_int(&(gfx_options.buffer_pics_nr), "buffer_pics_nr",
		"Number of pics to buffer in LRU storage\n");
	con_hook_int(&(gfx_options.pic0_dither_mode), "pic0_dither_mode",
		"Mode to use for pic0 dithering\n");
	con_hook_int(&(gfx_options.pic0_dither_pattern), "pic0_dither_pattern",
		"Pattern to use for pic0 dithering\n");
	con_hook_int(&(gfx_options.pic0_unscaled), "pic0_unscaled",
		"Whether pic0 should be drawn unscaled\n");
	con_hook_int(&(gfx_options.dirty_frames), "dirty_frames",
		"Dirty frames management\n");
	*/
}

Console::~Console() {
}

void Console::preEnter() {
	_vm->_gamestate->_sound.sfx_suspend(true);
	_vm->_mixer->pauseAll(true);
}

void Console::postEnter() {
	_vm->_gamestate->_sound.sfx_suspend(false);
	_vm->_mixer->pauseAll(false);
}


#if 0
// Unused
#define LOOKUP_SPECIES(species) (\
	(species >= 1000) ? species : *(s->_classtable[species].scriptposp) \
		+ s->_classtable[species].class_offset)
#endif

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("Commands\n");
	DebugPrintf("--------\n");
	DebugPrintf("Kernel:\n");
	DebugPrintf(" opcodes - Lists the opcode names\n");
	DebugPrintf(" selectors - Lists the selector names\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("\n");
	DebugPrintf("Resources:\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("\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(" restart_game - Restarts the game\n");
	DebugPrintf(" version - Shows the resource and interpreter versions\n");
	DebugPrintf(" room - Shows the current room number\n");
	DebugPrintf(" exit - Exits the game\n");
	DebugPrintf("\n");
	DebugPrintf("Screen:\n");
	DebugPrintf(" sci0_palette - Sets the SCI0 palette to use (EGA, Amiga or grayscale)\n");
	DebugPrintf(" clear_screen - Clears the screen\n");
	DebugPrintf(" redraw_screen - Redraws the screen\n");
	DebugPrintf(" fill_screen - Fills the screen with one of the EGA colors\n");
	DebugPrintf(" show_map - Shows one of the screen maps (visual, priority or control)\n");
	DebugPrintf(" update_zone - Propagates a rectangular area from the back buffer to the front buffer\n");
	DebugPrintf(" propagate_zone - Propagates a rectangular area from a lower graphics buffer to a higher one\n");
	DebugPrintf(" priority_bands - Shows information about priority bands\n");
	DebugPrintf("\n");
	DebugPrintf("Graphics:\n");
	DebugPrintf(" draw_pic - Draws a pic resource\n");
	DebugPrintf(" draw_rect - Draws a rectangle to the screen with one of the EGA colors\n");
	DebugPrintf(" draw_cel - Draws a single view cel to the center of the screen\n");
	DebugPrintf(" view_info - Displays information for the specified view\n");
	DebugPrintf("\n");
	DebugPrintf("GUI:\n");
	DebugPrintf(" current_port - Shows the ID of the currently active port\n");
	DebugPrintf(" print_port - Prints information about a port\n");
	DebugPrintf(" visual_state - Shows the state of the current visual widget\n");
	DebugPrintf(" flush_visual - Flushes dynamically allocated ports (for memory profiling)\n");
	DebugPrintf(" dynamic_views - Lists active dynamic views\n");
	DebugPrintf(" dropped_views - Lists dropped dynamic views\n");
	DebugPrintf(" status_bar - Sets the colors of the status bar\n");
#ifdef GFXW_DEBUG_WIDGETS
	DebugPrintf(" print_widget - Shows active widgets (no params) or information on the specified widget indices\n");
#endif
	DebugPrintf("\n");
	DebugPrintf("Segments:\n");
	DebugPrintf(" segment_table - Lists all segments\n");
	DebugPrintf(" segment_info - Provides information on the specified segment\n");
	DebugPrintf(" segment_kill - 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(" 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(" stop_sfx - Stops a playing sound\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(" set_acc - Sets the accumulator\n");
	DebugPrintf(" backtrace - Dumps the send/self/super/call/calle/callb stack\n");
	DebugPrintf(" step - Executes one operation (no parameters) or several operations (specified as a parameter) \n");
	DebugPrintf(" step_event - Steps forward until a SCI event is received.\n");
	DebugPrintf(" step_ret - Steps forward until ret is called on the current execution stack level.\n");
	DebugPrintf(" step_global - Steps until the global variable with the specified index is modified.\n");
	DebugPrintf(" step_callk - 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("\n");
	DebugPrintf("Breakpoints:\n");
	DebugPrintf(" bp_list - Lists the current breakpoints\n");
	DebugPrintf(" bp_del - Deletes a breakpoint with the specified index\n");
	DebugPrintf(" bp_exec_method - Sets a breakpoint on the execution of the specified method\n");
	DebugPrintf(" bp_exec_function - 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 - Shows the addresses of variables in the VM\n");
	DebugPrintf(" vm_vars - 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 - Examines an arbitrary reference\n");
	DebugPrintf(" view_object - 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;
}

const char *selector_name(EngineState *s, int selector) {
	if (selector >= 0 && selector < (int)s->_kernel->getSelectorNamesSize())
		return s->_kernel->getSelectorName(selector).c_str();
	else
		return "--INVALID--";
}

bool Console::cmdGetVersion(int argc, const char **argv) {
	int ver = _vm->getVersion();

	DebugPrintf("Resource file version:        %s\n", sci_version_types[_vm->getResMgr()->_sciVersion]);
	DebugPrintf("Emulated interpreter version: %s\n", versionNames[ver]);

	return true;
}

bool Console::cmdOpcodes(int argc, const char **argv) {
	DebugPrintf("Opcode names in numeric order [index: type name]:\n");
	for (uint seeker = 0; seeker < _vm->_gamestate->_kernel->getOpcodesSize(); seeker++) {
		opcode op = _vm->_gamestate->_kernel->getOpcode(seeker);
		DebugPrintf("%03x: %03x %20s | ", seeker, op.type, op.name.c_str());
		if ((seeker % 3) == 2)
			DebugPrintf("\n");
	}

	DebugPrintf("\n");

	return true;
}

bool Console::cmdSelectors(int argc, const char **argv) {
	DebugPrintf("Selector names in numeric order:\n");
	for (uint seeker = 0; seeker < _vm->_gamestate->_kernel->getSelectorNamesSize(); seeker++) {
		DebugPrintf("%03x: %20s | ", seeker, _vm->_gamestate->_kernel->getSelectorName(seeker).c_str());
		if ((seeker % 3) == 2)
			DebugPrintf("\n");
	}

	DebugPrintf("\n");

	return true;
}

bool Console::cmdKernelFunctions(int argc, const char **argv) {
	DebugPrintf("Kernel function names in numeric order:\n");
	for (uint seeker = 0; seeker <  _vm->_gamestate->_kernel->getKernelNamesSize(); seeker++) {
		DebugPrintf("%03x: %20s | ", seeker, _vm->_gamestate->_kernel->getKernelName(seeker).c_str());
		if ((seeker % 3) == 2)
			DebugPrintf("\n");
	}

	DebugPrintf("\n");

	return true;
}

bool Console::cmdSuffixes(int argc, const char **argv) {
	_vm->_gamestate->_vocabulary->printSuffixes();

	return true;
}

bool Console::cmdParserWords(int argc, const char **argv) {
	_vm->_gamestate->_vocabulary->printParserWords();

	return true;
}

enum {
	kParseEndOfInput = 0,
	kParseOpeningParenthesis = 1,
	kParseClosingParenthesis = 2,
	kParseNil = 3,
	kParseNumber = 4
};

int parseNodes(EngineState *s, int *i, int *pos, int type, int nr, int argc, const char **argv) {
	int nextToken = 0, nextValue = 0, newPos = 0, oldPos = 0;

	if (type == kParseNil)
		return 0;

	if (type == kParseNumber) {
		s->parser_nodes[*pos += 1].type = kParseTreeLeafNode;
		s->parser_nodes[*pos].content.value = nr;
		return *pos;
	}
	if (type == kParseEndOfInput) {
		sciprintf("Unbalanced parentheses\n");
		return -1;
	}
	if (type == kParseClosingParenthesis) {
		sciprintf("Syntax error at token %d\n", *i);
		return -1;
	}

	s->parser_nodes[oldPos = ++(*pos)].type = kParseTreeBranchNode;

	for (int j = 0; j <= 1; j++) {
		if (*i == argc) {
			nextToken = kParseEndOfInput;
		} else {
			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 ((newPos = s->parser_nodes[oldPos].content.branches[j] = parseNodes(s, i, pos, nextToken, nextValue, argc, argv)) == -1)
			return -1;
	}

	const char *token = argv[(*i)++];
	if (strcmp(token, ")"))
		sciprintf("Expected ')' at token %d\n", *i);

	return oldPos;
}

bool Console::cmdSetParseNodes(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Sets the contents of all parse nodes.\n");
		DebugPrintf("Usage: %s <parse node1> <parse node2> ... <parse noden>\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 (parseNodes(_vm->_gamestate, &i, &pos, nextToken, nextValue, argc, argv) == -1)
		return 1;

	vocab_dump_parse_tree("debug-parse-tree", _vm->_gamestate->parser_nodes);

	return true;
}

bool Console::cmdRegisters(int argc, const char **argv) {
	DebugPrintf("Current register values:\n");
#if 0
		// TODO: p_restadjust
	DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(_vm->_gamestate->r_acc), PRINT_REG(_vm->_gamestate->r_prev), *p_restadjust);
#endif

	if (!_vm->_gamestate->_executionStack.empty()) {
#if 0
		// TODO: p_pc, p_objp, p_pp, p_sp
		DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n", PRINT_REG(*p_pc), PRINT_REG(*p_objp), PRINT_STK(*p_pp), PRINT_STK(*p_sp));
#endif
	} else
		DebugPrintf("<no execution stack: pc,obj,fp omitted>\n");

	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 <resource type> <resource number>\n", argv[0]);
		cmdResourceTypes(argc, argv);
		return true;
	}

	int resNum = atoi(argv[2]);
	if (resNum == 0) {
		DebugPrintf("The resource number specified is not a number");
		return true;
	}

	ResourceType res = parseResourceType(argv[1]);

	if (res == kResourceTypeInvalid)
		DebugPrintf("Resource type '%s' is not valid\n", argv[1]);
	else {
		Resource *resource = _vm->getResMgr()->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 <resource number>\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 <script number>\n", argv[0]);
		return true;
	}

	_vm->_gamestate->_kernel->dissectScript(atoi(argv[1]), _vm->_gamestate->_vocabulary);

	return true;
}

bool Console::cmdRoomNumber(int argc, const char **argv) {
	DebugPrintf("Current room number is %d\n", _vm->_gamestate->currentRoomNumber());

	return true;
}

bool Console::cmdResourceSize(int argc, const char **argv) {
	if (argc != 3) {
		DebugPrintf("Shows the size of a resource\n");
		DebugPrintf("Usage: %s <resource type> <resource number>\n", argv[0]);
		return true;
	}

	int resNum = atoi(argv[2]);
	if (resNum == 0) {
		DebugPrintf("The resource number specified is not a number");
		return true;
	}

	ResourceType res = parseResourceType(argv[1]);

	if (res == kResourceTypeInvalid)
		DebugPrintf("Resource type '%s' is not valid\n", argv[1]);
	else {
		Resource *resource = _vm->getResMgr()->findResource(ResourceId(res, resNum), 0);
		if (resource) {
			DebugPrintf("Resource size: %d\n", resource->size);
		} else {
			DebugPrintf("Resource %s.%03d not found\n", argv[1], resNum);
		}
	}

	return true;
}

bool Console::cmdResourceTypes(int argc, const char **argv) {
	DebugPrintf("The %d valid resource types are:\n", kResourceTypeInvalid);
	for (int i = 0; i < kResourceTypeInvalid; i++) {
		DebugPrintf("%s", getResourceTypeName((ResourceType) i));
		DebugPrintf((i < kResourceTypeInvalid - 1) ? ", " : "\n");
	}

	return true;
}

extern int sci0_palette;

bool Console::cmdSci0Palette(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Sets the SCI0 palette to use - 0: EGA, 1: AGI/Amiga, 2: Grayscale\n");
		return true;
	}

	sci0_palette = atoi(argv[1]);
	cmdRedrawScreen(argc, argv);

	return false;
}

bool Console::cmdHexgrep(int argc, const char **argv) {
	if (argc < 4) {
		DebugPrintf("Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers.\n");
		DebugPrintf("Usage: %s <resource type> <resource number> <search string>\n", argv[0]);
		DebugPrintf("<resource number> can be a specific resource number, or \"all\" for all of the resources of the specified type\n", argv[0]);
		DebugPrintf("EXAMPLES:\n  hexgrep script all e8 03 c8 00\n  hexgrep pic 042 fe");
		cmdResourceTypes(argc, argv);
		return true;
	}

	ResourceType restype = parseResourceType(argv[1]);
	int resNumber = 0, resMax = 0;
	char seekString[500];
	Resource *script = NULL;

	if (restype == kResourceTypeInvalid) {
		DebugPrintf("Resource type '%s' is not valid\n", argv[1]);
		return true;
	}

	if (!scumm_stricmp(argv[2], "all")) {
		resNumber = 0;
		resMax = 999;
	} else {
		resNumber = resMax = atoi(argv[2]);
	}

	strcpy(seekString, argv[3]);

	// Construct the seek string
	for (int i = 4; i < argc; i++) {
		strcat(seekString, argv[i]);
	}

	for (; resNumber <= resMax; resNumber++) {
		if ((script = _vm->getResMgr()->findResource(ResourceId(restype, resNumber), 0))) {
			unsigned int seeker = 0, seekerold = 0;
			uint32 comppos = 0;
			int output_script_name = 0;

			while (seeker < script->size) {
				if (script->data[seeker] == seekString[comppos]) {
					if (comppos == 0)
						seekerold = seeker;

					comppos++;

					if (comppos == strlen(seekString)) {
						comppos = 0;
						seeker = seekerold + 1;

						if (!output_script_name) {
							DebugPrintf("\nIn %s.%03d:\n", getResourceTypeName((ResourceType)restype), resNumber);
							output_script_name = 1;
						}
						DebugPrintf("   0x%04x\n", seekerold);
					}
				} else
					comppos = 0;

				seeker++;
			}
		}
	}

	return true;
}

bool Console::cmdList(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Lists all the resources of a given type\n");
		cmdResourceTypes(argc, argv);
		return true;
	}


	ResourceType res = parseResourceType(argv[1]);
	if (res == kResourceTypeInvalid)
		DebugPrintf("Unknown resource type: '%s'\n", argv[1]);
	else {
		int number = -1;

		if ((res == kResourceTypeAudio36) || (res == kResourceTypeSync36)) {
			if (argc != 3) {
				DebugPrintf("Please specify map number\n");
				return true;
			}
			number = atoi(argv[2]);
		}

		Common::List<ResourceId> *resources = _vm->getResMgr()->listResources(res, number);
		sort(resources->begin(), resources->end(), ResourceIdLess());
		Common::List<ResourceId>::iterator itr = resources->begin();

		int cnt = 0;
		while (itr != resources->end()) {
			if (number == -1) {
				DebugPrintf("%8i", itr->number);
				if (++cnt % 10 == 0)
					DebugPrintf("\n");
			}
			else if (number == (int)itr->number) {
				DebugPrintf("(%3i, %3i, %3i, %3i)   ", (itr->tuple >> 24) & 0xff, (itr->tuple >> 16) & 0xff,
							(itr->tuple >> 8) & 0xff, itr->tuple & 0xff);
				if (++cnt % 4 == 0)
					DebugPrintf("\n");
			}
			itr++;
		}
		DebugPrintf("\n");

		delete resources;
	}

	return true;
}

bool Console::cmdClearScreen(int argc, const char **argv) {
	gfxop_clear_box(_vm->_gamestate->gfx_state, gfx_rect(0, 0, 320, 200));
	gfxop_update_box(_vm->_gamestate->gfx_state, gfx_rect(0, 0, 320, 200));
	return false;
}

bool Console::cmdRedrawScreen(int argc, const char **argv) {
	_vm->_gamestate->visual->draw(Common::Point(0, 0));
	gfxop_update_box(_vm->_gamestate->gfx_state, gfx_rect(0, 0, 320, 200));
	gfxop_update(_vm->_gamestate->gfx_state);
	gfxop_sleep(_vm->_gamestate->gfx_state, 0);
	return false;
}

bool Console::cmdSaveGame(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Saves the current game state to the hard disk\n");
		DebugPrintf("Usage: %s <filename>\n", argv[0]);
		return true;
	}

	int result = 0;
	for (uint i = 0; i < _vm->_gamestate->_fileHandles.size(); i++)
		if (_vm->_gamestate->_fileHandles[i].isOpen())
			result++;

	if (result)
		DebugPrintf("Note: Game state has %d open file handles.\n", result);

	Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
	Common::OutSaveFile *out;
	if (!(out = saveFileMan->openForSaving(argv[1]))) {
		DebugPrintf("Error opening savegame \"%s\" for writing\n", argv[1]);
		return true;
	}

	// TODO: enable custom descriptions? force filename into a specific format?
	if (gamestate_save(_vm->_gamestate, out, "debugging")) {
		DebugPrintf("Saving the game state to '%s' failed\n", argv[1]);
	}

	return true;
}

bool Console::cmdRestoreGame(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Restores a saved game from the hard disk\n");
		DebugPrintf("Usage: %s <filename>\n", argv[0]);
		return true;
	}

	EngineState *newstate = NULL;

	Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
	Common::SeekableReadStream *in;
	if (!(in = saveFileMan->openForLoading(argv[1]))) {
		// found a savegame file
		newstate = gamestate_restore(_vm->_gamestate, in);
		delete in;
	}

	if (newstate) {
		_vm->_gamestate->successor = newstate; // Set successor

		script_abort_flag = 2; // Abort current game with replay
		g_debugstate_valid = 0;

		shrink_execution_stack(_vm->_gamestate, _vm->_gamestate->execution_stack_base + 1);
		return 0;
	} else {
		DebugPrintf("Restoring gamestate '%s' failed.\n", argv[1]);
		return 1;
	}

	return false;
}

bool Console::cmdRestartGame(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Restarts the game. There are two ways to restart a SCI game:\n");
		DebugPrintf("%s play - calls the game object's play() method\n", argv[0]);
		DebugPrintf("%s replay - calls the replay() methody\n", argv[0]);
		return true;
	}

	if (!scumm_stricmp(argv[1], "play")) {
		_vm->_gamestate->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
	} else if (!scumm_stricmp(argv[1], "replay")) {
		_vm->_gamestate->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
	} else {
		DebugPrintf("Invalid usage of %s\n", argv[0]);
		return true;
	}

	_vm->_gamestate->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
	script_abort_flag = 1;
	g_debugstate_valid = 0;

	return false;
}

bool Console::cmdClassTable(int argc, const char **argv) {
	DebugPrintf("Available classes:\n");
	for (uint i = 0; i < _vm->_gamestate->_classtable.size(); i++) {
		if (_vm->_gamestate->_classtable[i].reg.segment) {
			DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i, 
					PRINT_REG(_vm->_gamestate->_classtable[i].reg), _vm->_gamestate->_classtable[i].script);
		}
	}

	return true;
}

bool Console::cmdSentenceFragments(int argc, const char **argv) {
	DebugPrintf("Sentence fragments (used to build Parse trees)\n");

	for (uint i = 0; i < _vm->_gamestate->_vocabulary->getParserBranchesSize(); i++) {
		int j = 0;

		const parse_tree_branch_t &branch = _vm->_gamestate->_vocabulary->getParseTreeBranch(i);
		DebugPrintf("R%02d: [%x] ->", i, branch.id);
		while ((j < 10) && branch.data[j]) {
			int dat = branch.data[j++];

			switch (dat) {
			case VOCAB_TREE_NODE_COMPARE_TYPE:
				dat = branch.data[j++];
				DebugPrintf(" C(%x)", dat);
				break;

			case VOCAB_TREE_NODE_COMPARE_GROUP:
				dat = branch.data[j++];
				DebugPrintf(" WG(%x)", dat);
				break;

			case VOCAB_TREE_NODE_FORCE_STORAGE:
				dat = branch.data[j++];
				DebugPrintf(" FORCE(%x)", dat);
				break;

			default:
				if (dat > VOCAB_TREE_NODE_LAST_WORD_STORAGE) {
					int dat2 = branch.data[j++];
					DebugPrintf(" %x[%x]", dat, dat2);
				} else
					DebugPrintf(" ?%x?", dat);
			}
		}
		DebugPrintf("\n");
	}

	DebugPrintf("%d rules.\n", _vm->_gamestate->_vocabulary->getParserBranchesSize());

	return true;
}

bool Console::cmdParse(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Parses a sequence of words with a GNF rule set and prints the resulting parse tree\n");
		DebugPrintf("Usage: %s <word1> <word2> ... <wordn>\n", argv[0]);
		return true;
	}

	ResultWordList words;
	char *error;
	char string[1000];

	// Construct the string
	strcpy(string, argv[2]);
	for (int i = 2; i < argc; i++) {
		strcat(string, argv[i]);
	}

	DebugPrintf("Parsing '%s'\n", string);
	bool res = _vm->_gamestate->_vocabulary->tokenizeString(words, string, &error);
	if (res && !words.empty()) {
		int syntax_fail = 0;

		vocab_synonymize_tokens(words, _vm->_gamestate->_synonyms);

		DebugPrintf("Parsed to the following blocks:\n");

		for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i)
			DebugPrintf("   Type[%04x] Group[%04x]\n", i->_class, i->_group);

		if (_vm->_gamestate->_vocabulary->parseGNF(_vm->_gamestate->parser_nodes, words, true))
			syntax_fail = 1; // Building a tree failed

		if (syntax_fail)
			DebugPrintf("Building a tree failed.\n");
		else
			vocab_dump_parse_tree("debug-parse-tree", _vm->_gamestate->parser_nodes);

	} else {
		DebugPrintf("Unknown word: '%s'\n", error);
		free(error);
	}

	return true;
}

bool Console::cmdParserNodes(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Shows the specified number of nodes from the parse node tree\n");
		DebugPrintf("Usage: %s <nr>\n", argv[0]);
		DebugPrintf("where <nr> is the number of nodes to show from the parse node tree\n");
		return true;
	}

	int end = MIN<int>(atoi(argv[1]), VOCAB_TREE_NODES);

	for (int i = 0; i < end; i++) {
		DebugPrintf(" Node %03x: ", i);
		if (_vm->_gamestate->parser_nodes[i].type == kParseTreeLeafNode)
			DebugPrintf("Leaf: %04x\n", _vm->_gamestate->parser_nodes[i].content.value);
		else
			DebugPrintf("Branch: ->%04x, ->%04x\n", _vm->_gamestate->parser_nodes[i].content.branches[0],
			          _vm->_gamestate->parser_nodes[i].content.branches[1]);
	}

	return true;
}

bool Console::cmdDrawPic(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Draws a pic resource\n");
		DebugPrintf("Usage: %s <nr> [<pal>] [<fl>]\n", argv[0]);
		DebugPrintf("where <nr> is the number of the pic resource to draw\n");
		DebugPrintf("<pal> is the optional default palette for the pic (default: 0)\n");
		DebugPrintf("<fl> are any pic draw flags (default: 1)\n");
		return true;
	}

	int flags = 1, default_palette = 0;

	if (argc > 2)
		default_palette = atoi(argv[2]);

	if (argc == 4)
		flags = atoi(argv[3]);

	gfxop_new_pic(_vm->_gamestate->gfx_state, atoi(argv[1]), flags, default_palette);
	gfxop_clear_box(_vm->_gamestate->gfx_state, gfx_rect(0, 0, 320, 200));
	gfxop_update(_vm->_gamestate->gfx_state);
	gfxop_sleep(_vm->_gamestate->gfx_state, 0);

	return false;
}

bool Console::cmdDrawRect(int argc, const char **argv) {
	if (argc != 6) {
		DebugPrintf("Draws a rectangle to the screen with one of the EGA colors\n");
		DebugPrintf("Usage: %s <x> <y> <width> <height> <color>\n", argv[0]);
		DebugPrintf("where <color> is the EGA color to use (0-15)\n");
		return true;
	}

	int col = CLIP<int>(atoi(argv[5]), 0, 15);

	gfxop_set_clip_zone(_vm->_gamestate->gfx_state, gfx_rect_fullscreen);
	gfxop_fill_box(_vm->_gamestate->gfx_state, gfx_rect(atoi(argv[1]), atoi(argv[2]), 
										atoi(argv[3]), atoi(argv[4])), _vm->_gamestate->ega_colors[col]);
	gfxop_update(_vm->_gamestate->gfx_state);

	return false;
}

bool Console::cmdDrawCel(int argc, const char **argv) {
	if (argc != 4) {
		DebugPrintf("Draws a single view cel to the center of the screen\n");
		DebugPrintf("Usage: %s <view> <loop> <cel> <palette>\n", argv[0]);
		return true;
	}

	int view = atoi(argv[1]);
	int loop = atoi(argv[2]);
	int cel = atoi(argv[3]);
	int palette = atoi(argv[4]);

	gfxop_set_clip_zone(_vm->_gamestate->gfx_state, gfx_rect_fullscreen);
	gfxop_draw_cel(_vm->_gamestate->gfx_state, view, loop, cel, Common::Point(160, 100), _vm->_gamestate->ega_colors[0], palette);
	gfxop_update(_vm->_gamestate->gfx_state);

	return false;
}

bool Console::cmdViewInfo(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Displays the number of loops and cels of each loop\n");
		DebugPrintf("for the specified view resource and palette.");
		DebugPrintf("Usage: %s <view> <palette>\n", argv[0]);
		return true;
	}

	int view = atoi(argv[1]);
	int palette = atoi(argv[2]);
	int loops, i;
	gfxr_view_t *view_pixmaps = NULL;
	gfx_color_t transparent = { PaletteEntry(), 0, -1, -1, 0 };

	DebugPrintf("Resource view.%d ", view);

	loops = gfxop_lookup_view_get_loops(_vm->_gamestate->gfx_state, view);

	if (loops < 0)
		DebugPrintf("does not exist.\n");
	else {
		DebugPrintf("has %d loops:\n", loops);

		for (i = 0; i < loops; i++) {
			int j, cels;

			DebugPrintf("Loop %d: %d cels.\n", i, cels = gfxop_lookup_view_get_cels(_vm->_gamestate->gfx_state, view, i));
			for (j = 0; j < cels; j++) {
				int width;
				int height;
				Common::Point mod;

				// Show pixmap on screen
				view_pixmaps = _vm->_gamestate->gfx_state->gfxResMan->getView(view, &i, &j, palette);
				gfxop_draw_cel(_vm->_gamestate->gfx_state, view, i, j, Common::Point(0,0), transparent, palette);

				gfxop_get_cel_parameters(_vm->_gamestate->gfx_state, view, i, j, &width, &height, &mod);

				DebugPrintf("   cel %d: size %dx%d, adj+(%d,%d)\n", j, width, height, mod.x, mod.y);
			}
		}
	}

	return true;
}

bool Console::cmdUpdateZone(int argc, const char **argv) {
	if (argc != 4) {
		DebugPrintf("Propagates a rectangular area from the back buffer to the front buffer\n");
		DebugPrintf("Usage: %s <x> <y> <width> <height>\n", argv[0]);
		return true;
	}

	int x = atoi(argv[1]);
	int y = atoi(argv[2]);
	int width = atoi(argv[3]);
	int height = atoi(argv[4]);

	_vm->_gamestate->gfx_state->driver->update(gfx_rect(x, y, width, height), Common::Point(x, y), GFX_BUFFER_FRONT);

	return false;
}

bool Console::cmdPropagateZone(int argc, const char **argv) {
	if (argc != 5) {
		DebugPrintf("Propagates a rectangular area from a lower graphics buffer to a higher one\n");
		DebugPrintf("Usage: %s <x> <y> <width> <height> <map>\n", argv[0]);
		DebugPrintf("Where <map> can be 0 or 1\n");
		return true;
	}

	int x = atoi(argv[1]);
	int y = atoi(argv[2]);
	int width = atoi(argv[3]);
	int height = atoi(argv[4]);
	int map = CLIP<int>(atoi(argv[5]), 0, 1);
	rect_t rect = gfx_rect(x, y, width, height);

	gfxop_set_clip_zone(_vm->_gamestate->gfx_state, gfx_rect_fullscreen);

	if (map == 1)
		gfxop_clear_box(_vm->_gamestate->gfx_state, rect);
	else
		gfxop_update_box(_vm->_gamestate->gfx_state, rect);
	gfxop_update(_vm->_gamestate->gfx_state);
	gfxop_sleep(_vm->_gamestate->gfx_state, 0);

	return false;
}

bool Console::cmdFillScreen(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Fills the screen with one of the EGA colors\n");
		DebugPrintf("Usage: %s <color>\n", argv[0]);
		DebugPrintf("where <color> is the EGA color to use (0-15)\n");
		return true;
	}

	int col = CLIP<int>(atoi(argv[1]), 0, 15);

	gfxop_set_clip_zone(_vm->_gamestate->gfx_state, gfx_rect_fullscreen);
	gfxop_fill_box(_vm->_gamestate->gfx_state, gfx_rect_fullscreen, _vm->_gamestate->ega_colors[col]);
	gfxop_update(_vm->_gamestate->gfx_state);

	return false;
}

bool Console::cmdCurrentPort(int argc, const char **argv) {
	if (!_vm->_gamestate->port)
		DebugPrintf("There is no port active currently.\n");
	else
		DebugPrintf("Current port ID: %d\n", _vm->_gamestate->port->_ID);

	return true;
}

bool Console::cmdPrintPort(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Prints information about a port\n");
		DebugPrintf("%s current - prints information about the current port\n", argv[0]);
		DebugPrintf("%s <ID> - prints information about the port with the specified ID\n", argv[0]);
		return true;
	}

	GfxPort *port;
	
	if (!scumm_stricmp(argv[1], "current")) {
		port = _vm->_gamestate->port;
		if (!port)
			DebugPrintf("There is no active port currently\n");
		else
			port->print(0);
	} else {
		if (!_vm->_gamestate->visual) {
			DebugPrintf("Visual is uninitialized\n");
		} else {
			port = _vm->_gamestate->visual->getPort(atoi(argv[1]));
			if (!port)
				DebugPrintf("No such port\n");
			else
				port->print(0);
		}
	}

	return true;
}

bool Console::cmdParseGrammar(int argc, const char **argv) {
	DebugPrintf("Parse grammar, in strict GNF:\n");

	_vm->_gamestate->_vocabulary->buildGNF(true);

	return true;
}

bool Console::cmdVisualState(int argc, const char **argv) {
	DebugPrintf("State of the current visual widget:\n");

	if (_vm->_gamestate->visual)
		_vm->_gamestate->visual->print(0);
	else
		DebugPrintf("The visual widget is uninitialized.\n");

	return true;
}

bool Console::cmdFlushPorts(int argc, const char **argv) {
	gfxop_set_pointer_cursor(_vm->_gamestate->gfx_state, GFXOP_NO_POINTER);
	DebugPrintf("Flushing dynamically allocated ports (for memory profiling)...\n");
	delete _vm->_gamestate->visual;
	_vm->_gamestate->gfx_state->gfxResMan->freeAllResources();
	_vm->_gamestate->visual = NULL;

	return true;
}

bool Console::cmdDynamicViews(int argc, const char **argv) {
	DebugPrintf("List of active dynamic views:\n");

	if (_vm->_gamestate->dyn_views)
		_vm->_gamestate->dyn_views->print(0);
	else
		DebugPrintf("The list is empty.\n");

	return true;
}

bool Console::cmdDroppedViews(int argc, const char **argv) {
	DebugPrintf("List of dropped dynamic views:\n");

	if (_vm->_gamestate->drop_views)
		_vm->_gamestate->drop_views->print(0);
	else
		DebugPrintf("The list is empty.\n");

	return true;
}

bool Console::cmdPriorityBands(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Priority bands start at y=%d. They end at y=%d\n", _vm->_gamestate->priority_first, _vm->_gamestate->priority_last);
		DebugPrintf("Use %s <priority band> to print the start of priority for the specified priority band (0 - 15)\n", argv[0]);
		return true;
	}

	int zone = CLIP<int>(atoi(argv[1]), 0, 15);
	DebugPrintf("Zone %x starts at y=%d\n", zone, _find_priority_band(_vm->_gamestate, zone));

	return true;
}

bool Console::cmdStatusBarColors(int argc, const char **argv) {
	if (argc != 3) {
		DebugPrintf("Sets the colors of the status bar\n");
		DebugPrintf("Usage: %s <foreground color> <background color>\n", argv[0]);
		return true;
	}

	_vm->_gamestate->titlebar_port->_color = _vm->_gamestate->ega_colors[atoi(argv[1])];
	_vm->_gamestate->titlebar_port->_bgcolor = _vm->_gamestate->ega_colors[atoi(argv[2])];

	_vm->_gamestate->status_bar_foreground = atoi(argv[1]);
	_vm->_gamestate->status_bar_background = atoi(argv[2]);

	sciw_set_status_bar(_vm->_gamestate, _vm->_gamestate->titlebar_port, _vm->_gamestate->_statusBarText, 
							_vm->_gamestate->status_bar_foreground, _vm->_gamestate->status_bar_background);
	gfxop_update(_vm->_gamestate->gfx_state);

	return false;
}

bool Console::cmdPrintSegmentTable(int argc, const char **argv) {
	DebugPrintf("Segment table:\n");

	for (uint i = 0; i < _vm->_gamestate->seg_manager->_heap.size(); i++) {
		MemObject *mobj = _vm->_gamestate->seg_manager->_heap[i];
		if (mobj && mobj->getType()) {
			DebugPrintf(" [%04x] ", i);

			switch (mobj->getType()) {
			case MEM_OBJ_SCRIPT:
				DebugPrintf("S  script.%03d l:%d ", (*(Script *)mobj).nr, (*(Script *)mobj).lockers);
				break;

			case MEM_OBJ_CLONES:
				DebugPrintf("C  clones (%d allocd)", (*(CloneTable *)mobj).entries_used);
				break;

			case MEM_OBJ_LOCALS:
				DebugPrintf("V  locals %03d", (*(LocalVariables *)mobj).script_id);
				break;

			case MEM_OBJ_STACK:
				DebugPrintf("D  data stack (%d)", (*(DataStack *)mobj).nr);
				break;

			case MEM_OBJ_SYS_STRINGS:
				DebugPrintf("Y  system string table");
				break;

			case MEM_OBJ_LISTS:
				DebugPrintf("L  lists (%d)", (*(ListTable *)mobj).entries_used);
				break;

			case MEM_OBJ_NODES:
				DebugPrintf("N  nodes (%d)", (*(NodeTable *)mobj).entries_used);
				break;

			case MEM_OBJ_HUNK:
				DebugPrintf("H  hunk (%d)", (*(HunkTable *)mobj).entries_used);
				break;

			case MEM_OBJ_DYNMEM:
				DebugPrintf("M  dynmem: %d bytes", (*(DynMem *)mobj)._size);
				break;

			case MEM_OBJ_STRING_FRAG:
				DebugPrintf("F  string fragments");
				break;

			default:
				DebugPrintf("I  Invalid (type = %x)", mobj->getType());
				break;
			}

			DebugPrintf("  seg_ID = %d \n", mobj->getSegMgrId());
		}
	}
	DebugPrintf("\n");

	return true;
}

bool Console::segmentInfo(int nr) {
	DebugPrintf("[%04x] ", nr);

	if ((nr < 0) || ((uint)nr >= _vm->_gamestate->seg_manager->_heap.size()) || !_vm->_gamestate->seg_manager->_heap[nr])
		return false;

	MemObject *mobj = _vm->_gamestate->seg_manager->_heap[nr];

	switch (mobj->getType()) {

	case MEM_OBJ_SCRIPT: {
		Script *scr = (Script *)mobj;
		DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->nr, scr->lockers, (uint)scr->buf_size, (uint)scr->buf_size);
		if (scr->export_table)
			DebugPrintf("  Exports: %4d at %d\n", scr->exports_nr, (int)(((byte *)scr->export_table) - ((byte *)scr->buf)));
		else
			DebugPrintf("  Exports: none\n");

		DebugPrintf("  Synonyms: %4d\n", scr->synonyms_nr);

		if (scr->locals_block)
			DebugPrintf("  Locals : %4d in segment 0x%x\n", scr->locals_block->_locals.size(), scr->locals_segment);
		else
			DebugPrintf("  Locals : none\n");

		DebugPrintf("  Objects: %4d\n", scr->_objects.size());
		for (uint i = 0; i < scr->_objects.size(); i++) {
			DebugPrintf("    ");
			// Object header
			Object *obj = obj_get(_vm->_gamestate, scr->_objects[i].pos);
			if (obj)
				DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(scr->_objects[i].pos), 
							obj_get_name(_vm->_gamestate, scr->_objects[i].pos), obj->_variables.size(), obj->methods_nr);
		}
	}
	break;

	case MEM_OBJ_LOCALS: {
		LocalVariables *locals = (LocalVariables *)mobj;
		DebugPrintf("locals for script.%03d\n", locals->script_id);
		DebugPrintf("  %d (0x%x) locals\n", locals->_locals.size(), locals->_locals.size());
	}
	break;

	case MEM_OBJ_STACK: {
		DataStack *stack = (DataStack *)mobj;
		DebugPrintf("stack\n");
		DebugPrintf("  %d (0x%x) entries\n", stack->nr, stack->nr);
	}
	break;

	case MEM_OBJ_SYS_STRINGS: {
		DebugPrintf("system string table - viewing currently disabled\n");
#if 0
		SystemStrings *strings = &(mobj->data.sys_strings);

		for (int i = 0; i < SYS_STRINGS_MAX; i++)
			if (strings->strings[i].name)
				DebugPrintf("  %s[%d]=\"%s\"\n", strings->strings[i].name, strings->strings[i].max_size, strings->strings[i].value);
#endif
	}
	break;

	case MEM_OBJ_CLONES: {
		CloneTable *ct = (CloneTable *)mobj;

		DebugPrintf("clones\n");

		for (uint i = 0; i < ct->_table.size(); i++)
			if (ct->isValidEntry(i)) {
				reg_t objpos;
				objpos.offset = i;
				objpos.segment = nr;
				DebugPrintf("  [%04x] %s; copy of ", i, obj_get_name(_vm->_gamestate, objpos));
				// Object header
				Object *obj = obj_get(_vm->_gamestate, ct->_table[i].pos);
				if (obj)
					DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].pos), 
								obj_get_name(_vm->_gamestate, ct->_table[i].pos), obj->_variables.size(), obj->methods_nr);
			}
	}
	break;

	case MEM_OBJ_LISTS: {
		ListTable *lt = (ListTable *)mobj;

		DebugPrintf("lists\n");
		for (uint i = 0; i < lt->_table.size(); i++)
			if (lt->isValidEntry(i)) {
				DebugPrintf("  [%04x]: ", i);
				printList(&(lt->_table[i]));
			}
	}
	break;

	case MEM_OBJ_NODES: {
		DebugPrintf("nodes (total %d)\n", (*(NodeTable *)mobj).entries_used);
		break;
	}

	case MEM_OBJ_HUNK: {
		HunkTable *ht = (HunkTable *)mobj;

		DebugPrintf("hunk  (total %d)\n", ht->entries_used);
		for (uint i = 0; i < ht->_table.size(); i++)
			if (ht->isValidEntry(i)) {
				DebugPrintf("    [%04x] %d bytes at %p, type=%s\n",
				          i, ht->_table[i].size, ht->_table[i].mem, ht->_table[i].type);
			}
	}
	break;

	case MEM_OBJ_DYNMEM: {
		DebugPrintf("dynmem (%s): %d bytes\n",
		          (*(DynMem *)mobj)._description ? (*(DynMem *)mobj)._description : "no description", (*(DynMem *)mobj)._size);

		Common::hexdump((*(DynMem *)mobj)._buf, (*(DynMem *)mobj)._size, 16, 0);
	}
	break;

	case MEM_OBJ_STRING_FRAG: {
		DebugPrintf("string frags\n");
		break;
	}

	default :
		DebugPrintf("Invalid type %d\n", mobj->getType());
		break;
	}

	DebugPrintf("\n");
	return true;
}

bool Console::cmdSegmentInfo(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Provides information on the specified segment(s)\n");
		DebugPrintf("Usage: %s <segment number>\n", argv[0]);
		DebugPrintf("<segment number> can be a number, which shows the information of the segment with\n");
		DebugPrintf("the specified number, or \"all\" to show information on all active segments");
		return true;
	}

	if (!scumm_stricmp(argv[1], "all")) {
		for (uint i = 0; i < _vm->_gamestate->seg_manager->_heap.size(); i++)
			segmentInfo(i);
	} else {
		int nr = atoi(argv[1]);
		if (!segmentInfo(nr))
			DebugPrintf("Segment %04x does not exist\n", nr);
	}

	return true;
}


bool Console::cmdKillSegment(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Deletes the specified segment\n");
		DebugPrintf("Usage: %s <segment number>\n", argv[0]);
		return true;
	}

	_vm->_gamestate->seg_manager->getScript(atoi(argv[1]))->setLockers(0);

	return true;
}

bool Console::cmdShowMap(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Shows one of the screen maps\n");
		DebugPrintf("Usage: %s <screen map>\n", argv[0]);
		DebugPrintf("Screen maps:\n");
		DebugPrintf("- 0: visual map (back buffer)\n");
		DebugPrintf("- 1: priority map (back buffer)\n");
		DebugPrintf("- 2: control map (static buffer)\n");
		return true;
	}

	gfxop_set_clip_zone(_vm->_gamestate->gfx_state, gfx_rect_fullscreen);

	int map = atoi(argv[1]);

	switch (map) {
	case 0:
		_vm->_gamestate->visual->add_dirty_abs((GfxContainer *)_vm->_gamestate->visual, gfx_rect(0, 0, 320, 200), 0);
		_vm->_gamestate->visual->draw(Common::Point(0, 0));
		break;

	case 1:
		gfx_xlate_pixmap(_vm->_gamestate->gfx_state->pic->priority_map, _vm->_gamestate->gfx_state->driver->getMode(), GFX_XLATE_FILTER_NONE);
		gfxop_draw_pixmap(_vm->_gamestate->gfx_state, _vm->_gamestate->gfx_state->pic->priority_map, gfx_rect(0, 0, 320, 200), Common::Point(0, 0));
		break;

	case 2:
		gfx_xlate_pixmap(_vm->_gamestate->gfx_state->control_map, _vm->_gamestate->gfx_state->driver->getMode(), GFX_XLATE_FILTER_NONE);
		gfxop_draw_pixmap(_vm->_gamestate->gfx_state, _vm->_gamestate->gfx_state->control_map, gfx_rect(0, 0, 320, 200), Common::Point(0, 0));
		break;

	default:
		DebugPrintf("Map %d is not available.\n", map);
		return true;
	}

	gfxop_update(_vm->_gamestate->gfx_state);

	return false;
}

bool Console::cmdSongLib(int argc, const char **argv) {
	DebugPrintf("Song library:\n");

	Song *seeker = _vm->_gamestate->_sound._songlib._lib;

	do {
		DebugPrintf("    %p", (void *)seeker);

		if (seeker) {
			DebugPrintf("[%04lx,p=%d,s=%d]->", seeker->_handle, seeker->_priority, seeker->_status);
			seeker = seeker->_next;
		}
		DebugPrintf("\n");
	} while (seeker);
	DebugPrintf("\n");

	return true;
}

bool Console::cmdGCInvoke(int argc, const char **argv) {
	DebugPrintf("Performing garbage collection...\n");
	run_gc(_vm->_gamestate);
	return true;
}

bool Console::cmdGCObjects(int argc, const char **argv) {
	reg_t_hash_map *use_map = find_all_used_references(_vm->_gamestate);

	DebugPrintf("Reachable object references (normalised):\n");
	for (reg_t_hash_map::iterator i = use_map->begin(); i != use_map->end(); ++i) {
		DebugPrintf(" - %04x:%04x\n", PRINT_REG(i->_key));
	}

	delete use_map;

	return true;
}

void _print_address(void * _, reg_t addr) {
	if (addr.segment)
		((SciEngine *)g_engine)->getDebugger()->DebugPrintf("  %04x:%04x\n", PRINT_REG(addr));
}

bool Console::cmdGCShowReachable(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Prints all addresses directly reachable from the memory object specified as parameter.\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t addr;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &addr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	MemObject *mobj = GET_SEGMENT_ANY(*_vm->_gamestate->seg_manager, addr.segment);
	if (!mobj) {
		DebugPrintf("Unknown segment : %x\n", addr.segment);
		return 1;
	}

	DebugPrintf("Reachable from %04x:%04x:\n", PRINT_REG(addr));
	mobj->listAllOutgoingReferences(_vm->_gamestate, addr, NULL, _print_address);

	return true;
}

bool Console::cmdGCShowFreeable(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Prints all addresses freeable in the segment associated with the\n");
		DebugPrintf("given address (offset is ignored).\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t addr;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &addr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	MemObject *mobj = GET_SEGMENT_ANY(*_vm->_gamestate->seg_manager, addr.segment);
	if (!mobj) {
		DebugPrintf("Unknown segment : %x\n", addr.segment);
		return true;
	}

	DebugPrintf("Freeable in segment %04x:\n", addr.segment);
	mobj->listAllDeallocatable(addr.segment, NULL, _print_address);

	return true;
}

bool Console::cmdGCNormalize(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Prints the \"normal\" address of a given address,\n");
		DebugPrintf("i.e. the address we would free in order to free\n");
		DebugPrintf("the object associated with the original address.\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t addr;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &addr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	MemObject *mobj = GET_SEGMENT_ANY(*_vm->_gamestate->seg_manager, addr.segment);
	if (!mobj) {
		DebugPrintf("Unknown segment : %x\n", addr.segment);
		return true;
	}

	addr = mobj->findCanonicAddress(_vm->_gamestate->seg_manager, addr);
	DebugPrintf(" %04x:%04x\n", PRINT_REG(addr));

	return true;
}

bool Console::cmdVMVarlist(int argc, const char **argv) {
	//const char *varnames[] = {"global", "local", "temp", "param"};

	DebugPrintf("Addresses of variables in the VM:\n");

#if 0
	// TODO: p_var_segs, p_vars, p_var_base, p_var_max

	for (int i = 0; i < 4; i++) {
		DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(p_var_segs[i], p_vars[i] - p_var_base[i])));
		if (p_var_max)
			DebugPrintf("  total %d", p_var_max[i]);
		DebugPrintf("\n");
	}
#endif

	return true;
}

bool Console::cmdVMVars(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Displays or changes variables in the VM\n");
		DebugPrintf("Usage: %s <type> <varnum> [<value>]\n", argv[0]);
		DebugPrintf("First parameter is either g(lobal), l(ocal), t(emp) or p(aram).\n");
		DebugPrintf("Second parameter is the var number\n");
		DebugPrintf("Third parameter (if specified) is the value to set the variable to, in address form\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	//const char *varnames[] = {"global", "local", "temp", "param"};
	const char *varabbrev = "gltp";
	const char *vartype_pre = strchr(varabbrev, *argv[1]);
	int vartype;
	int idx = atoi(argv[2]);

	if (!vartype_pre) {
		DebugPrintf("Invalid variable type '%c'\n", *argv[1]);
		return true;
	}

	vartype = vartype_pre - varabbrev;

	if (idx < 0) {
		DebugPrintf("Invalid: negative index\n");
		return true;
	}

#if 0
	// TODO: p_var_max
	if ((p_var_max) && (p_var_max[vartype] <= idx)) {
		DebugPrintf("Max. index is %d (0x%x)\n", p_var_max[vartype], p_var_max[vartype]);
		return true;
	}
#endif

	switch (argc) {
	case 2:
#if 0
		// TODO: p_vars
		DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(p_vars[vartype][idx]));
#endif
		break;
	case 3:
#if 0
		// TODO: p_vars

		if (parse_reg_t(_vm->_gamestate, argv[3], &p_vars[vartype][idx])) {
			DebugPrintf("Invalid address passed.\n");
			DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
			return true;
		}
#endif
		break;
	default:
		DebugPrintf("Too many arguments\n");
	}

	return true;
}

bool Console::cmdStack(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Lists the specified number of stack elements.\n");
		DebugPrintf("Usage: %s <elements>\n", argv[0]);
		return true;
	}

	if (_vm->_gamestate->_executionStack.empty()) {
		DebugPrintf("No exec stack!");
		return true;
	}

	ExecStack &xs = _vm->_gamestate->_executionStack.back();
	int nr = atoi(argv[1]);

	for (int i = nr; i > 0; i--) {
		if ((xs.sp - xs.fp - i) == 0)
			DebugPrintf("-- temp variables --\n");
		if (xs.sp - i >= _vm->_gamestate->stack_base)
			DebugPrintf("ST:%04x = %04x:%04x\n", (unsigned)(xs.sp - i - _vm->_gamestate->stack_base), PRINT_REG(xs.sp[-i]));
	}

	return true;
}

bool Console::cmdValueType(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Determines the type of a value.\n");
		DebugPrintf("The type can be one of the following:\n");
		DebugPrintf("Invalid, list, object, reference or arithmetic\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t val;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &val)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	int t = determine_reg_type(_vm->_gamestate, val, true);
	int invalid = t & KSIG_INVALID;

	switch (t & ~KSIG_INVALID) {
	case 0:
		DebugPrintf("Invalid");
		break;
	case KSIG_LIST:
		DebugPrintf("List");
		break;
	case KSIG_OBJECT:
		DebugPrintf("Object");
		break;
	case KSIG_REF:
		DebugPrintf("Reference");
		break;
	case KSIG_ARITHMETIC:
		DebugPrintf("Arithmetic");
		break;
	default:
		DebugPrintf("Erroneous unknown type %02x(%d decimal)\n", t, t);
	}

	DebugPrintf("%s\n", invalid ? " (invalid)" : "");

	return true;
}

bool Console::cmdViewListNode(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Examines the list node at the given address.\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t addr;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &addr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	printNode(addr);
	return true;
}

bool Console::cmdViewReference(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Examines an arbitrary reference.\n");
		DebugPrintf("Usage: %s <start address> [<end address>]\n", argv[0]);
		DebugPrintf("Where <start address> is the starting address to examine\n");
		DebugPrintf("<end address>, if provided, is the address where examining ends at\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t reg = NULL_REG;
	reg_t reg_end = NULL_REG;

	if (parse_reg_t(_vm->_gamestate, argv[1], &reg)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	if (argc > 2) {
		if (parse_reg_t(_vm->_gamestate, argv[2], &reg_end)) {
			DebugPrintf("Invalid address passed.\n");
			DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
			return true;
		}
	}

	int type_mask = determine_reg_type(_vm->_gamestate, reg, 1);
	int filter;
	int found = 0;

	DebugPrintf("%04x:%04x is of type 0x%x%s: ", PRINT_REG(reg), type_mask & ~KSIG_INVALID, type_mask & KSIG_INVALID ? " (invalid)" : "");

	type_mask &= ~KSIG_INVALID;

	if (reg.segment == 0 && reg.offset == 0) {
		DebugPrintf("Null.\n");
		return true;
	}

	if (reg_end.segment != reg.segment) {
		DebugPrintf("Ending segment different from starting segment. Assuming no bound on dump.\n");
		reg_end = NULL_REG;
	}

	for (filter = 1; filter < 0xf000; filter <<= 1) {
		int type = type_mask & filter;

		if (found && type) {
			DebugPrintf("--- Alternatively, it could be a ");
		}


		switch (type) {
		case 0:
			break;
		case KSIG_LIST: {
			List *l = lookup_list(_vm->_gamestate, reg);

			DebugPrintf("list\n");

			if (l)
				printList(l);
			else
				DebugPrintf("Invalid list.\n");
		}
			break;
		case KSIG_NODE:
			DebugPrintf("list node\n");
			printNode(reg);
			break;
		case KSIG_OBJECT:
			DebugPrintf("object\n");
			printObject(_vm->_gamestate, reg);
			break;
		case KSIG_REF: {
			int size;
			unsigned char *block = _vm->_gamestate->seg_manager->dereference(reg, &size);

			DebugPrintf("raw data\n");

			if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
				DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
				reg_end = NULL_REG;
			}

			if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
				size = reg_end.offset - reg.offset;

			if (reg_end.segment != 0)
				DebugPrintf("Block size less than or equal to %d\n", size);

			Common::hexdump(block, size, 16, 0);
			}
			break;
		case KSIG_ARITHMETIC:
			DebugPrintf("arithmetic value\n  %d (%04x)\n", (int16) reg.offset, reg.offset);
			break;
		default:
			DebugPrintf("unknown type %d.\n", type);
		}

		if (type) {
			DebugPrintf("\n");
			found = 1;
		}
	}

	return true;
}

bool Console::cmdViewObject(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Examines the object at the given address.\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t addr;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &addr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	DebugPrintf("Information on the object at the given address:\n");
	printObject(_vm->_gamestate, addr);

	return true;
}

bool Console::cmdViewActiveObject(int argc, const char **argv) {
	DebugPrintf("Information on the currently active object or class:\n");

#if 0
	// TODO: p_objp
	printObject(_vm->_gamestate, *p_objp);
#endif

	return true;
}

bool Console::cmdViewAccumulatorObject(int argc, const char **argv) {
	DebugPrintf("Information on the currently active object or class at the address indexed by the accumulator:\n");
	printObject(_vm->_gamestate, _vm->_gamestate->r_acc);

	return true;
}

bool Console::cmdScriptSteps(int argc, const char **argv) {
	DebugPrintf("Number of executed SCI operations: %d\n", script_step_counter);
	return true;
}

bool Console::cmdSetAccumulator(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Sets the accumulator.\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t val;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &val)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	_vm->_gamestate->r_acc = val;

	return true;
}

bool Console::cmdBacktrace(int argc, const char **argv) {
	DebugPrintf("Dumping the send/self/super/call/calle/callb stack:\n");

	DebugPrintf("Call stack (current base: 0x%x):\n", _vm->_gamestate->execution_stack_base);
	Common::List<ExecStack>::iterator iter;
	uint i = 0;

	for (iter = _vm->_gamestate->_executionStack.begin();
	     iter != _vm->_gamestate->_executionStack.end(); ++iter, ++i) {
		ExecStack &call = *iter;
		const char *objname = obj_get_name(_vm->_gamestate, call.sendp);
		int paramc, totalparamc;

		switch (call.type) {

		case EXEC_STACK_TYPE_CALL: {// Normal function
			sciprintf(" %x:[%x]  %s::%s(", i, call.origin, objname, (call.selector == -1) ? "<call[be]?>" :
			          selector_name(_vm->_gamestate, call.selector));
		}
		break;

		case EXEC_STACK_TYPE_KERNEL: // Kernel function
			sciprintf(" %x:[%x]  k%s(", i, call.origin, _vm->_gamestate->_kernel->getKernelName(-(call.selector) - 42).c_str());
			break;

		case EXEC_STACK_TYPE_VARSELECTOR:
			sciprintf(" %x:[%x] vs%s %s::%s (", i, call.origin, (call.argc) ? "write" : "read",
			          objname, _vm->_gamestate->_kernel->getSelectorName(call.selector).c_str());
			break;
		}

		totalparamc = call.argc;

		if (totalparamc > 16)
			totalparamc = 16;

		for (paramc = 1; paramc <= totalparamc; paramc++) {
			sciprintf("%04x:%04x", PRINT_REG(call.variables_argp[paramc]));

			if (paramc < call.argc)
				sciprintf(", ");
		}

		if (call.argc > 16)
			sciprintf("...");

		sciprintf(")\n    obj@%04x:%04x", PRINT_REG(call.objp));
		if (call.type == EXEC_STACK_TYPE_CALL) {
			sciprintf(" pc=%04x:%04x", PRINT_REG(call.addr.pc));
			if (call.sp == CALL_SP_CARRY)
				sciprintf(" sp,fp:carry");
			else {
				sciprintf(" sp=ST:%04x", (unsigned)(call.sp - _vm->_gamestate->stack_base));
				sciprintf(" fp=ST:%04x", (unsigned)(call.fp - _vm->_gamestate->stack_base));
			}
		} else
			sciprintf(" pc:none");

		sciprintf(" argp:ST:%04x", (unsigned)(call.variables_argp - _vm->_gamestate->stack_base));
		if (call.type == EXEC_STACK_TYPE_CALL)
			sciprintf(" script: %d", (*(Script *)_vm->_gamestate->seg_manager->_heap[call.addr.pc.segment]).nr);
		sciprintf("\n");
	}

	return 0;
	return true;
}

bool Console::cmdStep(int argc, const char **argv) {
	g_debugstate_valid = 0;
	if (argc == 2 && atoi(argv[1]) > 0)
		g_debug_step_running = atoi(argv[1]) - 1;

	return true;
}

bool Console::cmdStepEvent(int argc, const char **argv) {
	g_stop_on_event = 1;
	g_debugstate_valid = 0;

	return true;
}

bool Console::cmdStepRet(int argc, const char **argv) {
	g_debug_seeking = kDebugSeekLevelRet;
	g_debug_seek_level = _vm->_gamestate->_executionStack.size() - 1;
	g_debugstate_valid = 0;

	return true;
}

bool Console::cmdStepGlobal(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Steps until the global variable with the specified index is modified.\n");
		DebugPrintf("Usage: %s <global variable index>\n", argv[0]);
		return true;
	}

	g_debug_seeking = kDebugSeekGlobal;
	g_debug_seek_special = atoi(argv[1]);
	g_debugstate_valid = 0;

	return true;
}

bool Console::cmdStepCallk(int argc, const char **argv) {
	int callk_index;
	char *endptr;

	if (argc == 2) {
		/* Try to convert the parameter to a number. If the conversion stops
		   before end of string, assume that the parameter is a function name
		   and scan the function table to find out the index. */
		callk_index = strtoul(argv[1], &endptr, 0);
		if (*endptr != '\0') {
			callk_index = -1;
			for (uint i = 0; i < _vm->_gamestate->_kernel->getKernelNamesSize(); i++)
				if (argv[1] == _vm->_gamestate->_kernel->getKernelName(i)) {
					callk_index = i;
					break;
				}

			if (callk_index == -1) {
				DebugPrintf("Unknown kernel function '%s'\n", argv[1]);
				return true;
			}
		}

		g_debug_seeking = kDebugSeekSpecialCallk;
		g_debug_seek_special = callk_index;
		g_debugstate_valid = 0;
	} else {
		g_debug_seeking = kDebugSeekCallk;
		g_debugstate_valid = 0;
	}

	return true;
}

bool Console::cmdDissassemble(int argc, const char **argv) {
	if (argc != 3) {
		DebugPrintf("Disassembles a method by name.\n");
		DebugPrintf("Usage: %s <object> <method>\n", argv[0]);
		return true;
	}

	reg_t objAddr = NULL_REG;

	if (parse_reg_t(_vm->_gamestate, argv[1], &objAddr)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	Object *obj = obj_get(_vm->_gamestate, objAddr);
	int selector_id = _vm->_gamestate->_kernel->findSelector(argv[2]);
	reg_t addr;

	if (!obj) {
		DebugPrintf("Not an object.");
		return true;
	}

	if (selector_id < 0) {
		DebugPrintf("Not a valid selector name.");
		return true;
	}

	if (lookup_selector(_vm->_gamestate, objAddr, selector_id, NULL, &addr) != kSelectorMethod) {
		DebugPrintf("Not a method.");
		return true;
	}

	do {
		// TODO
		//addr = disassemble(_vm->_gamestate, addr, 0, 0);
	} while (addr.offset > 0);

	return true;
}

bool Console::cmdDissassembleAddress(int argc, const char **argv) {
	if (argc < 2) {
		DebugPrintf("Disassembles one or more commands.\n");
		DebugPrintf("Usage: %s [startaddr] <options>\n", argv[0]);
		DebugPrintf("Valid options are:\n");
		DebugPrintf(" bwt  : Print byte/word tag\n");
		DebugPrintf(" c<x> : Disassemble <x> bytes\n");
		DebugPrintf(" bc   : Print bytecode\n");
		return true;
	}

	reg_t vpc = NULL_REG;
	int op_count = 1;
	int do_bwc = 0;
	int do_bytes = 0;
	int size;

	if (parse_reg_t(_vm->_gamestate, argv[1], &vpc)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	_vm->_gamestate->seg_manager->dereference(vpc, &size);
	size += vpc.offset; // total segment size

	for (int i = 1; i < argc; i++) {
		if (!scumm_stricmp(argv[i], "bwt"))
			do_bwc = 1;
		else if (!scumm_stricmp(argv[i], "bc"))
			do_bytes = 1;
		else if (toupper(argv[i][0]) == 'C')
			op_count = atoi(argv[i] + 1);
		else {
			DebugPrintf("Invalid option '%s'\n", argv[i]);
			return true;
		}
	}

	if (op_count < 0) {
		DebugPrintf("Invalid op_count\n");
		return true;
	}

	do {
		// TODO
		//vpc = disassemble(_vm->_gamestate, vpc, do_bwc, do_bytes);

	} while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--op_count));

	return true;
}

bool Console::cmdSend(int argc, const char **argv) {
	if (argc < 3) {
		DebugPrintf("Sends a message to an object.\n");
		DebugPrintf("Usage: %s <object> <selector name> <param1> <param2> ... <paramn>\n", argv[0]);
		DebugPrintf("Example: send ?fooScript cue\n");
		return true;
	}

	reg_t object;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &object)) {
		DebugPrintf("Invalid address passed for parameter 1.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}
	
	const char *selector_name = argv[2];
	StackPtr stackframe = _vm->_gamestate->_executionStack.front().sp;
	int selector_id;
	int i;
	ExecStack *xstack;
	Object *o;
	reg_t fptr;

	selector_id = _vm->_gamestate->_kernel->findSelector(selector_name);

	if (selector_id < 0) {
		sciprintf("Unknown selector: \"%s\"\n", selector_name);
		return 1;
	}

	o = obj_get(_vm->_gamestate, object);
	if (o == NULL) {
		sciprintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object));
		return 1;
	}

	SelectorType selector_type = lookup_selector(_vm->_gamestate, object, selector_id, 0, &fptr);

	if (selector_type == kSelectorNone) {
		sciprintf("Object does not support selector: \"%s\"\n", selector_name);
		return 1;
	}

	stackframe[0] = make_reg(0, selector_id);
	stackframe[1] = make_reg(0, argc - 3);	// -object -selector name -command name

	for (i = 3; i < argc; i++) {
		if (parse_reg_t(_vm->_gamestate, argv[i], &stackframe[i])) {
			DebugPrintf("Invalid address passed for parameter %d.\n", i);
			DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
			return true;
		}
	}

	xstack = add_exec_stack_entry(_vm->_gamestate, fptr,
	                 _vm->_gamestate->_executionStack.front().sp + argc,
	                 object, argc - 3,
	                 _vm->_gamestate->_executionStack.front().sp - 1, 0, object,
	                 _vm->_gamestate->_executionStack.size()-1, SCI_XS_CALLEE_LOCALS);
	xstack->selector = selector_id;
	xstack->type = selector_type == kSelectorVariable ? EXEC_STACK_TYPE_VARSELECTOR : EXEC_STACK_TYPE_CALL;

	// Now commit the actual function:
	xstack = send_selector(_vm->_gamestate, object, object, stackframe, argc - 3, stackframe);

	xstack->sp += argc;
	xstack->fp += argc;

	_vm->_gamestate->_executionStackPosChanged = true;

	return true;
}

bool Console::cmdGo(int argc, const char **argv) {
	g_debug_seeking = 0;
	g_debugstate_valid = 0;

	return true;
}

bool Console::cmdBreakpointList(int argc, const char **argv) {
	Breakpoint *bp = _vm->_gamestate->bp_list;
	int i = 0;
	int bpdata;

	DebugPrintf("Breakpoint list:\n");

	while (bp) {
		DebugPrintf("  #%i: ", i);
		switch (bp->type) {
		case BREAK_SELECTOR:
			DebugPrintf("Execute %s\n", bp->data.name);
			break;
		case BREAK_EXPORT:
			bpdata = bp->data.address;
			DebugPrintf("Execute script %d, export %d\n", bpdata >> 16, bpdata & 0xFFFF);
			break;
		}

		bp = bp->next;
		i++;
	}

	return true;
}

bool Console::cmdBreakpointDelete(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Deletes a breakpoint with the specified index.\n");
		DebugPrintf("Usage: %s <breakpoint index>\n", argv[0]);
		return true;
	}

	Breakpoint *bp, *bp_next, *bp_prev;
	int i = 0, found = 0;
	int type;
	int idx = atoi(argv[1]);

	// Find breakpoint with given index
	bp_prev = NULL;
	bp = _vm->_gamestate->bp_list;
	while (bp && i < idx) {
		bp_prev = bp;
		bp = bp->next;
		i++;
	}
	if (!bp) {
		DebugPrintf("Invalid breakpoint index %i\n", idx);
		return true;
	}

	// Delete it
	bp_next = bp->next;
	type = bp->type;
	if (type == BREAK_SELECTOR) free(bp->data.name);
	free(bp);
	if (bp_prev)
		bp_prev->next = bp_next;
	else
		_vm->_gamestate->bp_list = bp_next;

	// Check if there are more breakpoints of the same type. If not, clear
	// the respective bit in s->have_bp.
	for (bp = _vm->_gamestate->bp_list; bp; bp = bp->next) {
		if (bp->type == type) {
			found = 1;
			break;
		}
	}

	if (!found)
		_vm->_gamestate->have_bp &= ~type;

	return true;
}

bool Console::cmdBreakpointExecMethod(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Sets a breakpoint on the execution of the specified method.\n");
		DebugPrintf("Usage: %s <method name>\n", argv[0]);
		DebugPrintf("Example: %s ego::doit\n", argv[0]);
		DebugPrintf("May also be used to set a breakpoint that applies whenever an object\n");
		DebugPrintf("of a specific type is touched: %s foo::\n", argv[0]);
		return true;
	}

	/* Note: We can set a breakpoint on a method that has not been loaded yet.
	   Thus, we can't check whether the command argument is a valid method name.
	   A breakpoint set on an invalid method name will just never trigger. */
	Breakpoint *bp;
	if (_vm->_gamestate->bp_list) {
		// List exists, append the breakpoint to the end
		bp = _vm->_gamestate->bp_list;
		while (bp->next)
			bp = bp->next;
		bp->next = (Breakpoint *)malloc(sizeof(Breakpoint));
		bp = bp->next;
	} else {
		// No list, so create the list head
		_vm->_gamestate->bp_list = (Breakpoint *)malloc(sizeof(Breakpoint));
		bp = _vm->_gamestate->bp_list;
	}
	bp->next = NULL;
	bp->type = BREAK_SELECTOR;
	bp->data.name = (char *)malloc(strlen(argv[1]) + 1);
	strcpy(bp->data.name, argv[1]);
	_vm->_gamestate->have_bp |= BREAK_SELECTOR;

	return true;
}

bool Console::cmdBreakpointExecFunction(int argc, const char **argv) {
	// TODO/FIXME: Why does this accept 2 parameters (the high and the low part of the address)?"
	if (argc != 3) {
		DebugPrintf("Sets a breakpoint on the execution of the specified exported function.\n");
		DebugPrintf("Usage: %s <addr1> <addr2>\n", argv[0]);
		return true;
	}

	/* Note: We can set a breakpoint on a method that has not been loaded yet.
	   Thus, we can't check whether the command argument is a valid method name.
	   A breakpoint set on an invalid method name will just never trigger. */
	Breakpoint *bp;
	if (_vm->_gamestate->bp_list) {
		// List exists, append the breakpoint to the end
		bp = _vm->_gamestate->bp_list;
		while (bp->next)
			bp = bp->next;
		bp->next = (Breakpoint *)malloc(sizeof(Breakpoint));
		bp = bp->next;
	} else {
		// No list, so create the list head
		_vm->_gamestate->bp_list = (Breakpoint *)malloc(sizeof(Breakpoint));
		bp = _vm->_gamestate->bp_list;
	}
	bp->next = NULL;
	bp->type = BREAK_EXPORT;
	bp->data.address = (atoi(argv[1]) << 16 | atoi(argv[2]));
	_vm->_gamestate->have_bp |= BREAK_EXPORT;

	return true;
}

bool Console::cmdIsSample(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Tests whether a given sound resource is a PCM sample, \n");
		DebugPrintf("and displays information on it if it is.\n");
		DebugPrintf("Usage: %s <sample id>\n", argv[0]);
		return true;
	}

	Resource *song = _vm->getResMgr()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);
	SongIterator *songit;
	Audio::AudioStream *data;

	if (!song) {
		DebugPrintf("Not a sound resource!\n");
		return true;
	}

	songit = songit_new(song->data, song->size, SCI_SONG_ITERATOR_TYPE_SCI0, 0xcaffe /* What do I care about the ID? */);

	if (!songit) {
		DebugPrintf("Could not convert to song iterator!\n");
		return true;
	}

	if ((data = songit->getAudioStream())) {
		// TODO
/*
		DebugPrintf("\nIs sample (encoding %dHz/%s/%04x)", data->conf.rate, (data->conf.stereo) ?
		          ((data->conf.stereo == SFX_PCM_STEREO_LR) ? "stereo-LR" : "stereo-RL") : "mono", data->conf.format);
*/
		delete data;
	} else
		DebugPrintf("Valid song, but not a sample.\n");

	delete songit;

	return true;
}

bool Console::cmdSfx01Header(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Dumps the header of a SCI01 song\n");
		DebugPrintf("Usage: %s <track>\n", argv[0]);
		return true;
	}

	Resource *song = _vm->getResMgr()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);

	if (!song) {
		DebugPrintf("Doesn't exist\n");
		return true;
	}

	uint32 offset = 0;

	DebugPrintf("SCI01 song track mappings:\n");

	if (*song->data == 0xf0) // SCI1 priority spec
		offset = 8;

	if (song->size <= 0)
		return 1;

	while (song->data[offset] != 0xff) {
		byte device_id = song->data[offset];
		DebugPrintf("* Device %02x:\n", device_id);
		offset++;

		if (offset + 1 >= song->size)
			return 1;

		while (song->data[offset] != 0xff) {
			int track_offset;
			int end;
			byte header1, header2;

			if (offset + 7 >= song->size)
				return 1;

			offset += 2;

			track_offset = READ_LE_UINT16(song->data + offset);
			header1 = song->data[track_offset];
			header2 = song->data[track_offset+1];
			track_offset += 2;

			end = READ_LE_UINT16(song->data + offset + 2);
			DebugPrintf("  - %04x -- %04x", track_offset, track_offset + end);

			if (track_offset == 0xfe)
				DebugPrintf(" (PCM data)\n");
			else
				DebugPrintf(" (channel %d, special %d, %d playing notes, %d foo)\n",
				          header1 & 0xf, header1 >> 4, header2 & 0xf, header2 >> 4);
			offset += 4;
		}
		offset++;
	}

	return true;
}

static int _parse_ticks(byte *data, int *offset_p, int size) {
	int ticks = 0;
	int tempticks;
	int offset = 0;

	do {
		tempticks = data[offset++];
		ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ? SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
	} while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX && offset < size);

	if (offset_p)
		*offset_p = offset;

	return ticks;
}

// Specialised for SCI01 tracks (this affects the way cumulative cues are treated)
static void midi_hexdump(byte *data, int size, int notational_offset) {
	int offset = 0;
	int prev = 0;
	const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0};

	if (*data == 0xf0) // SCI1 priority spec
		offset = 8;

	while (offset < size) {
		int old_offset = offset;
		int offset_mod;
		int time = _parse_ticks(data + offset, &offset_mod, size);
		int cmd;
		int pleft;
		int firstarg = 0;
		int i;
		int blanks = 0;

		offset += offset_mod;
		printf("  [%04x] %d\t",
		        old_offset + notational_offset, time);

		cmd = data[offset];
		if (!(cmd & 0x80)) {
			cmd = prev;
			if (prev < 0x80) {
				printf("Track broken at %x after"
				        " offset mod of %d\n",
				        offset + notational_offset, offset_mod);
				Common::hexdump(data, size, 16, notational_offset);
				return;
			}
			printf("(rs %02x) ", cmd);
			blanks += 8;
		} else {
			++offset;
			printf("%02x ", cmd);
			blanks += 3;
		}
		prev = cmd;

		pleft = MIDI_cmdlen[cmd >> 4];
		if (SCI_MIDI_CONTROLLER(cmd) && data[offset] == SCI_MIDI_CUMULATIVE_CUE)
			--pleft; // This is SCI(0)1 specific

		for (i = 0; i < pleft; i++) {
			if (i == 0)
				firstarg = data[offset];
			printf("%02x ", data[offset++]);
			blanks += 3;
		}

		while (blanks < 16) {
			blanks += 4;
			printf("    ");
		}

		while (blanks < 20) {
			++blanks;
			printf(" ");
		}

		if (cmd == SCI_MIDI_EOT)
			printf(";; EOT");
		else if (cmd == SCI_MIDI_SET_SIGNAL) {
			if (firstarg == SCI_MIDI_SET_SIGNAL_LOOP)
				printf(";; LOOP point");
			else
				printf(";; CUE (%d)", firstarg);
		} else if (SCI_MIDI_CONTROLLER(cmd)) {
			if (firstarg == SCI_MIDI_CUMULATIVE_CUE)
				printf(";; CUE (cumulative)");
			else if (firstarg == SCI_MIDI_RESET_ON_SUSPEND)
				printf(";; RESET-ON-SUSPEND flag");
		}
		printf("\n");

		if (old_offset >= offset) {
			printf("-- Not moving forward anymore,"
			        " aborting (%x/%x)\n", offset, old_offset);
			return;
		}
	}
}

bool Console::cmdSfx01Track(int argc, const char **argv) {
	if (argc != 3) {
		DebugPrintf("Dumps a track of a SCI01 song\n");
		DebugPrintf("Usage: %s <track> <offset>\n", argv[0]);
		return true;
	}

	Resource *song = _vm->getResMgr()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);

	int offset = atoi(argv[2]);

	if (!song) {
		DebugPrintf("Doesn't exist\n");
		return true;
	}

	midi_hexdump(song->data + offset, song->size, offset);

	return true;
}

bool Console::cmdStopSfx(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("Stops a playing sound\n");
		DebugPrintf("Usage: %s <address>\n", argv[0]);
		DebugPrintf("Where <address> is the address of the sound to stop.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	reg_t id;
	
	if (parse_reg_t(_vm->_gamestate, argv[1], &id)) {
		DebugPrintf("Invalid address passed.\n");
		DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
		return true;
	}

	int handle = id.segment << 16 | id.offset;	// frobnicate handle
	EngineState* s = _vm->_gamestate;		// for PUT_SEL32V

	if (id.segment) {
		_vm->_gamestate->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
		_vm->_gamestate->_sound.sfx_remove_song(handle);
		PUT_SEL32V(id, signal, -1);
		PUT_SEL32V(id, nodePtr, 0);
		PUT_SEL32V(id, handle, 0);
	}

	return true;
}

bool Console::cmdExit(int argc, const char **argv) {
	if (argc != 2) {
		DebugPrintf("%s game - exit gracefully\n", argv[0]);
		DebugPrintf("%s now - exit ungracefully\n", argv[0]);
		return true;
	}

	if (!scumm_stricmp(argv[1], "game")) {
		// Quit gracefully
		script_abort_flag = 1; // Terminate VM
		g_debugstate_valid = 0;
		g_debug_seeking = 0;
		g_debug_step_running = 0;

	} else if (!scumm_stricmp(argv[1], "now")) {
		// Quit ungracefully
		exit(0);
	}

	return false;
}

bool Console::cmdAddresses(int argc, const char **argv) {
	DebugPrintf("Address parameters may be passed in one of three forms:\n");
	DebugPrintf(" - ssss:oooo -- where 'ssss' denotes a segment and 'oooo' an offset.\n");
	DebugPrintf("   Example: \"a:c5\" would address something in segment 0xa at offset 0xc5.\n");
	DebugPrintf(" - &scr:oooo -- where 'scr' is a script number and oooo an offset within that script; will\n");
	DebugPrintf("   fail if the script is not currently loaded\n");
	DebugPrintf(" - $REG -- where 'REG' is one of 'PC', 'ACC', 'PREV' or 'OBJ': References the address\n");
	DebugPrintf("   indicated by the register of this name.\n");
	DebugPrintf(" - $REG+n (or -n) -- Like $REG, but modifies the offset part by a specific amount (which\n");
	DebugPrintf("   is specified in hexadecimal).\n");
	DebugPrintf(" - ?obj -- Looks up an object with the specified name, uses its address. This will abort if\n");
	DebugPrintf("   the object name is ambiguous; in that case, a list of addresses and indices is provided.\n");
	DebugPrintf("   ?obj.idx may be used to disambiguate 'obj' by the index 'idx'.\n");

	return true;
}

int parse_reg_t(EngineState *s, const char *str, reg_t *dest) { // Returns 0 on success
	int rel_offsetting = 0;
	const char *offsetting = NULL;
	// Non-NULL: Parse end of string for relative offsets
	char *endptr;

	if (*str == '$') { // Register
		rel_offsetting = 1;

		if (!scumm_strnicmp(str + 1, "PC", 2)) {
			*dest = s->_executionStack.back().addr.pc;
			offsetting = str + 3;
		} else if (!scumm_strnicmp(str + 1, "P", 1)) {
			*dest = s->_executionStack.back().addr.pc;
			offsetting = str + 2;
		} else if (!scumm_strnicmp(str + 1, "PREV", 4)) {
			*dest = s->r_prev;
			offsetting = str + 5;
		} else if (!scumm_strnicmp(str + 1, "ACC", 3)) {
			*dest = s->r_acc;
			offsetting = str + 4;
		} else if (!scumm_strnicmp(str + 1, "A", 1)) {
			*dest = s->r_acc;
			offsetting = str + 2;
		} else if (!scumm_strnicmp(str + 1, "OBJ", 3)) {
			*dest = s->_executionStack.back().objp;
			offsetting = str + 4;
		} else if (!scumm_strnicmp(str + 1, "O", 1)) {
			*dest = s->_executionStack.back().objp;
			offsetting = str + 2;
		} else
			return 1; // No matching register

		if (!*offsetting)
			offsetting = NULL;
		else if (*offsetting != '+' && *offsetting != '-')
			return 1;
	} else if (*str == '&') {
		int script_nr;
		// Look up by script ID
		char *colon = (char *)strchr(str, ':');

		if (!colon)
			return 1;
		*colon = 0;
		offsetting = colon + 1;

		script_nr = strtol(str + 1, &endptr, 10);

		if (*endptr)
			return 1;

		dest->segment = s->seg_manager->segGet(script_nr);

		if (!dest->segment) {
			return 1;
		}
	} else if (*str == '?') {
		int index = -1;
		int times_found = 0;
		char *tmp;
		const char *str_objname;
		char *str_suffix;
		char suffchar = 0;
		uint i;
		// Parse obj by name

		tmp = (char *)strchr(str, '+');
		str_suffix = (char *)strchr(str, '-');
		if (tmp < str_suffix)
			str_suffix = tmp;
		if (str_suffix) {
			suffchar = (*str_suffix);
			*str_suffix = 0;
		}

		tmp = (char *)strchr(str, '.');

		if (tmp) {
			*tmp = 0;
			index = strtol(tmp + 1, &endptr, 16);
			if (*endptr)
				return -1;
		}

		str_objname = str + 1;

		// Now all values are available; iterate over all objects.
		for (i = 0; i < s->seg_manager->_heap.size(); i++) {
			MemObject *mobj = s->seg_manager->_heap[i];
			int idx = 0;
			int max_index = 0;

			if (mobj) {
				if (mobj->getType() == MEM_OBJ_SCRIPT)
					max_index = (*(Script *)mobj)._objects.size();
				else if (mobj->getType() == MEM_OBJ_CLONES)
					max_index = (*(CloneTable *)mobj)._table.size();
			}

			while (idx < max_index) {
				int valid = 1;
				Object *obj = NULL;
				reg_t objpos;
				objpos.offset = 0;
				objpos.segment = i;

				if (mobj->getType() == MEM_OBJ_SCRIPT) {
					obj = &(*(Script *)mobj)._objects[idx];
					objpos.offset = obj->pos.offset;
				} else if (mobj->getType() == MEM_OBJ_CLONES) {
					obj = &((*(CloneTable *)mobj)._table[idx]);
					objpos.offset = idx;
					valid = ((CloneTable *)mobj)->isValidEntry(idx);
				}

				if (valid) {
					const char *objname = obj_get_name(s, objpos);
					if (!strcmp(objname, str_objname)) {
						// Found a match!
						if ((index < 0) && (times_found > 0)) {
							if (times_found == 1) {
								// First time we realized the ambiguity
								printf("Ambiguous:\n");
								printf("  %3x: [%04x:%04x] %s\n", 0, PRINT_REG(*dest), str_objname);
							}
							printf("  %3x: [%04x:%04x] %s\n", times_found, PRINT_REG(objpos), str_objname);
						}
						if (index < 0 || times_found == index)
							*dest = objpos;
						++times_found;
					}
				}
				++idx;
			}

		}

		if (!times_found)
			return 1;

		if (times_found > 1 && index < 0) {
			printf("Ambiguous: Aborting.\n");
			return 1; // Ambiguous
		}

		if (times_found <= index)
			return 1; // Not found

		offsetting = str_suffix;
		if (offsetting)
			*str_suffix = suffchar;
		rel_offsetting = 1;
	} else {
		char *colon = (char *)strchr(str, ':');

		if (!colon) {
			offsetting = str;
			dest->segment = 0;
		} else {
			*colon = 0;
			offsetting = colon + 1;

			dest->segment = strtol(str, &endptr, 16);
			if (*endptr)
				return 1;
		}
	}
	if (offsetting) {
		int val = strtol(offsetting, &endptr, 16);

		if (rel_offsetting)
			dest->offset += val;
		else
			dest->offset = val;

		if (*endptr)
			return 1;
	}

	return 0;
}

void Console::printList(List *l) {
	reg_t pos = l->first;
	reg_t my_prev = NULL_REG;

	DebugPrintf("\t<\n");

	while (!pos.isNull()) {
		Node *node;
		NodeTable *nt = (NodeTable *)GET_SEGMENT(*_vm->_gamestate->seg_manager, pos.segment, MEM_OBJ_NODES);

		if (!nt || !nt->isValidEntry(pos.offset)) {
			DebugPrintf("   WARNING: %04x:%04x: Doesn't contain list node!\n",
			          PRINT_REG(pos));
			return;
		}

		node = &(nt->_table[pos.offset]);

		DebugPrintf("\t%04x:%04x  : %04x:%04x -> %04x:%04x\n", PRINT_REG(pos), PRINT_REG(node->key), PRINT_REG(node->value));

		if (my_prev != node->pred)
			DebugPrintf("   WARNING: current node gives %04x:%04x as predecessor!\n",
			          PRINT_REG(node->pred));

		my_prev = pos;
		pos = node->succ;
	}

	if (my_prev != l->last)
		DebugPrintf("   WARNING: Last node was expected to be %04x:%04x, was %04x:%04x!\n",
		          PRINT_REG(l->last), PRINT_REG(my_prev));
	DebugPrintf("\t>\n");
}

int Console::printNode(reg_t addr) {
	MemObject *mobj = GET_SEGMENT(*_vm->_gamestate->seg_manager, addr.segment, MEM_OBJ_LISTS);

	if (mobj) {
		ListTable *lt = (ListTable *)mobj;
		List *list;

		if (!lt->isValidEntry(addr.offset)) {
			DebugPrintf("Address does not contain a list\n");
			return 1;
		}

		list = &(lt->_table[addr.offset]);

		DebugPrintf("%04x:%04x : first x last = (%04x:%04x, %04x:%04x)\n", PRINT_REG(addr), PRINT_REG(list->first), PRINT_REG(list->last));
	} else {
		NodeTable *nt;
		Node *node;
		mobj = GET_SEGMENT(*_vm->_gamestate->seg_manager, addr.segment, MEM_OBJ_NODES);

		if (!mobj) {
			DebugPrintf("Segment #%04x is not a list or node segment\n", addr.segment);
			return 1;
		}

		nt = (NodeTable *)mobj;

		if (!nt->isValidEntry(addr.offset)) {
			DebugPrintf("Address does not contain a node\n");
			return 1;
		}
		node = &(nt->_table[addr.offset]);

		DebugPrintf("%04x:%04x : prev x next = (%04x:%04x, %04x:%04x); maps %04x:%04x -> %04x:%04x\n",
		          PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(node->succ), PRINT_REG(node->key), PRINT_REG(node->value));
	}

	return 0;
}

int printObject(EngineState *s, reg_t pos) {
	Object *obj = obj_get(s, pos);
	Object *var_container = obj;
	int i;

	if (!obj) {
		sciprintf("[%04x:%04x]: Not an object.", PRINT_REG(pos));
		return 1;
	}

	// Object header
	sciprintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), obj_get_name(s, pos),
				obj->_variables.size(), obj->methods_nr);

	if (!(obj->_variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS))
		var_container = obj_get(s, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]);
	sciprintf("  -- member variables:\n");
	for (i = 0; (uint)i < obj->_variables.size(); i++) {
		sciprintf("    ");
		if (i < var_container->variable_names_nr) {
			sciprintf("[%03x] %s = ", VM_OBJECT_GET_VARSELECTOR(var_container, i), selector_name(s, VM_OBJECT_GET_VARSELECTOR(var_container, i)));
		} else
			sciprintf("p#%x = ", i);

		reg_t val = obj->_variables[i];
		sciprintf("%04x:%04x", PRINT_REG(val));

		Object *ref = obj_get(s, val);
		if (ref)
			sciprintf(" (%s)", obj_get_name(s, val));

		sciprintf("\n");
	}
	sciprintf("  -- methods:\n");
	for (i = 0; i < obj->methods_nr; i++) {
		reg_t fptr = VM_OBJECT_READ_FUNCTION(obj, i);
		sciprintf("    [%03x] %s = %04x:%04x\n", VM_OBJECT_GET_FUNCSELECTOR(obj, i), selector_name(s, VM_OBJECT_GET_FUNCSELECTOR(obj, i)), PRINT_REG(fptr));
	}
	if (s->seg_manager->_heap[pos.segment]->getType() == MEM_OBJ_SCRIPT)
		sciprintf("\nOwner script:\t%d\n", s->seg_manager->getScript(pos.segment)->nr);

	return 0;
}

#define GETRECT(ll, rr, tt, bb) \
	ll = GET_SELECTOR(pos, ll); \
	rr = GET_SELECTOR(pos, rr); \
	tt = GET_SELECTOR(pos, tt); \
	bb = GET_SELECTOR(pos, bb);

#if 0
// TODO Re-implement this
static void viewobjinfo(EngineState *s, HeapPtr pos) {
	char *signals[16] = {
		"stop_update",
		"updated",
		"no_update",
		"hidden",
		"fixed_priority",
		"always_update",
		"force_update",
		"remove",
		"frozen",
		"is_extra",
		"hit_obstacle",
		"doesnt_turn",
		"no_cycler",
		"ignore_horizon",
		"ignore_actor",
		"dispose!"
	};

	int x, y, z, priority;
	int cel, loop, view, signal;
	int nsLeft, nsRight, nsBottom, nsTop;
	int lsLeft, lsRight, lsBottom, lsTop;
	int brLeft, brRight, brBottom, brTop;
	int i;
	int have_rects = 0;
	Common::Rect nsrect, nsrect_clipped, brrect;

	if (lookup_selector(s, pos, s->_kernel->_selectorMap.nsBottom, NULL) == kSelectorVariable) {
		GETRECT(nsLeft, nsRight, nsBottom, nsTop);
		GETRECT(lsLeft, lsRight, lsBottom, lsTop);
		GETRECT(brLeft, brRight, brBottom, brTop);
		have_rects = 1;
	}

	GETRECT(view, loop, signal, cel);

	sciprintf("\n-- View information:\ncel %d/%d/%d at ", view, loop, cel);

	x = GET_SELECTOR(pos, x);
	y = GET_SELECTOR(pos, y);
	priority = GET_SELECTOR(pos, priority);
	if (s->_kernel->_selectorMap.z > 0) {
		z = GET_SELECTOR(pos, z);
		sciprintf("(%d,%d,%d)\n", x, y, z);
	} else
		sciprintf("(%d,%d)\n", x, y);

	if (priority == -1)
		sciprintf("No priority.\n\n");
	else
		sciprintf("Priority = %d (band starts at %d)\n\n", priority, PRIORITY_BAND_FIRST(priority));

	if (have_rects) {
		sciprintf("nsRect: [%d..%d]x[%d..%d]\n", nsLeft, nsRight, nsTop, nsBottom);
		sciprintf("lsRect: [%d..%d]x[%d..%d]\n", lsLeft, lsRight, lsTop, lsBottom);
		sciprintf("brRect: [%d..%d]x[%d..%d]\n", brLeft, brRight, brTop, brBottom);
	}

	nsrect = get_nsrect(s, pos, 0);
	nsrect_clipped = get_nsrect(s, pos, 1);
	brrect = set_base(s, pos);
	sciprintf("new nsRect: [%d..%d]x[%d..%d]\n", nsrect.x, nsrect.xend, nsrect.y, nsrect.yend);
	sciprintf("new clipped nsRect: [%d..%d]x[%d..%d]\n", nsrect_clipped.x, nsrect_clipped.xend, nsrect_clipped.y, nsrect_clipped.yend);
	sciprintf("new brRect: [%d..%d]x[%d..%d]\n", brrect.x, brrect.xend, brrect.y, brrect.yend);
	sciprintf("\n signals = %04x:\n", signal);

	for (i = 0; i < 16; i++)
		if (signal & (1 << i))
			sciprintf("  %04x: %s\n", 1 << i, signals[i]);
}
#endif
#undef GETRECT

#define GETRECT(ll, rr, tt, bb) \
	ll = GET_SELECTOR(pos, ll); \
	rr = GET_SELECTOR(pos, rr); \
	tt = GET_SELECTOR(pos, tt); \
	bb = GET_SELECTOR(pos, bb);

#if 0
// Draws the nsRect and brRect of a dynview object. nsRect is green, brRect is blue.
// TODO: Re-implement this
static int c_gfx_draw_viewobj(EngineState *s, const Common::Array<cmd_param_t> &cmdParams) {
	HeapPtr pos = (HeapPtr)(cmdParams[0].val);
	int is_view;
	int x, y, priority;
	int nsLeft, nsRight, nsBottom, nsTop;
	int brLeft, brRight, brBottom, brTop;

	if (!s) {
		sciprintf("Not in debug state!\n");
		return 1;
	}

	if ((pos < 4) || (pos > 0xfff0)) {
		sciprintf("Invalid address.\n");
		return 1;
	}

	if (((int16)READ_LE_UINT16(s->heap + pos + SCRIPT_OBJECT_MAGIC_OFFSET)) != SCRIPT_OBJECT_MAGIC_NUMBER) {
		sciprintf("Not an object.\n");
		return 0;
	}


	is_view = (lookup_selector(s, pos, s->_kernel->_selectorMap.x, NULL) == kSelectorVariable) &&
	    (lookup_selector(s, pos, s->_kernel->_selectorMap.brLeft, NULL) == kSelectorVariable) &&
	    (lookup_selector(s, pos, s->_kernel->_selectorMap.signal, NULL) == kSelectorVariable) &&
	    (lookup_selector(s, pos, s->_kernel->_selectorMap.nsTop, NULL) == kSelectorVariable);

	if (!is_view) {
		sciprintf("Not a dynamic View object.\n");
		return 0;
	}

	x = GET_SELECTOR(pos, x);
	y = GET_SELECTOR(pos, y);
	priority = GET_SELECTOR(pos, priority);
	GETRECT(brLeft, brRight, brBottom, brTop);
	GETRECT(nsLeft, nsRight, nsBottom, nsTop);
	gfxop_set_clip_zone(s->gfx_state, gfx_rect_fullscreen);

	brTop += 10;
	brBottom += 10;
	nsTop += 10;
	nsBottom += 10;

	gfxop_fill_box(s->gfx_state, gfx_rect(nsLeft, nsTop, nsRight - nsLeft + 1, nsBottom - nsTop + 1), s->ega_colors[2]);
	gfxop_fill_box(s->gfx_state, gfx_rect(brLeft, brTop, brRight - brLeft + 1, brBottom - brTop + 1), s->ega_colors[1]);
	gfxop_fill_box(s->gfx_state, gfx_rect(x - 1, y - 1, 3, 3), s->ega_colors[0]);
	gfxop_fill_box(s->gfx_state, gfx_rect(x - 1, y, 3, 1), s->ega_colors[priority]);
	gfxop_fill_box(s->gfx_state, gfx_rect(x, y - 1, 1, 3), s->ega_colors[priority]);
	gfxop_update(s->gfx_state);

	return 0;
}
#endif
#undef GETRECT

#if 0
// Executes one operation skipping over sends
// TODO Re-implement this
int c_stepover(EngineState *s, const Common::Array<cmd_param_t> &cmdParams) {
	int opcode, opnumber;

	if (!g_debugstate_valid) {
		sciprintf("Not in debug state\n");
		return 1;
	}

	g_debugstate_valid = 0;
	opcode = s->_heap[*p_pc];
	opnumber = opcode >> 1;
	if (opnumber == 0x22 /* callb */ || opnumber == 0x23 /* calle */ ||
	        opnumber == 0x25 /* send */ || opnumber == 0x2a /* self */ || opnumber == 0x2b /* super */) {
		g_debug_seeking = kDebugSeekSO;
		g_debug_seek_level = s->_executionStack.size()-1;
		// Store in g_debug_seek_special the offset of the next command after send
		switch (opcode) {
		case 0x46: // calle W
			g_debug_seek_special = *p_pc + 5;
			break;

		case 0x44: // callb W
		case 0x47: // calle B
		case 0x56: // super W
			g_debug_seek_special = *p_pc + 4;
			break;

		case 0x45: // callb B
		case 0x57: // super B
		case 0x4A: // send W
		case 0x54: // self W
			g_debug_seek_special = *p_pc + 3;
			break;

		default:
			g_debug_seek_special = *p_pc + 2;
		}
	}

	return 0;
}
#endif

#ifdef GFXW_DEBUG_WIDGETS
extern GfxWidget *debug_widgets[];
extern int debug_widget_pos;

// If called with no parameters, it shows which widgets are active
// With parameters, it lists the widget corresponding to the numerical index specified (for each parameter).
bool Console::cmdPrintWidget(int argc, const char **argv) {
	if (argc > 1) {
		for (int i = 0; i < argc; i++) {
			int widget_nr = atoi(argv[1]);

			DebugPrintf("===== Widget #%d:\n", widget_nr);
			debug_widgets[widget_nr]->print(0);
		}
	} else if (debug_widget_pos > 1) {
		DebugPrintf("Widgets 0-%d are active\n", debug_widget_pos - 1);
	} else if (debug_widget_pos == 1) {
		DebugPrintf("Widget 0 is active\n");
	} else {
		DebugPrintf("No widgets are active\n");
	}

	return true;
}
#endif

} // End of namespace Sci