/* 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 .
*
*/
#ifndef GLK_COMPREHEND_GAME_DATA_H
#define GLK_COMPREHEND_GAME_DATA_H
#include "glk/comprehend/file_buf.h"
#include "common/serializer.h"
#include "common/str-array.h"
namespace Glk {
namespace Comprehend {
#define MAX_FLAGS 256
#define MAX_VARIABLES 128
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
class ComprehendGame;
enum {
DIRECTION_NORTH,
DIRECTION_SOUTH,
DIRECTION_EAST,
DIRECTION_WEST,
DIRECTION_UP,
DIRECTION_DOWN,
DIRECTION_IN,
DIRECTION_OUT,
NR_DIRECTIONS
};
enum ScriptOpcode {
OPCODE_UNKNOWN,
OPCODE_HAVE_OBJECT,
OPCODE_OR,
OPCODE_IN_ROOM,
OPCODE_VAR_EQ1,
OPCODE_VAR_EQ2,
OPCODE_VAR_GT1,
OPCODE_VAR_GT2,
OPCODE_VAR_GTE1,
OPCODE_VAR_GTE2,
OPCODE_CURRENT_IS_OBJECT,
OPCODE_OBJECT_PRESENT,
OPCODE_ELSE,
OPCODE_OBJECT_IN_ROOM,
OPCODE_CURRENT_OBJECT_NOT_VALID,
OPCODE_INVENTORY_FULL,
OPCODE_INVENTORY_FULL_X,
OPCODE_TEST_FLAG,
OPCODE_CURRENT_OBJECT_IN_ROOM,
OPCODE_HAVE_CURRENT_OBJECT,
OPCODE_OBJECT_IS_NOT_NOWHERE,
OPCODE_CURRENT_OBJECT_PRESENT,
OPCODE_TEST_ROOM_FLAG,
OPCODE_NOT_HAVE_OBJECT,
OPCODE_NOT_IN_ROOM,
OPCODE_CURRENT_OBJECT_NOT_IN_ROOM,
OPCODE_OBJECT_NOT_IN_ROOM,
OPCODE_TEST_NOT_FLAG,
OPCODE_NOT_HAVE_CURRENT_OBJECT,
OPCODE_OBJECT_IS_NOWHERE,
OPCODE_OBJECT_NOT_PRESENT,
OPCODE_CURRENT_OBJECT_IS_NOWHERE,
OPCODE_CURRENT_OBJECT_NOT_PRESENT,
OPCODE_CURRENT_OBJECT_NOT_TAKEABLE,
OPCODE_TEST_NOT_ROOM_FLAG,
OPCODE_INVENTORY,
OPCODE_TAKE_OBJECT,
OPCODE_MOVE_OBJECT_TO_ROOM,
OPCODE_SAVE_ACTION,
OPCODE_CLEAR_LINE,
OPCODE_MOVE_TO_ROOM,
OPCODE_VAR_ADD,
OPCODE_SET_ROOM_DESCRIPTION,
OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM,
OPCODE_VAR_SUB,
OPCODE_SET_OBJECT_DESCRIPTION,
OPCODE_SET_OBJECT_LONG_DESCRIPTION,
OPCODE_MOVE_DEFAULT,
OPCODE_PRINT,
OPCODE_REMOVE_OBJECT,
OPCODE_SET_FLAG,
OPCODE_CALL_FUNC,
OPCODE_CALL_FUNC2,
OPCODE_TURN_TICK,
OPCODE_CLEAR_FLAG,
OPCODE_INVENTORY_ROOM,
OPCODE_TAKE_CURRENT_OBJECT,
OPCODE_SPECIAL,
OPCODE_DROP_OBJECT,
OPCODE_DROP_CURRENT_OBJECT,
OPCODE_SET_ROOM_GRAPHIC,
OPCODE_SET_OBJECT_GRAPHIC,
OPCODE_REMOVE_CURRENT_OBJECT,
OPCODE_MOVE_DIR,
OPCODE_VAR_INC,
OPCODE_VAR_DEC,
OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM,
OPCODE_DESCRIBE_CURRENT_OBJECT,
OPCODE_SET_STRING_REPLACEMENT1,
OPCODE_SET_STRING_REPLACEMENT2,
OPCODE_SET_STRING_REPLACEMENT3,
OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT,
OPCODE_DRAW_ROOM,
OPCODE_DRAW_OBJECT,
OPCODE_WAIT_KEY,
OPCODE_TEST_FALSE,
OPCODE_CAN_TAKE,
OPCODE_TOO_HEAVY,
OPCODE_OBJECT_TAKEABLE,
OPCODE_OBJECT_CAN_TAKE,
OPCODE_CLEAR_INVISIBLE,
OPCODE_SET_INVISIBLE,
OPCODE_CLEAR_CAN_TAKE,
OPCODE_SET_CAN_TAKE,
OPCODE_CLEAR_FLAG40,
OPCODE_SET_FLAG40,
OPCODE_RANDOM_MSG,
OPCODE_SET_WORD,
OPCODE_CLEAR_WORD
};
/* Game state update flags */
#define UPDATE_GRAPHICS (1 << 0) /* Implies UPDATE_GRAPHICS_ITEMS */
#define UPDATE_GRAPHICS_ITEMS (1 << 1)
#define UPDATE_ROOM_DESC (1 << 2)
#define UPDATE_ITEM_LIST (1 << 3)
#define UPDATE_ALL (~0U)
/* Action types */
enum {
ACTION_VERB_VERB_NOUN_NOUN,
ACTION_VERB_NOUN_JOIN_NOUN,
ACTION_VERB_JOIN_NOUN,
ACTION_VERB_DIR_NOUN,
ACTION_VERB_NOUN_NOUN,
ACTION_VERB_NOUN,
ACTION_VERB_OPT_NOUN
};
/* Standard strings (main string table) */
#define STRING_CANT_GO 0
#define STRING_DONT_UNDERSTAND 1
#define STRING_YOU_SEE 2
#define STRING_INVENTORY 3
#define STRING_INVENTORY_EMPTY 4
#define STRING_BEFORE_CONTINUE 5
#define STRING_SAVE_GAME 6
#define STRING_RESTORE_GAME 7
/* Special variables */
#define VAR_INVENTORY_WEIGHT 0
#define VAR_INVENTORY_LIMIT 1
#define VAR_TURN_COUNT 2
/* Special rooms */
#define ROOM_INVENTORY 0x00
#define ROOM_CONTAINER 0xfe
#define ROOM_NOWHERE 0xff
/* Item flags */
enum ItemFlag {
ITEMF_WEIGHT_MASK = 0x7,
ITEMF_CAN_TAKE = 1 << 3,
ITEMF_UNKNOWN = 1 << 6,
ITEMF_INVISIBLE = 1 << 7
};
/* Word types */
#define WORD_TYPE_VERB 0x01
#define WORD_TYPE_JOIN 0x02
#define WORD_TYPE_FEMALE 0x10
#define WORD_TYPE_MALE 0x20
#define WORD_TYPE_NOUN 0x40
#define WORD_TYPE_NOUN_PLURAL 0x80
#define WORD_TYPE_NOUN_MASK (WORD_TYPE_FEMALE | WORD_TYPE_MALE | \
WORD_TYPE_NOUN | WORD_TYPE_NOUN_PLURAL)
struct FunctionState {
bool _testResult;
bool _elseResult;
uint _orCount;
bool _and;
bool _inCommand;
bool _executed;
bool _notComparison;
FunctionState() {
clear();
}
void clear();
};
struct Room {
uint8 _direction[NR_DIRECTIONS];
uint8 _flags;
uint8 _graphic;
uint16 _stringDesc;
Room() {
clear();
}
void clear();
};
struct Item {
uint16 _stringDesc;
uint16 _longString; /* Only used by version 2 */
uint8 _room;
uint8 _flags;
uint8 _word;
uint8 _graphic;
Item() {
clear();
}
void clear();
void synchronize(Common::Serializer &s);
};
struct WordIndex {
uint8 _index;
uint8 _type;
WordIndex() {
clear();
}
void clear() {
_index = _type = 0;
}
bool operator==(WordIndex &src) {
return _index == src._index && _type == src._type;
}
bool operator()() const {
return _index != 0;
}
};
struct Word : public WordIndex {
char _word[7];
Word() : WordIndex() {
Word::clear();
}
void clear();
void load(FileBuffer *fb);
Word &operator=(const WordIndex &src);
};
struct WordMap {
/* , == */
WordIndex _word[3];
uint8 _flags;
WordMap() {
clear();
}
void clear();
};
struct Action {
size_t _nr_words;
uint8 _words[4];
uint16 _function;
Action() {
clear();
}
void clear();
};
struct Instruction {
uint8 _opcode;
size_t _nr_operands;
uint8 _operand[3];
bool _isCommand;
Instruction() {
clear();
}
Instruction(byte opcode, byte op1 = 0, byte op2 = 0, byte op3 = 0);
void clear();
};
typedef Common::Array Function;
typedef Common::StringArray StringTable;
struct StringFile {
Common::String _filename;
uint32 _baseOffset;
uint32 _endOffset;
StringFile() : _baseOffset(0), _endOffset(0) {
}
StringFile(const char *fname, uint32 baseOfs = 0, uint32 endO = 0) :
_filename(fname), _baseOffset(baseOfs), _endOffset(endO) {
}
};
struct GameHeader {
uint16 magic;
uint16 room_desc_table;
uint16 room_direction_table[NR_DIRECTIONS];
uint16 room_flags_table;
uint16 room_graphics_table;
size_t nr_items;
uint16 addr_item_locations;
uint16 addr_item_flags;
uint16 addr_item_word;
uint16 addr_item_strings;
uint16 addr_item_graphics;
uint16 addr_dictionary;
uint16 addr_word_map;
uint16 addr_word_map_target;
uint16 addr_strings;
uint16 addr_strings_end;
uint16 addr_actions[7];
uint16 addr_vm; // FIXME - functions
GameHeader() {
clear();
}
void clear();
};
typedef Common::Array ActionTable;
class GameData {
private:
uint16 _magicWord;
protected:
Common::String _gameDataFile;
Common::Array _stringFiles;
Common::StringArray _locationGraphicFiles;
Common::StringArray _itemGraphicFiles;
Common::String _titleGraphicFile;
uint _colorTable;
public:
GameHeader _header;
uint _comprehendVersion;
Common::Array _rooms;
uint8 _currentRoom;
uint8 _startRoom;
uint8 _itemCount;
uint8 _totalInventoryWeight;
Common::Array- _items;
Common::Array _words;
StringTable _strings;
StringTable _strings2;
bool _flags[MAX_FLAGS];
uint16 _variables[MAX_VARIABLES];
uint8 _currentReplaceWord;
uint8 _wordFlags;
uint _updateFlags;
Common::Array _wordMaps;
Common::Array _actions;
Common::Array _functions;
Common::StringArray _replaceWords;
private:
size_t opcode_nr_operands(uint8 opcode) const {
// Number of operands is encoded in the low 2 bits
return opcode & 0x3;
}
bool opcode_is_command(uint8 opcode) const {
/* If the MSB is set the instruction is a command */
return opcode & 0x80;
}
void load_extra_string_files();
void load_extra_string_file(const StringFile &stringFile);
void parse_header_le16(FileBuffer *fb, uint16 *val);
uint8 parse_vm_instruction(FileBuffer *fb, Instruction *instr);
void parse_function(FileBuffer *fb, Function *func);
void parse_vm(FileBuffer *fb);
void parse_action_tables(FileBuffer *fb);
void parse_dictionary(FileBuffer *fb);
void parse_word_map(FileBuffer *fb);
void parse_items(FileBuffer *fb);
void parse_rooms(FileBuffer *fb);
uint64 string_get_chunk(uint8 *string);
char decode_string_elem(uint8 c, bool capital, bool special);
void parse_string_table(FileBuffer *fb, uint start_addr,
uint32 end_addr, StringTable *table);
void parse_variables(FileBuffer *fb);
void parse_flags(FileBuffer *fb);
void parse_replace_words(FileBuffer *fb);
void loadGameData();
protected:
/**
* Game strings are stored using 5-bit characters. By default a character
* value maps to the lower-case letter table. If a character has the value 0x1e
* then the next character is upper-case. An upper-case space is used to
* specify that the character should be replaced at runtime (like a '%s'
* specifier). If a character has the value 0x1f then the next character is
* taken from the symbols table.
*/
Common::String parseString(FileBuffer *fb);
/**
* The main game data file header has the offsets for where each bit of
* game data is. The offsets have a magic constant value added to them.
*/
virtual void parse_header(FileBuffer *fb);
public:
GameData() {
clearGame();
}
virtual ~GameData() {
clearGame();
}
void clearGame();
void loadGame();
};
} // namespace Comprehend
} // namespace Glk
#endif