diff options
-rw-r--r-- | engines/made/database.h | 2 | ||||
-rw-r--r-- | engines/made/made.cpp | 5 | ||||
-rw-r--r-- | engines/made/script.cpp | 281 | ||||
-rw-r--r-- | engines/made/script.h | 11 | ||||
-rw-r--r-- | engines/made/scriptfuncs.cpp | 21 | ||||
-rw-r--r-- | engines/made/scriptfuncs.h | 1 |
6 files changed, 237 insertions, 84 deletions
diff --git a/engines/made/database.h b/engines/made/database.h index 50e37d6936..0924200374 100644 --- a/engines/made/database.h +++ b/engines/made/database.h @@ -95,6 +95,8 @@ public: return NULL; } + uint getObjectCount() const { return _objects.size(); } + int16 getMainCodeObjectIndex() const { return _mainCodeObjectIndex; } int16 getVar(int16 index); diff --git a/engines/made/made.cpp b/engines/made/made.cpp index 4ec857547b..76482b03bb 100644 --- a/engines/made/made.cpp +++ b/engines/made/made.cpp @@ -206,7 +206,12 @@ int MadeEngine::go() { } _eventKey = _eventMouseX = _eventMouseY = 0; + +#ifdef DUMP_SCRIPTS + _script->dumpAllScripts(); +#else _script->runScript(_dat->getMainCodeObjectIndex()); +#endif return 0; } diff --git a/engines/made/script.cpp b/engines/made/script.cpp index 7d7ebc83cd..6b736e321b 100644 --- a/engines/made/script.cpp +++ b/engines/made/script.cpp @@ -91,83 +91,87 @@ int16 *ScriptStack::getStackPtr() { /* ScriptInterpreter */ ScriptInterpreter::ScriptInterpreter(MadeEngine *vm) : _vm(vm) { -#define COMMAND(x) { &ScriptInterpreter::x, #x } +#ifdef DUMP_SCRIPTS +#define COMMAND(x, sig) { &ScriptInterpreter::x, #x, sig } +#else +#define COMMAND(x, sig) { &ScriptInterpreter::x, #x} +#endif static CommandEntry commandProcs[] = { /* 01 */ - COMMAND(cmd_branchTrue), - COMMAND(cmd_branchFalse), - COMMAND(cmd_branch), - COMMAND(cmd_true), + COMMAND(cmd_branchTrue, "W"), + COMMAND(cmd_branchFalse, "W"), + COMMAND(cmd_branch, "W"), + COMMAND(cmd_true, ""), /* 05 */ - COMMAND(cmd_false), - COMMAND(cmd_push), - COMMAND(cmd_not), - COMMAND(cmd_add), + COMMAND(cmd_false, ""), + COMMAND(cmd_push, ""), + COMMAND(cmd_not, ""), + COMMAND(cmd_add, ""), /* 09 */ - COMMAND(cmd_sub), - COMMAND(cmd_mul), - COMMAND(cmd_div), - COMMAND(cmd_mod), + COMMAND(cmd_sub, ""), + COMMAND(cmd_mul, ""), + COMMAND(cmd_div, ""), + COMMAND(cmd_mod, ""), /* 13 */ - COMMAND(cmd_band), - COMMAND(cmd_bor), - COMMAND(cmd_bnot), - COMMAND(cmd_lt), + COMMAND(cmd_band, ""), + COMMAND(cmd_bor, ""), + COMMAND(cmd_bnot, ""), + COMMAND(cmd_lt, ""), /* 17 */ - COMMAND(cmd_eq), - COMMAND(cmd_gt), - COMMAND(cmd_loadConstant), - COMMAND(cmd_loadVariable), + COMMAND(cmd_eq, ""), + COMMAND(cmd_gt, ""), + COMMAND(cmd_loadConstant, "w"), + COMMAND(cmd_loadVariable, "w"), /* 21 */ - COMMAND(cmd_getObjectProperty), - COMMAND(cmd_setObjectProperty), - COMMAND(cmd_set), - COMMAND(cmd_print), + COMMAND(cmd_getObjectProperty, ""), + COMMAND(cmd_setObjectProperty, ""), + COMMAND(cmd_set, "w"), + COMMAND(cmd_print, ""), /* 25 */ - COMMAND(cmd_terpri), - COMMAND(cmd_printNumber), - COMMAND(cmd_vref), - COMMAND(cmd_vset), + COMMAND(cmd_terpri, ""), + COMMAND(cmd_printNumber, ""), + COMMAND(cmd_vref, ""), + COMMAND(cmd_vset, ""), /* 29 */ - COMMAND(cmd_vsize), - COMMAND(cmd_exit), - COMMAND(cmd_return), - COMMAND(cmd_call), + COMMAND(cmd_vsize, ""), + COMMAND(cmd_exit, ""), + COMMAND(cmd_return, ""), + COMMAND(cmd_call, "b"), /* 33 */ - COMMAND(cmd_svar), - COMMAND(cmd_sset), - COMMAND(cmd_split), - COMMAND(cmd_snlit), + COMMAND(cmd_svar, ""), + COMMAND(cmd_sset, ""), + COMMAND(cmd_split, ""), + COMMAND(cmd_snlit, ""), /* 37 */ - COMMAND(cmd_yorn), - COMMAND(cmd_save), - COMMAND(cmd_restore), - COMMAND(cmd_arg), + COMMAND(cmd_yorn, ""), + COMMAND(cmd_save, ""), + COMMAND(cmd_restore, ""), + COMMAND(cmd_arg, "b"), /* 41 */ - COMMAND(cmd_aset), - COMMAND(cmd_tmp), - COMMAND(cmd_tset), - COMMAND(cmd_tspace), + COMMAND(cmd_aset, "b"), + COMMAND(cmd_tmp, "b"), + COMMAND(cmd_tset, "b"), + COMMAND(cmd_tspace, "b"), /* 45 */ - COMMAND(cmd_class), - COMMAND(cmd_objectp), - COMMAND(cmd_vectorp), - COMMAND(cmd_restart), + COMMAND(cmd_class, ""), + COMMAND(cmd_objectp, ""), + COMMAND(cmd_vectorp, ""), + COMMAND(cmd_restart, ""), /* 49 */ - COMMAND(cmd_rand), - COMMAND(cmd_randomize), - COMMAND(cmd_send), - COMMAND(cmd_extend), + COMMAND(cmd_rand, ""), + COMMAND(cmd_randomize, ""), + COMMAND(cmd_send, "b"), + COMMAND(cmd_extend, "Eb"), /* 53 */ - COMMAND(cmd_catch), - COMMAND(cmd_cdone), - COMMAND(cmd_throw), - COMMAND(cmd_functionp), + COMMAND(cmd_catch, ""), + COMMAND(cmd_cdone, ""), + COMMAND(cmd_throw, ""), + COMMAND(cmd_functionp, ""), /* 57 */ - COMMAND(cmd_le), - COMMAND(cmd_ge), - COMMAND(cmd_varx), - COMMAND(cmd_setx) + COMMAND(cmd_le, ""), + COMMAND(cmd_ge, ""), + COMMAND(cmd_varx, ""), + COMMAND(cmd_setx, "") }; _commands = commandProcs; _commandsMax = ARRAYSIZE(commandProcs) + 1; @@ -175,9 +179,6 @@ ScriptInterpreter::ScriptInterpreter(MadeEngine *vm) : _vm(vm) { _functions = new ScriptFunctions(_vm); _functions->setupExternalsTable(); - // set to true to dump scripts instead of parsing them - _dumpScripts = false; - #undef COMMAND } @@ -217,27 +218,18 @@ int16 ScriptInterpreter::readInt16() { } void ScriptInterpreter::cmd_branchTrue() { - if (_dumpScripts) - return; - int16 ofs = readInt16(); if (_stack.top() != 0) _codeIp = _codeBase + ofs; } void ScriptInterpreter::cmd_branchFalse() { - if (_dumpScripts) - return; - int16 ofs = readInt16(); if (_stack.top() == 0) _codeIp = _codeBase + ofs; } void ScriptInterpreter::cmd_branch() { - if (_dumpScripts) - return; - int16 ofs = readInt16(); _codeIp = _codeBase + ofs; } @@ -459,29 +451,31 @@ void ScriptInterpreter::cmd_call() { } void ScriptInterpreter::cmd_svar() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_svar"); } void ScriptInterpreter::cmd_sset() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_sset"); } void ScriptInterpreter::cmd_split() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_split"); } void ScriptInterpreter::cmd_snlit() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_snlit"); } void ScriptInterpreter::cmd_yorn() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_yorn"); } void ScriptInterpreter::cmd_save() { - if (_dumpScripts) - return; - int16 result = 0; int16 stringOfs = _stack.top(); const char *filename = _vm->_dat->getString(stringOfs); @@ -490,9 +484,6 @@ void ScriptInterpreter::cmd_save() { } void ScriptInterpreter::cmd_restore() { - if (_dumpScripts) - return; - int16 result = 0; int16 stringOfs = _stack.top(); const char *filename = _vm->_dat->getString(stringOfs); @@ -531,6 +522,7 @@ void ScriptInterpreter::cmd_tspace() { } void ScriptInterpreter::cmd_class() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_class"); } @@ -543,10 +535,12 @@ void ScriptInterpreter::cmd_objectp() { } void ScriptInterpreter::cmd_vectorp() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_vectorp"); } void ScriptInterpreter::cmd_restart() { + // TODO: Used in RTZ warning("Unimplemented command: cmd_restart"); } @@ -613,9 +607,6 @@ void ScriptInterpreter::cmd_extend() { for (int i = 0; i < argc; i++) debug(2, "argv[%02d] = %04X (%d)", i, argv[i], argv[i]); - if (_dumpScripts) - return; - int16 result = _functions->callFunction(func, argc, argv); debug(2, "result = %04X (%d)", result, result); @@ -626,18 +617,22 @@ void ScriptInterpreter::cmd_extend() { } void ScriptInterpreter::cmd_catch() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_catch"); } void ScriptInterpreter::cmd_cdone() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_cdone"); } void ScriptInterpreter::cmd_throw() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_throw"); } void ScriptInterpreter::cmd_functionp() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_functionp"); } @@ -658,11 +653,133 @@ void ScriptInterpreter::cmd_ge() { } void ScriptInterpreter::cmd_varx() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_varx"); } void ScriptInterpreter::cmd_setx() { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented command: cmd_setx"); } +#ifdef DUMP_SCRIPTS +void ScriptInterpreter::dumpScript(int16 objectIndex, int *opcodeStats, int *externStats) { + + debug(1, "Dumping code for object %04X", objectIndex); + + Object *obj = _vm->_dat->getObject(objectIndex); + byte *code = obj->getData(), *codeEnd = code + obj->getSize(); + + while (code < codeEnd) { + byte opcode = *code++; + if (opcode >= 1 && opcode <= _commandsMax) { + Common::String codeLine; + const char *desc = _commands[opcode - 1].desc; + const char *sig = _commands[opcode - 1].sig; + int valueType; /* 0: dec; 1: hex; 2: extended function */ + int16 value; + char tempStr[32]; + if (opcodeStats) + opcodeStats[opcode - 1]++; + codeLine += desc; + for (; *sig != '\0'; sig++) { + codeLine += " "; + // Fallthroughs are intended + switch (*sig) { + case 'b': + valueType = 0; + value = *code++; + break; + case 'B': + valueType = 1; + value = *code++; + break; + case 'w': + valueType = 0; + value = READ_LE_UINT16(code); + code += 2; + break; + case 'W': + valueType = 1; + value = READ_LE_UINT16(code); + code += 2; + break; + case 'E': + valueType = 2; + value = *code++; + break; + } + switch (valueType) { + case 0: + snprintf(tempStr, 32, "%d", value); + break; + case 1: + snprintf(tempStr, 32, "0x%X", value); + break; + case 2: + if (value < _functions->getCount()) { + snprintf(tempStr, 32, "%s", _functions->getFuncName(value)); + externStats[value]++; + } else { + snprintf(tempStr, 32, "invalid: %d", value); + } + break; + } + codeLine += tempStr; + } + debug(1, "%s", codeLine.c_str()); + } else { + error("ScriptInterpreter::dumpScript(%d) Unknown opcode %02X", objectIndex, opcode); + } + } + debug(1, "-------------------------------------------"); +} + +void ScriptInterpreter::dumpAllScripts() { + int *opcodeStats = new int[_commandsMax - 1]; + int *externStats = new int[_functions->getCount()]; + + for (int i = 0; i < _commandsMax; i++) + opcodeStats[i] = 0; + for (int i = 0; i < _functions->getCount(); i++) + externStats[i] = 0; + + for (uint objectIndex = 1; objectIndex <= _vm->_dat->getObjectCount(); objectIndex++) { + Object *obj = _vm->_dat->getObject(objectIndex); + // Check if it's a byte array which might contain code + if (obj->getClass() != 0x7FFF) + continue; + // Code objects aren't excplicitly marked as such, we need to check if + // the last byte is a cmd_return opcode. + byte *retByte = obj->getData() + obj->getSize() - 1; + if (*retByte == 0x1F) { + dumpScript(objectIndex, opcodeStats, externStats); + } + } + + debug(1, "OPCODE statistics:"); + for (int i = 0; i < _commandsMax - 1; i++) + if (opcodeStats[i] > 0) + debug(1, "%-30s: %d", _commands[i].desc, opcodeStats[i]); + debug(1, "UNUSED OPCODE statistics:"); + for (int i = 0; i < _commandsMax - 1; i++) + if (opcodeStats[i] == 0) + debug(1, "%-30s: %d", _commands[i].desc, opcodeStats[i]); + debug(1, "."); + + debug(1, "EXTERN statistics (%d):", _functions->getCount()); + for (int i = 0; i < _functions->getCount(); i++) + if (externStats[i] > 0) + debug(1, "%-30s: %d", _functions->getFuncName(i), externStats[i]); + debug(1, "UNUSED EXTERN statistics (%d):", _functions->getCount()); + for (int i = 0; i < _functions->getCount(); i++) + if (externStats[i] == 0) + debug(1, "%-30s: %d", _functions->getFuncName(i), externStats[i]); + debug(1, "."); + + delete[] opcodeStats; + delete[] externStats; +} +#endif + } // End of namespace Made diff --git a/engines/made/script.h b/engines/made/script.h index 0ec43478ab..459a088cb8 100644 --- a/engines/made/script.h +++ b/engines/made/script.h @@ -32,6 +32,11 @@ namespace Made { +// Define this to dump all game scripts and a usage statistic of all +// opcodes/extended functions instead of running the actual game. +// Then run ScummVM with debuglevel 1. +//#define DUMP_SCRIPTS + class MadeEngine; class ScriptFunctions; @@ -63,6 +68,8 @@ public: ScriptInterpreter(MadeEngine *vm); ~ScriptInterpreter(); void runScript(int16 scriptObjectIndex); + void dumpScript(int16 objectIndex, int *opcodeStats, int *externStats); + void dumpAllScripts(); protected: MadeEngine *_vm; @@ -71,7 +78,6 @@ protected: int16 _runningScriptObjectIndex; byte *_codeBase, *_codeIp; bool _terminated; - bool _dumpScripts; ScriptFunctions *_functions; @@ -82,6 +88,9 @@ protected: struct CommandEntry { CommandProc proc; const char *desc; +#ifdef DUMP_SCRIPTS + const char *sig; +#endif }; const CommandEntry *_commands; diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp index d5227ecbf5..d627be38d4 100644 --- a/engines/made/scriptfuncs.cpp +++ b/engines/made/scriptfuncs.cpp @@ -319,6 +319,7 @@ int16 ScriptFunctions::sfIsMusicPlaying(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfSetTextPos(int16 argc, int16 *argv) { + // TODO: Used in Manhole:NE warning("Unimplemented opcode: sfSetTextPos"); // This seems to be some kind of low-level opcode. // The original engine calls int 10h to set the VGA cursor position. @@ -331,21 +332,25 @@ int16 ScriptFunctions::sfFlashScreen(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) { + // TODO: Used in Manhole:NE warning("Unimplemented opcode: sfPlayNote"); return 0; } int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) { + // TODO: Used in Manhole:NE warning("Unimplemented opcode: sfStopNote"); return 0; } int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) { + // TODO: Used in Manhole:NE warning("Unimplemented opcode: sfPlayTele"); return 0; } int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) { + // TODO: Used in Manhole:NE warning("Unimplemented opcode: sfStopTele"); return 0; } @@ -374,7 +379,7 @@ int16 ScriptFunctions::sfSetScreenLock(int16 argc, int16 *argv) { int16 ScriptFunctions::sfAddSprite(int16 argc, int16 *argv) { if (_vm->getGameID() == GID_RTZ) { - warning("Unimplemented opcode: sfAddSprite"); + // Unused in RTZ return 0; } if (_vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_MANHOLE) { return argv[2]; @@ -483,6 +488,7 @@ int16 ScriptFunctions::sfDrawText(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfHomeText(int16 argc, int16 *argv) { + // TODO: Used in LGOP2 warning("Unimplemented opcode: sfHomeText"); return 0; } @@ -542,6 +548,7 @@ int16 ScriptFunctions::sfSetSpriteGround(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfLoadResText(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfLoadResText"); return 0; } @@ -611,6 +618,7 @@ int16 ScriptFunctions::sfGetCdTime(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfPlayCdSegment(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfPlayCdSegment"); return 0; } @@ -623,6 +631,7 @@ int16 ScriptFunctions::sfPrintf(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfClearMono(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfClearMono"); return 0; } @@ -637,11 +646,13 @@ int16 ScriptFunctions::sfGetSoundEnergy(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfClearText(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfClearText"); return 1; } int16 ScriptFunctions::sfAnimText(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfAnimText"); return 0; } @@ -692,11 +703,13 @@ int16 ScriptFunctions::sfLoadPicture(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfSetMusicVolume(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfSetMusicVolume"); return 0; } int16 ScriptFunctions::sfRestartEvents(int16 argc, int16 *argv) { + // TODO: Used in RTZ warning("Unimplemented opcode: sfRestartEvents"); return 0; } @@ -724,11 +737,13 @@ int16 ScriptFunctions::sfSetChannelState(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfSetChannelLocation(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfSetChannelLocation"); return 0; } int16 ScriptFunctions::sfSetChannelContent(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfSetChannelContent"); return 0; } @@ -796,6 +811,7 @@ int16 ScriptFunctions::sfSetSoundRate(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfDrawAnimPic(int16 argc, int16 *argv) { + // TODO: Used in RTZ warning("Unimplemented opcode: sfDrawAnimPic"); return 0; } @@ -810,6 +826,7 @@ int16 ScriptFunctions::sfLoadAnim(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfReadText(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfReadText"); return 0; } @@ -913,11 +930,13 @@ int16 ScriptFunctions::sfGetGameDescription(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfShakeScreen(int16 argc, int16 *argv) { + // TODO: Used in RTZ warning("Unimplemented opcode: sfShakeScreen"); return 0; } int16 ScriptFunctions::sfPlaceMenu(int16 argc, int16 *argv) { + // Never used in LGOP2, RTZ, Manhole:NE warning("Unimplemented opcode: sfPlaceMenu"); return 0; } diff --git a/engines/made/scriptfuncs.h b/engines/made/scriptfuncs.h index 2b0f15c7a2..9879556c3f 100644 --- a/engines/made/scriptfuncs.h +++ b/engines/made/scriptfuncs.h @@ -54,6 +54,7 @@ public: } void setupExternalsTable(); const char* getFuncName(int index) { return _externalFuncNames[index]; } + int getCount() const { return _externalFuncs.size(); } protected: MadeEngine *_vm; Audio::SoundHandle _audioStreamHandle; |