scummvm/engines/scumm/debugger.cpp
2006-02-20 20:57:26 +00:00

916 lines
25 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001-2006 The ScummVM project
*
* 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$
*
*/
#include "common/stdafx.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/str.h"
#include "common/system.h"
#include "common/util.h"
#include "scumm/actor.h"
#include "scumm/boxes.h"
#include "scumm/debugger.h"
#include "scumm/imuse/imuse.h"
#include "scumm/object.h"
#include "scumm/player_v2.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "common/debugger.cpp"
namespace Scumm {
// Debug channel lookup table for Debugger console
static const dbgChannelDesc debugChannels[] = {
{"SCRIPTS", "Track script execution", DEBUG_SCRIPTS},
{"OPCODES", "Track opcode execution", DEBUG_OPCODES},
{"IMUSE", "Track iMUSE events", DEBUG_IMUSE},
{"RESOURCE", "Track resource loading/management", DEBUG_RESOURCE},
{"VARS", "Track variable changes", DEBUG_VARS},
{"ACTORS", "Actor-related debug", DEBUG_ACTORS},
{"SOUND", "Sound related debug", DEBUG_SOUND},
{"INSANE", "Track INSANE", DEBUG_INSANE},
{"SMUSH", "Track SMUSH", DEBUG_SMUSH}
};
void CDECL debugC(int channel, const char *s, ...) {
char buf[STRINGBUFLEN];
va_list va;
// FIXME: Still spew all debug at -d9, for crashes in startup etc.
// Add setting from commandline ( / abstract channel interface)
if (!(g_scumm->_debugFlags & channel) && (gDebugLevel < 9))
return;
va_start(va, s);
vsnprintf(buf, STRINGBUFLEN, s, va);
va_end(va);
debug(buf);
}
ScummDebugger::ScummDebugger(ScummEngine *s)
: Common::Debugger<ScummDebugger>() {
_vm = s;
// Register variables
DVar_Register("debug_countdown", &_frame_countdown, DVAR_INT, 0);
DVar_Register("scumm_speed", &_vm->_fastMode, DVAR_BYTE, 0);
DVar_Register("scumm_room", &_vm->_currentRoom, DVAR_BYTE, 0);
DVar_Register("scumm_roomresource", &_vm->_roomResource, DVAR_INT, 0);
DVar_Register("scumm_vars", &_vm->_scummVars, DVAR_INTARRAY, _vm->_numVariables);
DVar_Register("scumm_gamename", &_vm->_targetName, DVAR_STRING, 0);
DVar_Register("scumm_exename", &_vm->_baseName, DVAR_STRING, 0);
DVar_Register("scumm_gameid", &_vm->_game.id, DVAR_BYTE, 0);
// Register commands
DCmd_Register("continue", &ScummDebugger::Cmd_Exit);
DCmd_Register("exit", &ScummDebugger::Cmd_Exit);
DCmd_Register("quit", &ScummDebugger::Cmd_Exit);
DCmd_Register("restart", &ScummDebugger::Cmd_Restart);
DCmd_Register("actor", &ScummDebugger::Cmd_Actor);
DCmd_Register("actors", &ScummDebugger::Cmd_PrintActor);
DCmd_Register("box", &ScummDebugger::Cmd_PrintBox);
DCmd_Register("matrix", &ScummDebugger::Cmd_PrintBoxMatrix);
DCmd_Register("camera", &ScummDebugger::Cmd_Camera);
DCmd_Register("room", &ScummDebugger::Cmd_Room);
DCmd_Register("objects", &ScummDebugger::Cmd_PrintObjects);
DCmd_Register("object", &ScummDebugger::Cmd_Object);
DCmd_Register("script", &ScummDebugger::Cmd_Script);
DCmd_Register("scr", &ScummDebugger::Cmd_Script);
DCmd_Register("scripts", &ScummDebugger::Cmd_PrintScript);
DCmd_Register("importres", &ScummDebugger::Cmd_ImportRes);
if (_vm->_game.id == GID_LOOM)
DCmd_Register("drafts", &ScummDebugger::Cmd_PrintDraft);
DCmd_Register("loadgame", &ScummDebugger::Cmd_LoadGame);
DCmd_Register("savegame", &ScummDebugger::Cmd_SaveGame);
DCmd_Register("level", &ScummDebugger::Cmd_DebugLevel);
DCmd_Register("debug", &ScummDebugger::Cmd_Debug);
DCmd_Register("help", &ScummDebugger::Cmd_Help);
DCmd_Register("show", &ScummDebugger::Cmd_Show);
DCmd_Register("hide", &ScummDebugger::Cmd_Hide);
DCmd_Register("imuse", &ScummDebugger::Cmd_IMuse);
}
ScummDebugger::~ScummDebugger() {} // we need this here for __SYMBIAN32__
void ScummDebugger::preEnter() {
// Pause sound output
_old_soundsPaused = _vm->_sound->_soundsPaused;
_vm->_sound->pauseSounds(true);
}
void ScummDebugger::postEnter() {
// Resume previous sound state
_vm->_sound->pauseSounds(_old_soundsPaused);
}
///////////////////////////////////////////////////
// Now the fun stuff:
// Commands
bool ScummDebugger::Cmd_Exit(int argc, const char **argv) {
_detach_now = true;
return false;
}
bool ScummDebugger::Cmd_Restart(int argc, const char **argv) {
_vm->restart();
_detach_now = true;
return false;
}
bool ScummDebugger::Cmd_IMuse(int argc, const char **argv) {
if (!_vm->_imuse && !_vm->_musicEngine) {
DebugPrintf("No iMuse engine is active.\n");
return true;
}
if (argc > 1) {
if (!strcmp(argv[1], "panic")) {
_vm->_musicEngine->stopAllSounds();
DebugPrintf("AAAIIIEEEEEE!\n");
DebugPrintf("Shutting down all music tracks\n");
return true;
} else if (!strcmp(argv[1], "play")) {
if (argc > 2 && (!strcmp(argv[2], "random") || atoi(argv[2]) != 0)) {
int sound = atoi(argv[2]);
if (!strcmp(argv[2], "random")) {
DebugPrintf("Selecting from %d songs...\n", _vm->_numSounds);
sound = _vm->_rnd.getRandomNumber(_vm->_numSounds);
}
_vm->ensureResourceLoaded(rtSound, sound);
_vm->_musicEngine->startSound(sound);
DebugPrintf("Attempted to start music %d.\n", sound);
} else {
DebugPrintf("Specify a music resource # from 1-255.\n");
}
return true;
} else if (!strcmp(argv[1], "stop")) {
if (argc > 2 && (!strcmp(argv[2], "all") || atoi(argv[2]) != 0)) {
if (!strcmp(argv[2], "all")) {
_vm->_musicEngine->stopAllSounds();
DebugPrintf("Shutting down all music tracks.\n");
} else {
_vm->_musicEngine->stopSound(atoi(argv[2]));
DebugPrintf("Attempted to stop music %d.\n", atoi(argv[2]));
}
} else {
DebugPrintf("Specify a music resource # or \"all\".\n");
}
return true;
}
}
DebugPrintf("Available iMuse commands:\n");
DebugPrintf(" panic - Stop all music tracks\n");
DebugPrintf(" play # - Play a music resource\n");
DebugPrintf(" stop # - Stop a music resource\n");
return true;
}
bool ScummDebugger::Cmd_Room(int argc, const char **argv) {
if (argc > 1) {
int room = atoi(argv[1]);
_vm->_actors[_vm->VAR(_vm->VAR_EGO)]._room = room;
_vm->_sound->stopAllSounds();
_vm->startScene(room, 0, 0);
_vm->_fullRedraw = true;
return false;
} else {
DebugPrintf("Current room: %d [%d] - use 'room <roomnum>' to switch\n", _vm->_currentRoom, _vm->_roomResource);
return true;
}
}
bool ScummDebugger::Cmd_LoadGame(int argc, const char **argv) {
if (argc > 1) {
int slot = atoi(argv[1]);
_vm->requestLoad(slot);
_detach_now = true;
return false;
}
DebugPrintf("Syntax: loadgame <slotnum>\n");
return true;
}
bool ScummDebugger::Cmd_SaveGame(int argc, const char **argv) {
if (argc > 2) {
int slot = atoi(argv[1]);
_vm->requestSave(slot, argv[2]);
} else
DebugPrintf("Syntax: savegame <slotnum> <name>\n");
return true;
}
bool ScummDebugger::Cmd_Show(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Syntax: show <parameter>\n");
return true;
}
if (!strcmp(argv[1], "hex")) {
_vm->_hexdumpScripts = true;
DebugPrintf("Script hex dumping on\n");
} else if (!strncmp(argv[1], "sta", 3)) {
_vm->_showStack = 1;
DebugPrintf("Stack tracing on\n");
} else {
DebugPrintf("Unknown show parameter '%s'\nParameters are 'hex' for hex dumping and 'sta' for stack tracing\n", argv[1]);
}
return true;
}
bool ScummDebugger::Cmd_Hide(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Syntax: hide <parameter>\n");
return true;
}
if (!strcmp(argv[1], "hex")) {
_vm->_hexdumpScripts = false;
DebugPrintf("Script hex dumping off\n");
} else if (!strncmp(argv[1], "sta", 3)) {
_vm->_showStack = 0;
DebugPrintf("Stack tracing off\n");
} else {
DebugPrintf("Unknown hide parameter '%s'\nParameters are 'hex' to turn off hex dumping and 'sta' to turn off stack tracing\n", argv[1]);
}
return true;
}
bool ScummDebugger::Cmd_Script(int argc, const char** argv) {
int scriptnum;
if (argc < 2) {
DebugPrintf("Syntax: script <scriptnum> <command>\n");
return true;
}
scriptnum = atoi(argv[1]);
// FIXME: what is the max range on these?
// if (scriptnum >= _vm->_numScripts) {
// DebugPrintf("Script number %d is out of range (range: 1 - %d)\n", scriptnum, _vm->_numScripts);
// return true;
//}
if ((!strcmp(argv[2], "kill")) || (!strcmp(argv[2], "stop"))) {
_vm->stopScript(scriptnum);
} else if ((!strcmp(argv[2], "run")) || (!strcmp(argv[2], "start"))) {
_vm->runScript(scriptnum, 0, 0, 0);
return false;
} else {
DebugPrintf("Unknown script command '%s'\nUse <kill/stop | run/start> as command\n", argv[2]);
}
return true;
}
bool ScummDebugger::Cmd_ImportRes(int argc, const char** argv) {
Common::File file;
uint32 size;
int resnum;
if (argc != 4) {
DebugPrintf("Syntax: importres <restype> <filename> <resnum>\n");
return true;
}
resnum = atoi(argv[3]);
// FIXME add bounds check
if (!strncmp(argv[1], "scr", 3)) {
file.open(argv[2], Common::File::kFileReadMode);
if (file.isOpen() == false) {
DebugPrintf("Could not open file %s\n", argv[2]);
return true;
}
if (_vm->_game.features & GF_SMALL_HEADER) {
size = file.readUint16LE();
file.seek(-2, SEEK_CUR);
} else if (_vm->_game.features & GF_SMALL_HEADER) {
if (_vm->_game.version == 4)
file.seek(8, SEEK_CUR);
size = file.readUint32LE();
file.readUint16LE();
file.seek(-6, SEEK_CUR);
} else {
file.readUint32BE();
size = file.readUint32BE();
file.seek(-8, SEEK_CUR);
}
file.read(_vm->res.createResource(rtScript, resnum, size), size);
} else
DebugPrintf("Unknown importres type '%s'\n", argv[1]);
return true;
}
bool ScummDebugger::Cmd_PrintScript(int argc, const char **argv) {
int i;
ScriptSlot *ss = _vm->vm.slot;
DebugPrintf("+-----------------------------------+\n");
DebugPrintf("|# | num|offst|sta|typ|fr|rec|fc|cut|\n");
DebugPrintf("+--+----+-----+---+---+--+---+--+---+\n");
for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
if (ss->number) {
DebugPrintf("|%2d|%4d|%05x|%3d|%3d|%2d|%3d|%2d|%3d|\n",
i, ss->number, ss->offs, ss->status, ss->where,
ss->freezeResistant, ss->recursive,
ss->freezeCount, ss->cutsceneOverride);
}
}
DebugPrintf("+-----------------------------------+\n");
return true;
}
bool ScummDebugger::Cmd_Actor(int argc, const char **argv) {
Actor *a;
int actnum;
int value = 0, value2 = 0;
if (argc < 3) {
DebugPrintf("Syntax: actor <actornum> <command> <parameter>\n");
return true;
}
actnum = atoi(argv[1]);
if (actnum >= _vm->_numActors) {
DebugPrintf("Actor %d is out of range (range: 1 - %d)\n", actnum, _vm->_numActors);
return true;
}
a = &_vm->_actors[actnum];
if (argc > 3)
value = atoi(argv[3]);
if (argc > 4)
value2 = atoi(argv[4]);
if (!strcmp(argv[2], "animvar")) {
a->setAnimVar(value, value2);
DebugPrintf("Actor[%d].animVar[%d] = %d\n", actnum, value, a->getAnimVar(value));
} else if (!strcmp(argv[2], "anim")) {
a->animateActor(value);
DebugPrintf("Actor[%d].animateActor(%d)\n", actnum, value);
} else if (!strcmp(argv[2], "ignoreboxes")) {
a->_ignoreBoxes = (value > 0);
DebugPrintf("Actor[%d].ignoreBoxes = %d\n", actnum, a->_ignoreBoxes);
} else if (!strcmp(argv[2], "x")) {
a->putActor(value, a->_pos.y, a->_room);
DebugPrintf("Actor[%d].x = %d\n", actnum, a->_pos.x);
_vm->_fullRedraw = true;
} else if (!strcmp(argv[2], "y")) {
a->putActor(a->_pos.x, value, a->_room);
DebugPrintf("Actor[%d].y = %d\n", actnum, a->_pos.y);
_vm->_fullRedraw = true;
} else if (!strcmp(argv[2], "_elevation")) {
a->setElevation(value);
DebugPrintf("Actor[%d]._elevation = %d\n", actnum, a->getElevation());
_vm->_fullRedraw = true;
} else if (!strcmp(argv[2], "costume")) {
if (value >= _vm->res.num[rtCostume])
DebugPrintf("Costume not changed as %d exceeds max of %d\n", value, _vm->res.num[rtCostume]);
else {
a->setActorCostume(value);
_vm->_fullRedraw = true;
DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume);
}
} else if (!strcmp(argv[2], "name")) {
DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(actnum));
} else {
DebugPrintf("Unknown actor command '%s'\nUse <ignoreboxes |costume> as command\n", argv[2]);
}
return true;
}
bool ScummDebugger::Cmd_PrintActor(int argc, const char **argv) {
int i;
Actor *a;
DebugPrintf("+-----------------------------------------------------------+\n");
DebugPrintf("|# | x | y | w |elev|cos|box|mov| zp|frm|scl|dir| cls |\n");
DebugPrintf("+--+----+----+---+----+---+---+---+---+---+---+---+---------+\n");
for (i = 1; i < _vm->_numActors; i++) {
a = &_vm->_actors[i];
if (a->_visible)
DebugPrintf("|%2d|%4d|%4d|%3d|%4d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|$%08x|\n",
a->_number, a->_pos.x, a->_pos.y, a->_width, a->getElevation(),
a->_costume, a->_walkbox, a->_moving, a->_forceClip, a->_frame,
a->_scalex, a->getFacing(), _vm->_classData[a->_number]);
}
DebugPrintf("\n");
return true;
}
bool ScummDebugger::Cmd_PrintObjects(int argc, const char **argv) {
int i;
ObjectData *o;
DebugPrintf("Objects in current room\n");
DebugPrintf("+---------------------------------+------------+\n");
DebugPrintf("|num | x | y |width|height|state|fl| cls |\n");
DebugPrintf("+----+----+----+-----+------+-----+--+---------+\n");
for (i = 1; i < _vm->_numLocalObjects ; i++) {
o = &(_vm->_objs[i]);
if (o->obj_nr == 0)
continue;
DebugPrintf("|%4d|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n",
o->obj_nr, o->x_pos, o->y_pos, o->width, o->height, o->state,
o->fl_object_index, _vm->_classData[o->obj_nr]);
}
DebugPrintf("\n");
return true;
}
bool ScummDebugger::Cmd_Object(int argc, const char **argv) {
int i;
int obj;
if (argc < 3) {
DebugPrintf("Syntax: object <objectnum> <command> <parameter>\n");
return true;
}
obj = atoi(argv[1]);
if (obj >= _vm->_numGlobalObjects) {
DebugPrintf("Object %d is out of range (range: 1 - %d)\n", obj, _vm->_numGlobalObjects);
return true;
}
if (!strcmp(argv[2], "pickup")) {
for (i = 0; i < _vm->_numInventory; i++) {
if (_vm->_inventory[i] == (uint16)obj) {
_vm->putOwner(obj, _vm->VAR(_vm->VAR_EGO));
_vm->runInventoryScript(obj);
return true;
}
}
if (argc == 3)
_vm->addObjectToInventory(obj, _vm->_currentRoom);
else
_vm->addObjectToInventory(obj, atoi(argv[3]));
_vm->putOwner(obj, _vm->VAR(_vm->VAR_EGO));
_vm->putClass(obj, kObjectClassUntouchable, 1);
_vm->putState(obj, 1);
_vm->markObjectRectAsDirty(obj);
_vm->clearDrawObjectQueue();
_vm->runInventoryScript(obj);
} else if (!strcmp(argv[2], "state")) {
_vm->putState(obj, atoi(argv[3]));
//is BgNeedsRedraw enough?
_vm->_bgNeedsRedraw = true;
} else if (!strcmp(argv[2], "name")) {
DebugPrintf("Name of object %d: %s\n", obj, _vm->getObjOrActorName(obj));
} else {
DebugPrintf("Unknown object command '%s'\nUse <pickup | state> as command\n", argv[2]);
}
return true;
}
bool ScummDebugger::Cmd_Help(int argc, const char **argv) {
// console normally has 39 line width
// wrap around nicely
int width = 0, size, i;
DebugPrintf("Commands are:\n");
for (i = 0 ; i < _dcmd_count ; i++) {
size = strlen(_dcmds[i].name) + 1;
if ((width + size) >= 39) {
DebugPrintf("\n");
width = size;
} else
width += size;
DebugPrintf("%s ", _dcmds[i].name);
}
width = 0;
DebugPrintf("\n\nVariables are:\n");
for (i = 0 ; i < _dvar_count ; i++) {
size = strlen(_dvars[i].name) + 1;
if ((width + size) >= 39) {
DebugPrintf("\n");
width = size;
} else
width += size;
DebugPrintf("%s ", _dvars[i].name);
}
DebugPrintf("\n");
return true;
}
bool ScummDebugger::Cmd_Debug(int argc, const char **argv) {
int numChannels = sizeof(debugChannels) / sizeof(dbgChannelDesc);
bool setFlag = false; // Remove or add debug channel?
if ((argc == 1) && (_vm->_debugFlags == 0)) {
DebugPrintf("No debug flags are enabled\n");
DebugPrintf("Available Channels: ");
for (int i = 0; i < numChannels; i++) {
DebugPrintf("%s, ", debugChannels[i].channel);
}
DebugPrintf("\n");
return true;
}
if ((argc == 1) && (_vm->_debugFlags > 0)) {
for (int i = 0; i < numChannels; i++) {
if (_vm->_debugFlags & debugChannels[i].flag)
DebugPrintf("%s - %s\n", debugChannels[i].channel,
debugChannels[i].desc);
}
return true;
}
// Enable or disable channel?
if (argv[1][0] == '+') {
setFlag = true;
} else if (argv[1][0] == '-') {
setFlag = false;
} else {
DebugPrintf("Syntax: Debug +CHANNEL, or Debug -CHANNEL\n");
DebugPrintf("Available Channels: ");
for (int i = 0; i < numChannels; i++) {
DebugPrintf("%s, ", debugChannels[i].channel);
DebugPrintf("\n");
}
}
// Identify flag
const char *realFlag = argv[1] + 1;
for (int i = 0; i < numChannels; i++) {
if ((scumm_stricmp(debugChannels[i].channel, realFlag)) == 0) {
if (setFlag) {
_vm->_debugFlags |= debugChannels[i].flag;
DebugPrintf("Enable ");
} else {
_vm->_debugFlags &= ~debugChannels[i].flag;
DebugPrintf("Disable ");
}
DebugPrintf("%s\n", debugChannels[i].desc);
return true;
}
}
DebugPrintf("Unknown flag. Type 'Debug ?' for syntax\n");
return true;
}
bool ScummDebugger::Cmd_DebugLevel(int argc, const char **argv) {
if (argc == 1) {
if (_vm->_debugMode == false)
DebugPrintf("Debugging is not enabled at this time\n");
else
DebugPrintf("Debugging is currently set at level %d\n", gDebugLevel);
} else { // set level
gDebugLevel = atoi(argv[1]);
if (gDebugLevel >= 0) {
_vm->_debugMode = true;
DebugPrintf("Debug level set to level %d\n", gDebugLevel);
} else if (gDebugLevel < 0) {
_vm->_debugMode = false;
DebugPrintf("Debugging is now disabled\n");
} else
DebugPrintf("Not a valid debug level\n");
}
return true;
}
bool ScummDebugger::Cmd_Camera(int argc, const char **argv) {
DebugPrintf("Camera: cur (%d,%d) - dest (%d,%d) - accel (%d,%d) -- last (%d,%d)\n",
_vm->camera._cur.x, _vm->camera._cur.y, _vm->camera._dest.x, _vm->camera._dest.y,
_vm->camera._accel.x, _vm->camera._accel.y, _vm->camera._last.x, _vm->camera._last.y);
return true;
}
bool ScummDebugger::Cmd_PrintBox(int argc, const char **argv) {
int num, i = 0;
if (argc > 1) {
for (i = 1; i < argc; i++)
printBox(atoi(argv[i]));
} else {
num = _vm->getNumBoxes();
DebugPrintf("\nWalk boxes:\n");
for (i = 0; i < num; i++)
printBox(i);
}
return true;
}
bool ScummDebugger::Cmd_PrintBoxMatrix(int argc, const char **argv) {
byte *boxm = _vm->getBoxMatrixBaseAddr();
int num = _vm->getNumBoxes();
int i, j;
DebugPrintf("Walk matrix:\n");
if (_vm->_game.version <= 2)
boxm += num;
for (i = 0; i < num; i++) {
DebugPrintf("%d: ", i);
if (_vm->_game.version <= 2) {
for (j = 0; j < num; j++)
DebugPrintf("[%d] ", *boxm++);
} else {
while (*boxm != 0xFF) {
DebugPrintf("[%d-%d=>%d] ", boxm[0], boxm[1], boxm[2]);
boxm += 3;
}
boxm++;
}
DebugPrintf("\n");
}
return true;
}
void ScummDebugger::printBox(int box) {
if (box < 0 || box >= _vm->getNumBoxes()) {
DebugPrintf("%d is not a valid box!\n", box);
return;
}
BoxCoords coords;
int flags = _vm->getBoxFlags(box);
int mask = _vm->getMaskFromBox(box);
int scale = _vm->getBoxScale(box);
_vm->getBoxCoordinates(box, &coords);
// Print out coords, flags, zbuffer mask
DebugPrintf("%d: [%d x %d] [%d x %d] [%d x %d] [%d x %d], flags=0x%02x, mask=%d, scale=%d\n",
box,
coords.ul.x, coords.ul.y, coords.ll.x, coords.ll.y,
coords.ur.x, coords.ur.y, coords.lr.x, coords.lr.y,
flags, mask, scale);
// Draw the box
drawBox(box);
}
/************ ENDER: Temporary debug code for boxen **************/
static int gfxPrimitivesCompareInt(const void *a, const void *b);
static void hlineColor(ScummEngine *scumm, int x1, int x2, int y, byte color) {
VirtScreen *vs = &scumm->virtscr[0];
byte *ptr;
// Clip y
y += scumm->_screenTop;
if (y < 0 || y >= scumm->_screenHeight)
return;
if (x2 < x1)
SWAP(x2, x1);
// Clip x1 / x2
const int left = scumm->_screenStartStrip * 8;
const int right = scumm->_screenEndStrip * 8;
if (x1 < left)
x1 = left;
if (x2 >= right)
x2 = right - 1;
ptr = (byte *)vs->pixels + x1 + y * vs->pitch;
while (x1++ <= x2) {
*ptr++ = color;
}
}
static int gfxPrimitivesCompareInt(const void *a, const void *b) {
return (*(const int *)a) - (*(const int *)b);
}
static void fillQuad(ScummEngine *scumm, Common::Point v[4], int color) {
const int N = 4;
int i;
int y;
int miny, maxy;
Common::Point pt1, pt2;
int polyInts[N];
// Determine Y maxima
miny = maxy = v[0].y;
for (i = 1; i < N; i++) {
if (v[i].y < miny) {
miny = v[i].y;
} else if (v[i].y > maxy) {
maxy = v[i].y;
}
}
// Draw, scanning y
for (y = miny; y <= maxy; y++) {
int ints = 0;
for (i = 0; i < N; i++) {
int ind1 = i;
int ind2 = (i + 1) % N;
pt1 = v[ind1];
pt2 = v[ind2];
if (pt1.y > pt2.y) {
SWAP(pt1, pt2);
}
if (pt1.y <= y && y <= pt2.y) {
if (y == pt1.y && y == pt2.y) {
hlineColor(scumm, pt1.x, pt2.x, y, color);
} else if ((y >= pt1.y) && (y < pt2.y)) {
polyInts[ints++] = (y - pt1.y) * (pt2.x - pt1.x) / (pt2.y - pt1.y) + pt1.x;
} else if ((y == maxy) && (y > pt1.y) && (y <= pt2.y)) {
polyInts[ints++] = (y - pt1.y) * (pt2.x - pt1.x) / (pt2.y - pt1.y) + pt1.x;
}
}
}
qsort(polyInts, ints, sizeof(int), gfxPrimitivesCompareInt);
for (i = 0; i < ints; i += 2) {
hlineColor(scumm, polyInts[i], polyInts[i + 1], y, color);
}
}
return;
}
void ScummDebugger::drawBox(int box) {
BoxCoords coords;
Common::Point r[4];
_vm->getBoxCoordinates(box, &coords);
r[0] = coords.ul;
r[1] = coords.ur;
r[2] = coords.lr;
r[3] = coords.ll;
// TODO - maybe use different colors for each box, and/or print the box number inside it?
fillQuad(_vm, r, 13);
VirtScreen *vs = _vm->findVirtScreen(coords.ul.y);
if (vs != NULL)
_vm->markRectAsDirty(vs->number, 0, vs->w, 0, vs->h);
_vm->drawDirtyScreenParts();
_vm->_system->updateScreen();
}
bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) {
const char *names[] = {
"Opening", "Straw to Gold", "Dyeing",
"Night Vision", "Twisting", "Sleep",
"Emptying", "Invisibility", "Terror",
"Sharpening", "Reflection", "Healing",
"Silence", "Shaping", "Unmaking",
"Transcendence"
};
int odds[] = {
15162, 15676, 16190, 64, 16961, 17475, 17989, 18503,
73, 19274, 76, 77, 20302, 20816, 21330, 84
};
const char *notes = "cdefgabC";
int i, base, draft;
if (_vm->_game.id != GID_LOOM) {
DebugPrintf("Command only works with Loom/LoomCD\n");
return true;
}
// There are 16 drafts, stored from variable 50 or 100 and upwards.
// Each draft occupies two variables. Even-numbered variables contain
// the notes for each draft, and a number of flags:
//
// +---+---+---+---+-----+-----+-----+-----+
// | A | B | C | D | 444 | 333 | 222 | 111 |
// +---+---+---+---+-----+-----+-----+-----+
//
// A Unknown
// B The player has used the draft successfully at least once
// C The player has knowledge of the draft
// D Unknown
// 444 The fourth note
// 333 The third note
// 222 The second note
// 111 The first note
//
// I don't yet know what the odd-numbered variables are used for.
// Possibly they store information on where and/or how the draft can
// be used. They appear to remain constant throughout the game.
base = (_vm->_game.version == 3) ? 50 : 100;
if (argc == 2) {
// We had to debug a problem at the end of the game that only
// happened if you interrupted the intro at a specific point.
// That made it useful with a command to learn all the drafts
// and notes.
if (strcmp(argv[1], "learn") == 0) {
for (i = 0; i < 16; i++)
_vm->_scummVars[base + 2 * i] |= 0x2000;
_vm->_scummVars[base + 72] = 8;
// In theory, we could run script 18 here to redraw
// the distaff, but I don't know if that's a safe
// thing to do.
DebugPrintf("Learned all drafts and notes.\n");
return true;
}
// During the testing of EGA Loom we had some trouble with the
// drafts data structure being overwritten. I don't expect
// this command is particularly useful any more, but it will
// attempt to repair the (probably) static part of it.
if (strcmp(argv[1], "fix") == 0) {
for (i = 0; i < 16; i++)
_vm->_scummVars[base + 2 * i + 1] = odds[i];
DebugPrintf(
"An attempt has been made to repair\n"
"the internal drafts data structure.\n"
"Continue on your own risk.\n");
return true;
}
}
// Probably the most useful command for ordinary use: list the drafts.
for (i = 0; i < 16; i++) {
draft = _vm->_scummVars[base + i * 2];
DebugPrintf("%d %-13s %c%c%c%c %c%c %5d %c\n",
base + 2 * i,
names[i],
notes[draft & 0x0007],
notes[(draft & 0x0038) >> 3],
notes[(draft & 0x01c0) >> 6],
notes[(draft & 0x0e00) >> 9],
(draft & 0x2000) ? 'K' : ' ',
(draft & 0x4000) ? 'U' : ' ',
_vm->_scummVars[base + 2 * i + 1],
(_vm->_scummVars[base + 2 * i + 1] != odds[i]) ? '!' : ' ');
}
return true;
}
} // End of namespace Scumm