mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 19:54:03 +00:00
445 lines
15 KiB
C++
445 lines
15 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "glk/comprehend/debugger_dumper.h"
|
|
#include "glk/comprehend/dictionary.h"
|
|
#include "glk/comprehend/game.h"
|
|
|
|
namespace Glk {
|
|
namespace Comprehend {
|
|
|
|
DebuggerDumper::DebuggerDumper() : _game(nullptr) {
|
|
_opcodes[OPCODE_HAVE_OBJECT] = "have_object";
|
|
_opcodes[OPCODE_NOT_HAVE_OBJECT] = "not_have_object";
|
|
_opcodes[OPCODE_HAVE_CURRENT_OBJECT] = "have_current_object";
|
|
_opcodes[OPCODE_NOT_HAVE_CURRENT_OBJECT] = "not_have_current_object";
|
|
|
|
_opcodes[OPCODE_OBJECT_IS_NOT_NOWHERE] = "object_is_not_nowhere";
|
|
|
|
_opcodes[OPCODE_CURRENT_IS_OBJECT] = "current_is_object";
|
|
_opcodes[OPCODE_CURRENT_OBJECT_NOT_TAKEABLE] = "current_object_not_takeable";
|
|
|
|
_opcodes[OPCODE_CURRENT_OBJECT_NOT_IN_ROOM] = "current_object_not_in_room";
|
|
_opcodes[OPCODE_CURRENT_OBJECT_IS_NOWHERE] = "current_object_is_nowhere";
|
|
|
|
_opcodes[OPCODE_CURRENT_OBJECT_NOT_PRESENT] = "current_object_not_present";
|
|
|
|
_opcodes[OPCODE_TAKE_OBJECT] = "take_object";
|
|
_opcodes[OPCODE_TAKE_CURRENT_OBJECT] = "take_current_object";
|
|
_opcodes[OPCODE_DROP_OBJECT] = "drop_object";
|
|
_opcodes[OPCODE_DROP_CURRENT_OBJECT] = "drop_current_object";
|
|
|
|
_opcodes[OPCODE_OR] = "or";
|
|
_opcodes[OPCODE_IN_ROOM] = "in_room";
|
|
_opcodes[OPCODE_VAR_EQ1] = "var_eq1";
|
|
_opcodes[OPCODE_VAR_EQ2] = "var_eq2";
|
|
_opcodes[OPCODE_VAR_GT1] = "var_gt1";
|
|
_opcodes[OPCODE_VAR_GT2] = "var_gt2";
|
|
_opcodes[OPCODE_VAR_GTE1] = "var_gte1";
|
|
_opcodes[OPCODE_VAR_GTE2] = "var_gte2";
|
|
_opcodes[OPCODE_CURRENT_OBJECT_NOT_VALID] = "current_object_not_valid";
|
|
_opcodes[OPCODE_INVENTORY_FULL] = "inventory_full";
|
|
_opcodes[OPCODE_INVENTORY_FULL_X] = "inventory_full_x";
|
|
_opcodes[OPCODE_OBJECT_PRESENT] = "object_present";
|
|
_opcodes[OPCODE_ELSE] = "else";
|
|
_opcodes[OPCODE_OBJECT_IN_ROOM] = "object_in_room";
|
|
_opcodes[OPCODE_TEST_FLAG] = "test_flag";
|
|
_opcodes[OPCODE_CURRENT_OBJECT_IN_ROOM] = "current_object_in_room";
|
|
_opcodes[OPCODE_CURRENT_OBJECT_PRESENT] = "current_object_present";
|
|
_opcodes[OPCODE_TEST_ROOM_FLAG] = "test_room_flag";
|
|
_opcodes[OPCODE_NOT_IN_ROOM] = "not_in_room";
|
|
_opcodes[OPCODE_OBJECT_NOT_PRESENT] = "object_not_present";
|
|
_opcodes[OPCODE_OBJECT_NOT_IN_ROOM] = "object_not_in_room";
|
|
_opcodes[OPCODE_TEST_NOT_FLAG] = "test_not_flag";
|
|
_opcodes[OPCODE_OBJECT_IS_NOWHERE] = "object_is_nowhere";
|
|
_opcodes[OPCODE_TEST_NOT_ROOM_FLAG] = "test_not_room_flag";
|
|
_opcodes[OPCODE_INVENTORY] = "inventory";
|
|
_opcodes[OPCODE_MOVE_OBJECT_TO_ROOM] = "move_object_to_room";
|
|
_opcodes[OPCODE_SAVE_ACTION] = "save_action";
|
|
_opcodes[OPCODE_CLEAR_LINE] = "clear_line";
|
|
_opcodes[OPCODE_MOVE_TO_ROOM] = "move_to_room";
|
|
_opcodes[OPCODE_VAR_ADD] = "var_add";
|
|
_opcodes[OPCODE_SET_ROOM_DESCRIPTION] = "set_room_description";
|
|
_opcodes[OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM] = "move_object_to_current_room";
|
|
_opcodes[OPCODE_VAR_SUB] = "var_sub";
|
|
_opcodes[OPCODE_SET_OBJECT_DESCRIPTION] = "set_object_description";
|
|
_opcodes[OPCODE_SET_OBJECT_LONG_DESCRIPTION] = "set_object_long_description";
|
|
_opcodes[OPCODE_MOVE_DEFAULT] = "move_default";
|
|
_opcodes[OPCODE_PRINT] = "print";
|
|
_opcodes[OPCODE_REMOVE_OBJECT] = "remove_object";
|
|
_opcodes[OPCODE_SET_FLAG] = "set_flag";
|
|
_opcodes[OPCODE_CALL_FUNC] = "call_func";
|
|
_opcodes[OPCODE_CALL_FUNC2] = "call_func2";
|
|
_opcodes[OPCODE_TURN_TICK] = "turn_tick";
|
|
_opcodes[OPCODE_CLEAR_FLAG] = "clear_flag";
|
|
_opcodes[OPCODE_INVENTORY_ROOM] = "inventory_room";
|
|
_opcodes[OPCODE_SPECIAL] = "special";
|
|
_opcodes[OPCODE_SET_ROOM_GRAPHIC] = "set_room_graphic";
|
|
_opcodes[OPCODE_SET_OBJECT_GRAPHIC] = "set_object_graphic";
|
|
_opcodes[OPCODE_REMOVE_CURRENT_OBJECT] = "remove_current_object";
|
|
_opcodes[OPCODE_MOVE_DIR] = "move_dir";
|
|
_opcodes[OPCODE_VAR_INC] = "var_inc";
|
|
_opcodes[OPCODE_VAR_DEC] = "var_dec";
|
|
_opcodes[OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM] = "move_current_object_to_room";
|
|
_opcodes[OPCODE_DESCRIBE_CURRENT_OBJECT] = "describe_current_object";
|
|
_opcodes[OPCODE_SET_STRING_REPLACEMENT1] = "set_string_replacement1";
|
|
_opcodes[OPCODE_SET_STRING_REPLACEMENT2] = "set_string_replacement2";
|
|
_opcodes[OPCODE_SET_STRING_REPLACEMENT3] = "set_string_replacement3";
|
|
_opcodes[OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT] = "set_current_noun_string_replacement";
|
|
_opcodes[OPCODE_DRAW_ROOM] = "draw_room";
|
|
_opcodes[OPCODE_DRAW_OBJECT] = "draw_object";
|
|
_opcodes[OPCODE_WAIT_KEY] = "wait_key";
|
|
_opcodes[OPCODE_TEST_FALSE] = "test_false";
|
|
_opcodes[OPCODE_OBJECT_CAN_TAKE] = "object_can_take";
|
|
_opcodes[OPCODE_OBJECT_TAKEABLE] = "object_takeable";
|
|
_opcodes[OPCODE_CLEAR_INVISIBLE] = "clear_invisible";
|
|
_opcodes[OPCODE_SET_INVISIBLE] = "set_invisible";
|
|
_opcodes[OPCODE_CLEAR_CAN_TAKE] = "clear_can_take";
|
|
_opcodes[OPCODE_SET_CAN_TAKE] = "set_can_take";
|
|
_opcodes[OPCODE_SET_FLAG40] = "set_flag40";
|
|
_opcodes[OPCODE_CLEAR_FLAG40] = "clear_flag40";
|
|
_opcodes[OPCODE_RANDOM_MSG] = "random_msg";
|
|
_opcodes[OPCODE_SET_WORD] = "set_word";
|
|
_opcodes[OPCODE_CLEAR_WORD] = "clear_word";
|
|
}
|
|
|
|
Common::String DebuggerDumper::dumpInstruction(ComprehendGame *game,
|
|
const FunctionState *func_state, const Instruction *instr) {
|
|
uint i;
|
|
int str_index, str_table;
|
|
ScriptOpcode opcode = _game->getScriptOpcode(instr);
|
|
Common::String line;
|
|
|
|
if (func_state)
|
|
line = Common::String::format("[or=%d,and=%d,test=%d,else=%d]",
|
|
func_state->_orCount, func_state->_and,
|
|
func_state->_testResult, func_state->_elseResult);
|
|
|
|
line += Common::String::format(" [%.2x] ", instr->_opcode);
|
|
if (_opcodes.contains(opcode)) {
|
|
if (_game->_comprehendVersion == 2 && !instr->_isCommand && (instr->_opcode & 0x40) != 0)
|
|
line += "!";
|
|
line += _opcodes[opcode];
|
|
} else {
|
|
line += "unknown";
|
|
}
|
|
|
|
if (instr->_nr_operands) {
|
|
line += "(";
|
|
for (i = 0; i < instr->_nr_operands; i++)
|
|
line += Common::String::format("%.2x%s",
|
|
instr->_operand[i],
|
|
i == (instr->_nr_operands - 1) ? ")" : ", ");
|
|
}
|
|
|
|
switch (opcode) {
|
|
case OPCODE_PRINT:
|
|
case OPCODE_SET_ROOM_DESCRIPTION:
|
|
case OPCODE_SET_OBJECT_DESCRIPTION:
|
|
case OPCODE_SET_OBJECT_LONG_DESCRIPTION:
|
|
|
|
if (opcode == OPCODE_PRINT) {
|
|
str_index = instr->_operand[0];
|
|
str_table = instr->_operand[1];
|
|
} else {
|
|
str_index = instr->_operand[1];
|
|
str_table = instr->_operand[2];
|
|
}
|
|
|
|
line += Common::String::format(" %s", game->instrStringLookup(str_index, str_table).c_str());
|
|
break;
|
|
|
|
case OPCODE_SET_STRING_REPLACEMENT1:
|
|
case OPCODE_SET_STRING_REPLACEMENT2:
|
|
case OPCODE_SET_STRING_REPLACEMENT3:
|
|
str_index = instr->_operand[0] - 1;
|
|
if (str_index < 0 || str_index >= (int)game->_replaceWords.size())
|
|
warning("invalid string replacement index - %d", str_index);
|
|
else
|
|
line += Common::String::format(" %s", game->_replaceWords[str_index].c_str());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return line;
|
|
}
|
|
|
|
void DebuggerDumper::dumpFunctions() {
|
|
uint i;
|
|
|
|
print("Functions (%u entries)\n", _game->_functions.size());
|
|
for (i = 0; i < _game->_functions.size(); i++)
|
|
dumpFunction(i);
|
|
}
|
|
|
|
void DebuggerDumper::dumpFunction(uint functionNum) {
|
|
const Function &func = _game->_functions[functionNum];
|
|
|
|
print("[%.4x] (%u instructions)\n", functionNum, func.size());
|
|
for (uint i = 0; i < func.size(); i++) {
|
|
Common::String line = dumpInstruction(_game, nullptr, &func[i]);
|
|
print("%s\n", line.c_str());
|
|
}
|
|
|
|
print("\n");
|
|
}
|
|
|
|
void DebuggerDumper::dumpActionTable() {
|
|
Action *action;
|
|
uint i, j;
|
|
|
|
print("Action tables: %u tables\n", _game->_actions.size());
|
|
|
|
for (uint tableNum = 0; tableNum < _game->_actions.size(); ++tableNum) {
|
|
ActionTable &table = _game->_actions[tableNum];
|
|
|
|
print("Action table #u (%u entries)\n", tableNum, table.size());
|
|
for (i = 0; i < table.size(); i++) {
|
|
action = &table[i];
|
|
|
|
print(" [%.4x] ", i);
|
|
|
|
for (j = 0; j < action->_nr_words; j++)
|
|
print("%.2x ", action->_words[j]);
|
|
|
|
print("-> %.4x\n", action->_function);
|
|
}
|
|
}
|
|
}
|
|
|
|
int DebuggerDumper::wordIndexCompare(const Word &a, const Word &b) {
|
|
if (a._index > b._index)
|
|
return 1;
|
|
if (a._index < b._index)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
void DebuggerDumper::dumpDictionary() {
|
|
Common::Array<Word> dictionary;
|
|
|
|
/* Sort the dictionary by index */
|
|
dictionary = _game->_words;
|
|
Common::sort(dictionary.begin(), dictionary.end(), wordIndexCompare);
|
|
|
|
print("Dictionary (%u words)\n", dictionary.size());
|
|
for (uint i = 0; i < dictionary.size(); i++) {
|
|
const Word &word = dictionary[i];
|
|
print(" [%.2x] %.2x %s\n", word._index, word._type, word._word);
|
|
}
|
|
}
|
|
|
|
void DebuggerDumper::dumpWordMap() {
|
|
Word *word[3];
|
|
char str[3][7];
|
|
WordMap *map;
|
|
uint i, j;
|
|
|
|
print("Word pairs (%u entries)\n", _game->_wordMaps.size());
|
|
for (i = 0; i < _game->_wordMaps.size(); i++) {
|
|
map = &_game->_wordMaps[i];
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
word[j] = dict_find_word_by_index_type(
|
|
_game, map->_word[j]._index, map->_word[j]._type);
|
|
if (word[j])
|
|
snprintf(str[j], sizeof(str[j]),
|
|
"%s", word[j]->_word);
|
|
else
|
|
snprintf(str[j], sizeof(str[j]), "%.2x:%.2x ",
|
|
map->_word[j]._index, map->_word[j]._type);
|
|
}
|
|
|
|
print(" [%.2x] %-6s %-6s -> %-6s\n",
|
|
i, str[0], str[1], str[2]);
|
|
}
|
|
}
|
|
|
|
void DebuggerDumper::dumpRooms() {
|
|
Room *room;
|
|
uint i;
|
|
|
|
// Room zero acts as the players inventory
|
|
print("Rooms (%u entries)\n", (uint)_game->_rooms.size() - 1);
|
|
for (i = 1; i < _game->_rooms.size(); i++) {
|
|
room = &_game->_rooms[i];
|
|
|
|
print(" [%.2x] flags=%.2x, graphic=%.2x\n",
|
|
i, room->_flags, room->_graphic);
|
|
print(" %s\n", _game->stringLookup(room->_stringDesc).c_str());
|
|
print(" n: %.2x s: %.2x e: %.2x w: %.2x\n",
|
|
room->_direction[DIRECTION_NORTH],
|
|
room->_direction[DIRECTION_SOUTH],
|
|
room->_direction[DIRECTION_EAST],
|
|
room->_direction[DIRECTION_WEST]);
|
|
print(" u: %.2x d: %.2x i: %.2x o: %.2x\n",
|
|
room->_direction[DIRECTION_UP],
|
|
room->_direction[DIRECTION_DOWN],
|
|
room->_direction[DIRECTION_IN],
|
|
room->_direction[DIRECTION_OUT]);
|
|
print("\n");
|
|
}
|
|
}
|
|
|
|
void DebuggerDumper::dumpItems() {
|
|
Item *item;
|
|
uint i, j;
|
|
|
|
print("Items (%u entries)\n", _game->_items.size());
|
|
for (i = 0; i < _game->_items.size(); i++) {
|
|
item = &_game->_items[i];
|
|
|
|
print(" [%.2x] %s\n", i + 1,
|
|
item->_stringDesc ? _game->stringLookup(item->_stringDesc).c_str() : "");
|
|
if (_game->_comprehendVersion == 2)
|
|
print(" long desc: %s\n",
|
|
_game->stringLookup(item->_longString).c_str());
|
|
|
|
print(" words: ");
|
|
for (j = 0; j < _game->_words.size(); j++)
|
|
if (_game->_words[j]._index == item->_word &&
|
|
(_game->_words[j]._type & WORD_TYPE_NOUN_MASK))
|
|
print("%s ", _game->_words[j]._word);
|
|
print("\n");
|
|
print(" flags=%.2x (takeable=%d, weight=%d)\n",
|
|
item->_flags, !!(item->_flags & ITEMF_CAN_TAKE),
|
|
(item->_flags & ITEMF_WEIGHT_MASK));
|
|
print(" room=%.2x, graphic=%.2x\n",
|
|
item->_room, item->_graphic);
|
|
print("\n");
|
|
}
|
|
}
|
|
|
|
void DebuggerDumper::dumpStringTable(Common::StringArray &table) {
|
|
uint i;
|
|
|
|
for (i = 0; i < table.size(); i++)
|
|
print("[%.4x] %s\n", i, table[i].c_str());
|
|
}
|
|
|
|
void DebuggerDumper::dumpGameDataStrings() {
|
|
print("Main string table (%u entries)\n",
|
|
_game->_strings.size());
|
|
dumpStringTable(_game->_strings);
|
|
}
|
|
|
|
void DebuggerDumper::dumpExtraStrings() {
|
|
print("Extra strings (%u entries)\n",
|
|
_game->_strings2.size());
|
|
dumpStringTable(_game->_strings2);
|
|
}
|
|
|
|
void DebuggerDumper::dumpReplaceWords() {
|
|
uint i;
|
|
|
|
print("Replacement words (%u entries)\n",
|
|
_game->_replaceWords.size());
|
|
for (i = 0; i < _game->_replaceWords.size(); i++)
|
|
print(" [%.2x] %s\n", i + 1, _game->_replaceWords[i].c_str());
|
|
}
|
|
|
|
void DebuggerDumper::dumpHeader() {
|
|
GameHeader *header = &_game->_header;
|
|
uint16 *dir_table = header->room_direction_table;
|
|
|
|
print("Game header:\n");
|
|
print(" magic: %.4x\n", header->magic);
|
|
|
|
print(" functions: %.4x\n", header->addr_vm);
|
|
print(" dictionary: %.4x\n", header->addr_dictionary);
|
|
print(" word map pairs: %.4x\n", header->addr_word_map);
|
|
print(" room desc strings: %.4x\n", header->room_desc_table);
|
|
print(" room north: %.4x\n", dir_table[DIRECTION_NORTH]);
|
|
print(" room south: %.4x\n", dir_table[DIRECTION_SOUTH]);
|
|
print(" room east: %.4x\n", dir_table[DIRECTION_EAST]);
|
|
print(" room west: %.4x\n", dir_table[DIRECTION_WEST]);
|
|
print(" room up: %.4x\n", dir_table[DIRECTION_UP]);
|
|
print(" room down: %.4x\n", dir_table[DIRECTION_DOWN]);
|
|
print(" room in: %.4x\n", dir_table[DIRECTION_IN]);
|
|
print(" room out: %.4x\n", dir_table[DIRECTION_OUT]);
|
|
print(" room flags: %.4x\n", header->room_flags_table);
|
|
print(" room images: %.4x\n", header->room_graphics_table);
|
|
print(" item locations: %.4x\n", header->addr_item_locations);
|
|
print(" item flags: %.4x\n", header->addr_item_flags);
|
|
print(" item words: %.4x\n", header->addr_item_word);
|
|
print(" item desc strings: %.4x\n", header->addr_item_strings);
|
|
print(" item images: %.4x\n", header->addr_item_graphics);
|
|
print(" string table: %.4x\n", header->addr_strings);
|
|
print(" string table end: %.4x\n", header->addr_strings_end);
|
|
}
|
|
|
|
void DebuggerDumper::dumpState() {
|
|
print("Current room: %.2x\n", _game->_currentRoom);
|
|
print("Carry weight %d/%d\n\n",
|
|
_game->_variables[VAR_INVENTORY_WEIGHT],
|
|
_game->_variables[VAR_INVENTORY_LIMIT]);
|
|
|
|
print("Flags:\n");
|
|
for (uint i = 0; i < ARRAY_SIZE(_game->_flags); i++)
|
|
print(" [%.2x]: %d\n", i, _game->_flags[i]);
|
|
print("\n");
|
|
|
|
print("Variables:\n");
|
|
for (uint i = 0; i < ARRAY_SIZE(_game->_variables); i++)
|
|
print(" [%.2x]: %5d (0x%.4x)\n",
|
|
i, _game->_variables[i],
|
|
_game->_variables[i]);
|
|
print("\n");
|
|
}
|
|
|
|
bool DebuggerDumper::dumpGameData(ComprehendGame *game, const Common::String &type, int param) {
|
|
_game = game;
|
|
|
|
if (type == "header")
|
|
dumpHeader();
|
|
else if (type == "strings")
|
|
dumpGameDataStrings();
|
|
else if (type == "extra_strings")
|
|
dumpExtraStrings();
|
|
else if (type == "rooms")
|
|
dumpRooms();
|
|
else if (type == "items")
|
|
dumpItems();
|
|
else if (type == "dictionary")
|
|
dumpDictionary();
|
|
else if (type == "word_map")
|
|
dumpWordMap();
|
|
else if (type == "actions")
|
|
dumpActionTable();
|
|
else if (type == "functions")
|
|
dumpFunctions();
|
|
else if (type == "function")
|
|
dumpFunction(param);
|
|
else if (type == "replace_words")
|
|
dumpReplaceWords();
|
|
else if (type == "state")
|
|
dumpState();
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace Comprehend
|
|
} // namespace Glk
|