Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
/* 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/quest/detection.h"
#include "glk/quest/detection_tables.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/md5.h"
#include "engines/game.h"
namespace Glk {
namespace Quest {
void QuestMetaEngine::getSupportedGames(PlainGameList &games) {
for (const PlainGameDescriptor *pd = QUEST_GAME_LIST; pd->gameId; ++pd)
games.push_back(*pd);
}
const GlkDetectionEntry* QuestMetaEngine::getDetectionEntries() {
return QUEST_GAMES;
}
GameDescriptor QuestMetaEngine::findGame(const char *gameId) {
for (const PlainGameDescriptor *pd = QUEST_GAME_LIST; pd->gameId; ++pd) {
if (!strcmp(gameId, pd->gameId))
return *pd;
}
return GameDescriptor::empty();
}
bool QuestMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
if (file->isDirectory())
continue;
Common::String filename = file->getName();
if (!filename.hasSuffixIgnoreCase(".cas") && !filename.hasSuffixIgnoreCase(".asl")
#ifdef QUEST_EXT
&& !filename.hasSuffixIgnoreCase(".quest")
#endif
)
continue;
Common::File gameFile;
if (!gameFile.open(*file))
continue;
gameFile.seek(0);
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
uint32 filesize = gameFile.size();
// Scan through the Quest game list for a match
const GlkDetectionEntry *p = QUEST_GAMES;
while (p->_md5 && p->_filesize != filesize && md5 != p->_md5)
++p;
if (!p->_gameId) {
const PlainGameDescriptor &desc = QUEST_GAME_LIST[0];
gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
} else {
// Found a match
PlainGameDescriptor gameDesc = findGame(p->_gameId);
gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, p->_extra, filename, p->_language));
}
}
return !gameList.empty();
}
void QuestMetaEngine::detectClashes(Common::StringMap &map) {
for (const PlainGameDescriptor *pd = QUEST_GAME_LIST; pd->gameId; ++pd) {
if (map.contains(pd->gameId))
error("Duplicate game Id found - %s", pd->gameId);
map[pd->gameId] = "";
}
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_DETECTION
#define GLK_QUEST_DETECTION
#include "common/fs.h"
#include "common/hash-str.h"
#include "engines/game.h"
#include "glk/detection.h"
namespace Glk {
namespace Quest {
class QuestMetaEngine {
public:
/**
* Get a list of supported games
*/
static void getSupportedGames(PlainGameList &games);
/**
* Get the detection entries
*/
static const GlkDetectionEntry* getDetectionEntries();
/**
* Returns a game description for the given game Id, if it's supported
*/
static GameDescriptor findGame(const char *gameId);
/**
* Detect supported games
*/
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
/**
* Check for game Id clashes with other sub-engines
*/
static void detectClashes(Common::StringMap &map);
};
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,136 @@
/* 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 "engines/game.h"
#include "common/language.h"
namespace Glk {
namespace Quest {
const PlainGameDescriptor QUEST_GAME_LIST[] = {
{ "quest", "Quest Game" },
{ "adventureq", "Adventure!" },
{ "attemptedassassination", "Attempted Assassination" },
{ "beam", "Beam" },
{ "bladesentinel", "The Blade Sentinel" },
{ "gatheredindarkness", "Gathered in Darkness" },
{ "hauntedhorror", "Haunted Horror" },
{ "lovesong", "Lovesong" },
{ "magicworld", "Magic World" },
{ "redsaucemonday", "Red Sauce Monday" },
{ "worldsend", "World's End" },
#ifdef QUEST_EXT
// The games below are newer games that the Geas interpreter doesn't yet support
{ "arcii", "ARC II" },
{ "attackonfrightside", "Attack On Frightside" },
{ "balaclava", "Balaclava" },
{ "bearsepicquest", "Bear's Epic Quest" },
{ "caught", "Caught!" },
{ "cuttings", "Cuttings" },
{ "draculacrl", "Dracula: CRL remake" },
{ "dreampieces", "Dream Pieces" },
{ "elections4", "It's election time in Pakistan: Go rich boy, go!" },
{ "escapebathhouse", "Escape from the Mechanical Bathhouse" },
{ "everyman", "Everyman" },
{ "exittheroom", "Exit the Room" },
{ "firstTimes", "First Times" },
{ "giftofthemagi", "Gift of the Magi" },
{ "medievalistsquest", "Medievalist's Quest" },
{ "micky", "Micky" },
{ "parishotel", "Welcome to the Paris Hotel" },
{ "questforloot", "Quest for loot and something else" },
{ "signos", "Signos" },
{ "sleepingassassin", "El asesino durmiente (The Sleeping Assassin)" },
{ "spondre", "Spondre" },
{ "murderjennylee", "The Brutal Murder of Jenny Lee" },
{ "thelasthero", "The Last Hero" },
{ "myothianfalcon", "The Myothian Falcon" },
{ "tokindlealight", "To Kindle a Light" },
{ "welcometoparishotel", "Welcome to the Paris Hotel!" },
{ "xanadu", "Xanadu - The World's Only Hope" },
#endif
{ nullptr, nullptr }
};
const GlkDetectionEntry QUEST_GAMES[] = {
DT_ENTRY0("adventureq", "93a358f817066494dbdabf222fc20595", 6974),
DT_ENTRY0("attemptedassassination", "e8cf55898bcc5ee43a2527d5fefeaaff", 18833),
DT_ENTRY0("hauntedhorror", "89a5d511aed564d4810b372d271e33fa", 19635),
DT_ENTRY0("magicworld", "463cf8919c7321f3af305534b7ae78f3", 15176),
DT_ENTRY0("redsaucemonday", "5a2f3e25d4a8c77e0c53d980dbb37451", 20324),
DT_ENTRY0("worldsend", "4f5daac10085927bf5180bea24f7ef0d", 73396),
// Competition 2001
DT_ENTRY0("lovesong", "2ea679cc6ee0735141571eb96075523d", 41264),
// Competition 2002
DT_ENTRY0("bladesentinel", "c348d637606430e2597678927c2f6a69", 51473),
// Competition 2006
DT_ENTRY1("beam", "1.10", "d696f04cb7e6851e0bcbde0dbd78cbd0", 68543),
// Competition 2007
DT_ENTRY0("gatheredindarkness", "40f75b697ffb77bba88da6b431efad78", 1052274),
#ifdef QUEST_EXT
// The games below are newer games that the Geas interpreter doesn't yet support
DT_ENTRY0("arcii", "3c208e2afd9f032508de9bebf1b83f9a", 17268977),
DT_ENTRY0("attackonfrightside", "84542fc6460833bbf2594ed83f8b1fc7", 46019),
DT_ENTRY0("balaclava", "8b30af05d9986f9f962c677181ecc766", 57719),
DT_ENTRY0("bearsepicquest", "e6896a65527f456b4362aaebcf39e354", 62075),
DT_ENTRY0("caught", "4502d89d8e304fe4165d46eb22f21f10", 5168593),
DT_ENTRY0("cuttings", "e0ded5a6b78e8c9482e746d55f61972c", 6583866),
DT_ENTRY0("draculacrl", "1af3ec877584b290f7ab1a1be8f944a5", 4548737),
DT_ENTRY0("elections4", "d0bc0cd54182d6099808767068592b94", 591994),
DT_ENTRY0("escapebathhouse", "02f7ba339e50c8faa8c5dc3a1c051d7b", 1067048),
DT_ENTRY0("everyman", "410c7211d3f0c700f34e97ed258e33f1", 56218),
DT_ENTRY1("exittheroom", "1.9", "5b600a1833b59ad115cb868ccc9d6f14", 129094),
DT_ENTRY0("firstTimes", "31d878c82d99856d473762612f154eb6", 10253826),
DT_ENTRY0("giftofthemagi", "b33132ce71c8a2eed0f6c1c1af284765", 78647),
DT_ENTRY0("medievalistsquest", "e0a15bc2a74a0bd6bb5c24661ea35829", 127977271),
DT_ENTRY0("micky", "9c2aa213bb73d8083506ee6f64436d9d", 287227),
DT_ENTRY0("parishotel", "c9a42bc3f306aba5e318b0a74115e0d4", 474983),
DT_ENTRY0("questforloot", "f7e32aec0f961a59a69bead3fadff4f0", 1357373),
DT_ENTRY0("sleepingassassin", "9c2aa213bb73d8083506ee6f64436d9d", 287227),
DT_ENTRY1("spondre", "1.1a", "c639077eb487eb6d1b63cda2c9ba5a9b", 1169469),
DT_ENTRY0("murderjennylee", "27d0f3ff28fd13e556203ab3d53edbe6", 80486),
DT_ENTRY0("thelasthero", "31e10b8a7f11a6289955b89437f8178c", 62512),
DT_ENTRY1("tokindlealight", "1.2", "5d3b57830b003046a621620ba0869d7c", 811845),
DT_ENTRY0("welcometoparishotel", "c9a42bc3f306aba5e318b0a74115e0d4", 474983),
DT_ENTRY0("xanadu", "fef25e3473755ec572d4236d56f918e2", 396973),
// Competition 2011
DT_ENTRY1("myothianfalcon", "1.2", "b5e7680eeb4f27c195f58472fa8eb146", 97861),
// Competition 2012
DT_ENTRY0("signos", "636793562d75ee82a4ea10d3bd3c62d6", 2311079),
// Competition 2013
DT_ENTRY1("dreampieces", "5.0", "fcb0fcc94ba24ba308415fd02a6f6c95", 274779),
#endif
DT_END_MARKER
};
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,656 @@
/* 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/quest/geas_file.h"
#include "glk/quest/reserved_words.h"
#include "glk/quest/read_file.h"
#include "glk/quest/geas_util.h"
#include "glk/quest/geas_impl.h"
#include "glk/quest/streams.h"
#include "glk/quest/string.h"
namespace Glk {
namespace Quest {
void report_error(const String &s);
// FIXME: This requires global constructor
//reserved_words room_tag_property("look", "alias", "prefix", "indescription", "description", "north", "south", "east", "west", "northwest", "northeast", "southeast", "southwest", "up", "down", "out", (char *) NULL);
void GeasFile::debug_print(String s) const {
if (gi == nullptr)
cerr << s << endl;
else
gi->debug_print(s);
}
const GeasBlock *GeasFile::find_by_name(String type, String name) const {
//name = lcase (name);
for (uint i = 0; i < size(type); i ++) {
//cerr << "find_by_name (" << type << ", " << name << "), vs. '"
// << block(type, i).name << "'\n";
//if (block(type, i).lname == name)
if (ci_equal(block(type, i).name, name))
return &block(type, i);
}
return nullptr;
}
const GeasBlock &GeasFile::block(String type, uint index) const {
StringArrayIntMap::const_iterator iter;
iter = type_indecies.find(type);
if (!(iter != type_indecies.end() && index < (*iter)._value.size()))
cerr << "Unable to find type " << type << "\n";
assert(iter != type_indecies.end() && index < (*iter)._value.size());
//assert (index >= 0 && index < size(type));
return blocks[(*iter)._value[index]];
}
uint GeasFile::size(String type) const {
//cerr << "GeasFile::size (" << type << ")" << endl;
// SENSITIVE?
//std::map<String, Common::Array<int>, CI_LESS>::const_iterator iter;
StringArrayIntMap::const_iterator iter;
//cerr << type_indecies << endl;
iter = type_indecies.find(type);
if (iter == type_indecies.end()) {
//cerr << " returning 0" << endl;
return 0;
}
//cerr << " returning " << (*iter)._value.size() << endl;
return (*iter)._value.size();
}
bool GeasFile::obj_has_property(String objname, String propname) const {
String tmp;
return get_obj_property(objname, propname, tmp);
}
/**
* Currently only works for actual objects, not rooms or the game
*/
//Set<String, CI_LESS> GeasFile::get_obj_keys (String obj) const
Set<String> GeasFile::get_obj_keys(String obj) const {
//Set<String, CI_LESS> rv;
reserved_words obj_tag_property("look", "examine", "speak", "take", "alias", "prefix", "suffix", "detail", "displaytype", "gender", "article", "hidden", "invisible", (char *) nullptr);
Set<String> rv;
get_obj_keys(obj, rv, obj_tag_property);
return rv;
}
void GeasFile::get_obj_keys(String obj, Set<String> &rv, const reserved_words &obj_tag_property) const {
cerr << "get_obj_keys (gf, <" << obj << ">)\n";
//Set<String> rv;
uint c1, c2;
String tok, line;
const reserved_words *rw = nullptr;
const GeasBlock *gb = find_by_name("object", obj);
rw = &obj_tag_property;
if (gb == nullptr) {
cerr << "No such object found, aborting\n";
//return rv;
return;
}
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
cerr << " handling line <" << line << ">\n";
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "properties") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
vstring params = split_param(param_contents(tok));
for (uint j = 0; j < params.size(); j ++) {
cerr << " handling parameter <" << params[j] << ">\n";
int k = params[j].find('=');
// SENSITIVE?
if (starts_with(params[j], "not ")) {
rv.insert(trim(params[j].substr(4)));
cerr << " adding <" << trim(params[j].substr(4))
<< ">\n";
} else if (k == -1) {
rv.insert(params[j]);
cerr << " adding <" << params[j] << ">\n";
} else {
rv.insert(trim(params[j].substr(0, k)));
cerr << " adding <" << trim(params[j].substr(0, k))
<< ">\n";
}
}
}
}
// SENSITIVE?
else if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok))
get_type_keys(param_contents(tok), rv);
}
//else if (has (tag_property, tok) && tag_property[tok])
else if (rw != nullptr && rw->has(tok)) {
String tok1 = next_token(line, c1, c2);
if (is_param(tok1))
rv.insert(tok);
}
}
cerr << "Returning (" << rv << ")\n";
}
void GeasFile::get_type_keys(String typen, Set<String> &rv) const {
cerr << "get_type_keys (" << typen << ", " << rv << ")\n";
const GeasBlock *gb = find_by_name("type", typen);
if (gb == nullptr) {
cerr << " g_t_k: Nonexistent type\n";
return;
}
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
//cerr << " g_t_k: Handling line '" << line << "'\n";
tok = first_token(line, c1, c2);
// SENSISTIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
get_type_keys(param_contents(tok), rv);
cerr << " g_t_k: Adding <" << tok << "> to rv: " << rv << "\n";
}
}
// SENSITIVE?
else if (tok == "action") {
cerr << " action, skipping\n";
} else {
int ch = line.find('=');
if (ch != -1) {
rv.insert(trim(line.substr(0, ch)));
cerr << " adding <" << trim(line.substr(0, ch)) << ">\n";
}
}
}
cerr << "Returning (" << rv << ")\n";
}
bool GeasFile::get_obj_property(String objname, String propname, String &string_rv) const {
cerr << "g_o_p: Getting prop <" << propname << "> of obj <" << objname << ">\n";
string_rv = "!";
bool bool_rv = false;
//cerr << "obj_types == " << obj_types << endl;
/*
cerr << "obj_types == \n";
for (map<String, String>::const_iterator iter = obj_types.begin();
iter != obj_types.end(); iter ++)
cerr << " " << (*iter)._key << " -> " << (*iter)._value << "\n";
cerr << ".\n";
*/
/*
String objtype;
if (objname == "game")
objtype = "game";
else if (!has (obj_types, objname))
{
debug_print ("Checking property of nonexistent object " + objname);
return false;
}
else
objtype = (*obj_types.find(objname))._value;
*/
if (!has(obj_types, objname)) {
debug_print("Checking nonexistent object <" + objname + "> for property <" + propname + ">");
return false;
}
String objtype = (*obj_types.find(objname))._value;
const GeasBlock *geasBlock = find_by_name(objtype, objname);
String not_prop = "not " + propname;
uint c1, c2;
assert(geasBlock != nullptr);
//assert (geasBlock->data != NULL);
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
//cerr << " g_o_p: Handling line <" << line << ">\n";
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok))
get_type_property(param_contents(tok), propname, bool_rv, string_rv);
else {
debug_print("Expected parameter for type in " + line);
}
}
// SENSITIVE?
else if (tok == "properties") {
tok = next_token(line, c1, c2);
if (!is_param(tok)) {
debug_print("Expected param on line " + line);
continue;
}
Common::Array<String> props = split_param(param_contents(tok));
for (uint j = 0; j < props.size(); j ++) {
//cerr << " g_o_p: Comparing against <" << props[j] << ">\n";
int index;
if (props[j] == propname) {
//cerr << " g_o_p: Present but empty, blanking\n";
string_rv = "";
bool_rv = true;
} else if (props[j] == not_prop) {
//cerr << " g_o_p: Negation, removing\n";
string_rv = "!";
bool_rv = false;
} else if ((index = props[j].find('=')) != -1 &&
(trim(props[j].substr(0, index)) == propname)) {
string_rv = props[j].substr(index + 1);
bool_rv = true;
//cerr << " g_o_p: Normal prop, now to <" << string_rv << ">\n";
}
}
}
}
cerr << "g_o_p: Ultimately returning " << (bool_rv ? "true" : "false")
<< ", with String <" << string_rv << ">\n\n";
return bool_rv;
}
void GeasFile::get_type_property(String typenamex, String propname, bool &bool_rv, String &string_rv) const {
//cerr << " Checking type <" << typenamex << "> for prop <" << propname << ">\n";
const GeasBlock *geasBlock = find_by_name("type", typenamex);
if (geasBlock == nullptr) {
debug_print("Object of nonexistent type " + typenamex);
return;
}
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
//cerr << " Comparing vs. line <" << line << ">\n";
uint c1, c2;
int p;
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok))
get_type_property(param_contents(tok), propname, bool_rv, string_rv);
} else if (line == propname) {
bool_rv = true;
string_rv = "";
} else {
p = line.find('=');
if (p != -1) {
tok = trim(line.substr(0, p));
if (tok == propname) {
string_rv = trim(line.substr(p + 1));
bool_rv = true;
}
}
}
/*
if (tok == propname)
{
cerr << " match...";
tok = next_token (line, c1, c2);
if (tok == "")
{
bool_rv = true;
string_rv = "";
//cerr << " present but empty\n";
}
else if (tok == "=")
{
bool_rv = true;
string_rv = trim (line.substr (c2));
//cerr << " now <" << string_rv << ">\n";
}
else
{
cerr << "Bad line while checking " << typenamex << " for prop "
<< propname << ": " << line << endl;
}
}
else if (tok == "type")
{
tok = next_token (line, c1, c2);
if (is_param (tok))
get_type_property (param_contents(tok), propname, bool_rv, string_rv);
}
*/
}
}
bool GeasFile::obj_of_type(String objname, String typenamex) const {
if (!has(obj_types, objname)) {
debug_print("Checking nonexistent obj <" + objname + "> for type <" +
typenamex + ">");
return false;
}
String objtype = (*obj_types.find(objname))._value;
const GeasBlock *geasBlock = find_by_name(objtype, objname);
uint c1, c2;
assert(geasBlock != nullptr);
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
if (type_of_type(param_contents(tok), typenamex))
return true;
} else {
debug_print("Eg_o_p: xpected parameter for type in " + line);
}
}
}
return false;
}
bool GeasFile::type_of_type(String subtype, String supertype) const {
if (ci_equal(subtype, supertype))
return true;
//cerr << " Checking type <" << subtype << "> for type <" << supertype << ">\n";
const GeasBlock *geasBlock = find_by_name("type", subtype);
if (geasBlock == nullptr) {
debug_print("t_o_t: Nonexistent type " + subtype);
return false;
}
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
//cerr << " Comparing vs. line <" << line << ">\n";
uint c1, c2;
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok) && type_of_type(param_contents(tok), supertype))
return true;
}
}
return false;
}
bool GeasFile::get_obj_action(String objname, String propname, String &string_rv) const {
cerr << "g_o_a: Getting action <" << propname << "> of object <" << objname << ">\n";
string_rv = "!";
bool bool_rv = false;
//cerr << "obj_types == " << obj_types << endl;
/*
cerr << "obj_types == \n";
for (map<String, String>::const_iterator iter = obj_types.begin();
iter != obj_types.end(); iter ++)
cerr << " " << (*iter)._key << " -> " << (*iter)._value << "\n";
cerr << ".\n";
*/
if (!has(obj_types, objname)) {
debug_print("Checking nonexistent object <" + objname + "> for action <" + propname + ">.");
return false;
}
String objtype = (*obj_types.find(objname))._value;
//reserved_words *rw;
const GeasBlock *geasBlock = find_by_name(objtype, objname);
String not_prop = "not " + propname;
uint c1, c2;
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
//cerr << " g_o_a: Handling line <" << line << ">\n";
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok))
get_type_action(param_contents(tok), propname, bool_rv, string_rv);
else {
gi->debug_print("Expected parameter for type in " + line);
}
}
/*
else if (rw != NULL && tok == propname && rw->has(propname))
{
tok = next_token (line, c1, c2);
if (is_param(tok))
{
cerr << " Parameter, skipping\n";
}
else
{
//cerr << " Action, skipping\n";
cerr << " Action, string_rv is now <" << string_rv << ">\n";
string_rv = line.substr (c1);
bool_rv = true;
}
}
*/
// SENSITIVE?
else if (tok == "action") {
tok = next_token(line, c1, c2);
if (is_param(tok) && param_contents(tok) == propname) {
if (c2 + 1 < line.length())
string_rv = line.substr(c2 + 1);
else
string_rv = "";
bool_rv = true;
cerr << " Action line, string_rv now <" << string_rv << ">\n";
}
}
}
cerr << "g_o_a: Ultimately returning value " << (bool_rv ? "true" : "false") << ", with String <" << string_rv << ">\n\n";
return bool_rv;
}
void GeasFile::get_type_action(String typenamex, String actname, bool &bool_rv, String &string_rv) const {
//cerr << " Checking type <" << typenamex << "> for action <" << actname << ">\n";
const GeasBlock *geasBlock = find_by_name("type", typenamex);
if (geasBlock == nullptr) {
debug_print("Object of nonexistent type " + typenamex);
return;
}
for (uint i = 0; i < geasBlock->data.size(); i ++) {
String line = geasBlock->data[i];
//cerr << " g_t_a: Comparing vs. line <" << line << ">\n";
uint c1, c2;
String tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "action") {
//cerr << " match...\n";
tok = next_token(line, c1, c2);
if (is_param(tok) && param_contents(tok) == actname) {
bool_rv = true;
string_rv = line.substr(c2);
//cerr << " present: {" + string_rv + "}\n";
}
}
// SENSITIVE?
else if (tok == "type") {
tok = next_token(line, c1, c2);
if (is_param(tok))
get_type_action(param_contents(tok), actname, bool_rv, string_rv);
}
}
}
void GeasFile::register_block(String blockname, String blocktype) {
cerr << "registering block " << blockname << " / " << blocktype << endl;
if (has(obj_types, blockname)) {
String errdesc = "Trying to register block of named <" + blockname +
"> of type <" + blocktype + "> when there is already one, of type <" +
obj_types[blockname] + ">";
error("%s", errdesc.c_str());
}
obj_types[blockname] = blocktype;
}
String GeasFile::static_svar_lookup(String varname) const {
cerr << "static_svar_lookup(" << varname << ")" << endl;
//varname = lcase (varname);
for (uint i = 0; i < size("variable"); i++) {
//if (blocks[i].lname == varname)
if (ci_equal(blocks[i].name, varname)) {
String rv;
String tok;
uint c1, c2;
bool found_typeline = false;
for (uint j = 0; j < blocks[i].data.size(); j++) {
String line = blocks[i].data[j];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "numeric")
error("Trying to evaluate int var '%s' as String", varname.c_str());
// SENSITIVE?
if (tok != "String")
error("Bad variable type %s", tok.c_str());
found_typeline = true;
}
// SENSITIVE?
else if (tok == "value") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
error("Expected param after value in %s", line.c_str());
rv = param_contents(tok);
}
}
if (!found_typeline)
error("%s is a numeric variable", varname.c_str());
cerr << "static_svar_lookup(" << varname << ") -> \"" << rv << "\"" << endl;
return rv;
}
}
debug_print("Variable <" + varname + "> not found.");
return "";
}
String GeasFile::static_ivar_lookup(String varname) const {
//varname = lcase (varname);
for (uint i = 0; i < size("variable"); i ++)
//if (blocks[i].lname == varname)
if (ci_equal(blocks[i].name, varname)) {
String rv;
String tok;
uint c1, c2;
for (uint j = 0; j < blocks[i].data.size(); j ++) {
String line = blocks[i].data[j];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "type") {
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "String")
error("Trying to evaluate String var '%s' as numeric", varname.c_str());
// SENSITIVE?
if (tok != "numeric")
error("Bad variable type %s", tok.c_str());
}
// SENSITIVE?
else if (tok == "value") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
error("Expected param after value in %s", line.c_str());
rv = param_contents(tok);
}
}
return rv;
}
debug_print("Variable <" + varname + "> not found");
return "-32768";
}
String GeasFile::static_eval(String input) const {
//cerr << "static_eval (" << input << ")" << endl;
String rv = "";
for (uint i = 0; i < input.length(); i ++) {
if (input[i] == '#') {
uint j;
for (j = i + 1; j < input.length() && input[j] != '#'; j ++)
;
if (j == input.length())
error("Error processing '%s', odd hashes", input.c_str());
uint k;
for (k = i + 1; k < j && input[k] != ':'; k ++)
;
if (k == ':') {
String objname;
if (input[i + 1] == '(' && input[k - 1] == ')')
objname = static_svar_lookup(input.substr(i + 2, k - i - 4));
else
objname = input.substr(i + 1, k - i - 2);
cerr << " objname == '" << objname << endl;
//rv += get_obj_property (objname, input.substr (k+1, j-k-2));
String tmp;
bool had_var;
String objprop = input.substr(k + 1, j - k - 2);
cerr << " objprop == " << objprop << endl;
had_var = get_obj_property(objname, objprop, tmp);
rv += tmp;
if (!had_var)
debug_print("Requesting nonexistent property <" + objprop +
"> of object <" + objname + ">");
} else {
cerr << "i == " << i << ", j == " << j << ", length is " << input.length() << endl;
cerr << "Looking up static var " << input.substr(i + 1, j - i - 1) << endl;
rv += static_svar_lookup(input.substr(i + 1, j - i - 1));
}
i = j;
} else if (input[i] == '%') {
uint j;
for (j = i; j < input.length() && input[j] != '%'; j ++)
;
if (j == input.length())
error("Error processing '%s', unmatched %%", input.c_str());
rv += static_ivar_lookup(input.substr(i + 1, j - i - 2));
i = j;
} else
rv += input[i];
}
if (rv != input)
cerr << "*** CHANGED ***\n";
//cerr << "static_eval (" << input << ") --> \"" << rv << "\"" << endl;
return rv;
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,122 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_FILE
#define GLK_QUEST_GEAS_FILE
#include "glk/quest/string.h"
#include "common/algorithm.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/stream.h"
namespace Glk {
namespace Quest {
class GeasInterface;
class reserved_words;
/**
* Ordered array of items
*/
template<class T>
class Set : public Common::Array<T> {
public:
/**
* Insert a new item
*/
void insert(T val) {
this->push_back(val);
Common::sort(this->begin(), this->end());
}
};
struct GeasBlock {
////// lname == lowercase name
////// nname == normal name
////// parent == initial parent object (lowercased)
// name == name
// parent == initial parent object
String blocktype, name, parent;
Common::Array<String> data;
//GeasBlock (const Common::Array<String> &, String, uint, bool);
GeasBlock() {}
};
struct GeasFile {
GeasInterface *gi;
void debug_print(String s) const;
//vector<GeasBlock> rooms, objects, textblocks, functions, procedures, types;
//GeasBlock synonyms, game;
Common::Array <GeasBlock> blocks;
//Common::Array<GeasBlock> rooms, objects, textblocks, functions, procedures,
// types, synonyms, game, variables, timers, choices;
StringMap obj_types;
StringArrayIntMap type_indecies;
void register_block(String blockname, String blocktype);
const GeasBlock &block(String type, uint index) const;
uint size(String type) const;
void read_into(const Common::Array<String> &, String, uint, bool, const reserved_words &, const reserved_words &, const reserved_words &);
GeasFile() : gi(nullptr) {}
explicit GeasFile(const Common::Array<String> &in_data,
GeasInterface *gi);
bool obj_has_property(String objname, String propname) const;
bool get_obj_property(String objname, String propname,
String &rv) const;
void get_type_property(String typenamex, String propname,
bool &, String &) const;
bool obj_of_type(String object, String type) const;
bool type_of_type(String subtype, String supertype) const;
Set<String> get_obj_keys(String obj) const;
void get_obj_keys(String, Set<String> &, const reserved_words &obj_tag_property) const;
void get_type_keys(String, Set<String> &) const;
bool obj_has_action(String objname, String propname) const;
bool get_obj_action(String objname, String propname,
String &rv) const;
void get_type_action(String typenamex, String propname,
bool &, String &) const;
String static_eval(String) const;
String static_ivar_lookup(String varname) const;
String static_svar_lookup(String varname) const;
const GeasBlock *find_by_name(String type, String name) const;
};
Common::WriteStream &operator<<(Common::WriteStream &, const GeasBlock &);
Common::WriteStream &operator<<(Common::WriteStream &, const GeasFile &);
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,234 @@
/* 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/quest/geas_glk.h"
#include "glk/quest/geas_runner.h"
#include "glk/quest/quest.h"
#include "glk/quest/streams.h"
#include "glk/windows.h"
namespace Glk {
namespace Quest {
winid_t mainglkwin;
winid_t inputwin;
winid_t bannerwin;
strid_t inputwinstream;
const bool use_inputwindow = false;
int ignore_lines; // count of lines to ignore in game output
void draw_banner() {
uint width;
uint index;
if (bannerwin) {
g_vm->glk_window_clear(bannerwin);
g_vm->glk_window_move_cursor(bannerwin, 0, 0);
strid_t stream = g_vm->glk_window_get_stream(bannerwin);
g_vm->glk_set_style_stream(stream, style_User1);
g_vm->glk_window_get_size(bannerwin, &width, nullptr);
for (index = 0; index < width; index++)
g_vm->glk_put_char_stream(stream, ' ');
g_vm->glk_window_move_cursor(bannerwin, 1, 0);
if (g_vm->banner.empty())
g_vm->glk_put_string_stream(stream, "Geas 0.4");
else
g_vm->glk_put_string_stream(stream, g_vm->banner.c_str());
}
}
void glk_put_cstring(const char *s) {
if (!g_vm->loadingSavegame())
g_vm->glk_put_string(s);
}
GeasResult GeasGlkInterface::print_normal(const String &s) {
if (!ignore_lines)
glk_put_cstring(s.c_str());
return r_success;
}
GeasResult GeasGlkInterface::print_newline() {
if (!ignore_lines)
glk_put_cstring("\n");
else
ignore_lines--;
return r_success;
}
GeasResult GeasGlkInterface::set_style(const GeasFontStyle &style) {
// Glk styles are defined before the window opens, so at this point we can only
// pick the most suitable style, not define a new one.
uint match;
if (style.is_italic && style.is_bold)
match = style_Alert;
else if (style.is_italic)
match = style_Emphasized;
else if (style.is_bold)
match = style_Subheader;
else if (style.is_underlined)
match = style_User2;
else
match = style_Normal;
g_vm->glk_set_style_stream(g_vm->glk_window_get_stream(mainglkwin), match);
return r_success;
}
void GeasGlkInterface::set_foreground(String s) {
if (s != "") {
}
}
void GeasGlkInterface::set_background(String s) {
if (s != "") {
}
}
/* Code lifted from GeasWindow. Should be common. Maybe in
* GeasInterface?
*/
String GeasGlkInterface::get_file(const String &fname) const {
Common::File f;
if (!f.open(Common::Path(fname))) {
glk_put_cstring("Couldn't open ");
glk_put_cstring(fname.c_str());
g_vm->glk_put_char(0x0a);
return "";
}
// Read entirety of the file
char *buf = new char[f.size()];
f.read(buf, f.size());
String result(buf, buf + f.size());
delete[] buf;
return result;
}
String GeasGlkInterface::get_string() {
char buf[200];
g_vm->glk_request_line_event(inputwin, buf, (sizeof buf) - 1, 0);
while (1) {
event_t ev;
g_vm->glk_select(&ev);
if (ev.type == evtype_LineInput && ev.window == inputwin) {
return String(buf, ev.val1);
}
/* All other events, including timer, are deliberately
* ignored.
*/
}
}
uint GeasGlkInterface::make_choice(String label, Common::Array<String> v) {
uint n;
g_vm->glk_window_clear(inputwin);
glk_put_cstring(label.c_str());
g_vm->glk_put_char(0x0a);
n = v.size();
for (uint i = 0; i < n; ++i) {
StringStream t;
String s;
t << i + 1;
t >> s;
glk_put_cstring(s.c_str());
glk_put_cstring(": ");
glk_put_cstring(v[i].c_str());
glk_put_cstring("\n");
}
StringStream t;
String s;
String s1;
t << n;
t >> s;
s1 = "Choose [1-" + s + "]> ";
g_vm->glk_put_string_stream(inputwinstream, s1.c_str());
int choice = atoi(get_string().c_str());
if (choice < 1) {
choice = 1;
}
if ((uint)choice > n) {
choice = n;
}
StringStream u;
u << choice;
u >> s;
s1 = "Chosen: " + s + "\n";
glk_put_cstring(s1.c_str());
return choice - 1;
}
String GeasGlkInterface::absolute_name(String rel_name, String parent) const {
cerr << "absolute_name ('" << rel_name << "', '" << parent << "')\n";
if (parent[0] != '/')
return rel_name;
if (rel_name[0] == '/') {
cerr << " --> " << rel_name << "\n";
return rel_name;
}
Common::Array<String> path;
uint dir_start = 1, dir_end;
while (dir_start < parent.length()) {
dir_end = dir_start;
while (dir_end < parent.length() && parent[dir_end] != '/')
dir_end ++;
path.push_back(parent.substr(dir_start, dir_end - dir_start));
dir_start = dir_end + 1;
}
path.pop_back();
dir_start = 0;
String tmp;
while (dir_start < rel_name.length()) {
dir_end = dir_start;
while (dir_end < rel_name.length() && rel_name[dir_end] != '/')
dir_end ++;
tmp = rel_name.substr(dir_start, dir_end - dir_start);
dir_start = dir_end + 1;
if (tmp == ".")
continue;
else if (tmp == "..")
path.pop_back();
else
path.push_back(tmp);
}
String rv;
for (uint i = 0; i < path.size(); i ++)
rv = rv + "/" + path[i];
cerr << " ---> " << rv << "\n";
return rv;
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,91 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_GLK
#define GLK_QUEST_GEAS_GLK
#include "glk/quest/geas_runner.h"
#include "glk/windows.h"
namespace Glk {
namespace Quest {
/* User interface bridge from Geas Core to Glk.
Glk Window arrangement.
+---------+
| B |
+---------+
| M |
| |
+---------+
| I |
+---------+
B is a one line "banner window", showing the game name and author. Kept
in the global variable, it's optional, null if unavailable.
optional.
M is the main window where the text of the game appears. Kept in the
global variable mainglkwin.
I is a one line "input window" where the user inputs their commands.
Kept in the global variable inputwin, it's optional, and if not separate
is set to mainglkwin.
Maybe in future revisions there will be a status window (including a
compass rose).
*/
class GeasGlkInterface : public GeasInterface {
protected:
String get_file(const String &fname) const override;
GeasResult print_normal(const String &s) override;
GeasResult print_newline() override;
void set_foreground(String) override;
void set_background(String) override;
GeasResult set_style(const GeasFontStyle &) override;
String get_string() override;
uint make_choice(String, Common::Array<String>) override;
String absolute_name(String, String) const override;
public:
GeasGlkInterface() {
;
}
};
extern winid_t mainglkwin;
extern winid_t inputwin;
extern winid_t bannerwin;
extern strid_t inputwinstream;
extern int ignore_lines;
extern const bool use_inputwindow;
void glk_put_cstring(const char *);
extern void draw_banner();
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,214 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_IMPL
#define GLK_QUEST_GEAS_IMPL
#include "glk/quest/geas_runner.h"
#include "glk/quest/geas_state.h"
#include "glk/quest/limit_stack.h"
namespace Glk {
namespace Quest {
struct match_binding {
String var_name;
String var_text;
uint start, end;
//operator String();
String tostring();
match_binding(String vn, uint i) : var_name(vn), start(i) {}
void set(String vt, uint i) {
var_text = vt;
end = i;
}
};
Common::WriteStream &operator<< (Common::WriteStream &, const match_binding &);
struct match_rv {
bool success;
Common::Array<match_binding> bindings;
//match_rv (bool b, const Common::Array<String> &v) : success(b), bindings(v) {}
match_rv() : success(false) {}
match_rv(bool b, const match_rv &rv) : success(b), bindings(rv.bindings) {}
operator bool () {
return success;
}
};
Common::WriteStream &operator<< (Common::WriteStream &o, const match_rv &rv);
/*
inline ostream &operator<< (ostream &o, const match_rv &rv)
{
//o << "match_rv {" << (rv.success ? "TRUE" : "FALSE") << ": " << rv.bindings << "}";
o << "match_rv {" << (rv.success ? "TRUE" : "FALSE") << ": [";
//o << rv.bindings.size();
//o << rv.bindings;
for (uint i = 0; i < rv.bindings.size(); i ++)
o << rv.bindings[i] << ", ";
o << "]}";
return o;
}
*/
class geas_implementation : public GeasRunner {
//GeasInterface *gi;
GeasFile gf;
//bool running;
bool dont_process, outputting;
LimitStack <GeasState> undo_buffer;
Common::Array <String> function_args;
String this_object;
v2string current_places;
bool is_running_;
Logger logger;
public:
geas_implementation(GeasInterface *in_gi)
: GeasRunner(in_gi), undo_buffer(20), is_running_(true) {}
void set_game(const String &fname) override;
bool is_running() const override;
GeasState *getState() override { return &state; }
String get_banner() override;
void run_command(String) override;
bool try_match(String s, bool, bool);
match_rv match_command(String input, String action) const;
match_rv match_command(String input, uint ichar,
String action, uint achar, match_rv rv) const;
bool dereference_vars(Common::Array<match_binding> &bindings, bool is_internal) const;
bool dereference_vars(Common::Array<match_binding> &, const Common::Array<String> &, bool is_internal) const;
bool match_object(String text, String name, bool is_internal = false) const;
void set_vars(const Common::Array<match_binding> &v);
bool run_commands(String, const GeasBlock *, bool is_internal = false);
void display_error(String errorname, String object = "");
String substitute_synonyms(String) const;
void set_svar(String, String);
void set_svar(String, uint, String);
void set_ivar(String, int);
void set_ivar(String, uint, int);
String get_svar(String) const;
String get_svar(String, uint) const;
int get_ivar(String) const;
int get_ivar(String, uint) const;
bool find_ivar(String, uint &) const;
bool find_svar(String, uint &) const;
void regen_var_look();
void regen_var_dirs();
void regen_var_objects();
void regen_var_room();
void look();
String displayed_name(String object) const;
//String get_obj_name (const Common::Array<String> &args) const;
String get_obj_name(String name, const Common::Array<String> &where, bool is_internal) const;
bool has_obj_property(String objname, String propname) const;
bool get_obj_property(String objname, String propname,
String &rv) const;
bool has_obj_action(String obj, String prop) const;
bool get_obj_action(String objname, String actname,
String &rv) const;
String exit_dest(String room, String dir, bool *is_act = NULL) const;
Common::Array<Common::Array<String> > get_places(String room);
void set_obj_property(String obj, String prop);
void set_obj_action(String obj, String act);
void move(String obj, String dest);
void goto_room(String room);
String get_obj_parent(String obj);
void print_eval(String);
void print_eval_p(String);
String eval_string(String s);
String eval_param(String s) {
assert(is_param(s));
return eval_string(param_contents(s));
}
void run_script_as(String, String);
void run_script(String);
void run_script(String, String &);
void run_procedure(String);
void run_procedure(String, Common::Array<String> args);
String run_function(String);
String run_function(String, Common::Array<String> args);
String bad_arg_count(String);
bool eval_conds(String);
bool eval_cond(String);
GeasState state;
void tick_timers() override;
v2string get_inventory() override;
v2string get_room_contents() override;
v2string get_room_contents(String);
vstring get_status_vars() override;
Common::Array<bool> get_valid_exits() override;
inline void print_formatted(String s) const {
if (outputting) gi->print_formatted(s);
}
inline void print_normal(String s) const {
if (outputting) gi->print_normal(s);
}
inline void print_newline() const {
if (outputting) gi->print_newline();
}
/*
inline void print_formatted (String s) const {
if (outputting)
gi->print_formatted(s);
else
gi->print_formatted ("{{" + s + "}}");
}
inline void print_normal (String s) const
{
if (outputting)
gi->print_normal (s);
else
gi->print_normal("{{" + s + "}}");
}
inline void print_newline() const {
if (outputting)
gi->print_newline();
else
gi->print_normal ("{{|n}}");
}
*/
};
} // End of namespace Quest
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,221 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_RUNNER
#define GLK_QUEST_GEAS_RUNNER
#include "glk/quest/string.h"
#include "glk/quest/geas_state.h"
#include "common/array.h"
#include "common/stream.h"
namespace Glk {
namespace Quest {
typedef Common::Array<String> vstring;
typedef Common::Array<vstring> v2string;
enum geas_justification { JUSTIFY_LEFT, JUSTIFY_RIGHT, JUSTIFY_CENTER };
struct GeasFontStyle {
bool is_underlined, is_italic, is_bold;
String color, font;
int size;
geas_justification justify;
GeasFontStyle() : is_underlined(false), is_italic(false), is_bold(false),
color(""), font(""), size(10), justify(JUSTIFY_LEFT) {}
};
class GeasFontStyleCompare {
public:
int operator()(const GeasFontStyle &a, const GeasFontStyle &b) {
if (a.size != b.size) return a.size < b.size;
if (a.is_underlined != b.is_underlined) return a.is_underlined;
if (a.is_bold != b.is_bold) return a.is_bold;
if (a.is_italic != b.is_italic) return a.is_italic;
if (a.color != b.color) return a.color < b.color;
if (a.justify != b.justify) return a.justify < b.justify;
return 0;
}
};
Common::WriteStream &operator<< (Common::WriteStream &o, const GeasFontStyle &gfs);
enum GeasResult {
r_success,
r_failure,
r_not_supported
};
/* Callback object used to pass information from GeasCore
* to the interface objects
*/
class GeasInterface {
private:
GeasFontStyle cur_style;
String default_font;
int default_size;
//string fgcolor, bgcolor;
public:
/* Takes 1 argument, a string with Quest markup
* Will output it to the user interface
* If the with_newline flag is set, it will print a newline afterwards
* unless the string ends in "|xn"
*/
GeasResult print_formatted(String s, bool with_newline = true);
/* Takes one argument; that string is printed without interpretation
* Must be implemented
* Called by print_formatted and by Geas directly.
*/
virtual GeasResult print_normal(const String &s) = 0;
virtual GeasResult print_newline() = 0;
protected:
void update_style() {
set_style(cur_style);
}
/* Changes style of output text.
* Need not be implemented
* Only called by update_style()
*/
virtual GeasResult set_style(const GeasFontStyle &) {
return r_not_supported;
}
public:
virtual String absolute_name(String rel_name, String parent) const = 0;
virtual String get_file(const String &filename) const = 0;
virtual void debug_print(const String &s) {
warning("%s", s.c_str());
}
virtual GeasResult wait_keypress(String) {
return r_not_supported;
}
virtual GeasResult pause(int msec) {
return r_not_supported;
}
virtual GeasResult clear_screen() {
return r_not_supported;
}
//virtual GeasResult set_foreground (string) { return r_not_supported; }
//virtual GeasResult set_background (string) { return r_not_supported; }
virtual void set_foreground(String) = 0;
virtual void set_background(String) = 0;
void set_default_font_size(String s);
void set_default_font(String s);
/* Unsure what arguments this will take.
* May also add animated, persistent, close image
*/
virtual GeasResult show_image(String filename, String resolution,
String caption, ...) {
return r_not_supported;
}
/* Again, unsure what arguments to give
* May add sound type
* If sync is true, do not return until file ends
* If filename is "", stop playing sounds.
*/
virtual GeasResult play_sound(String filename, bool looped, bool sync) {
return r_not_supported;
}
/* Asks the user to type a free format string
*/
virtual String get_string() = 0;
/* Presents a list with header 'info', and prompts the user to
* choose one item from 'choices'.
* returns the index chosen.
*/
virtual uint make_choice(String info, Common::Array<String> choices) = 0;
/* Asks the user a yes/no question
* (If not overridden, this has an implementation that uses make_choice()
*/
virtual bool choose_yes_no(String question);
/* args holds arguments sent to program.
* if active is true, geas should retain focus
* returns - 0 if disallowed
* - 1 if succeeded
* - 2 if file not found
* - 3 if it couldn't find a program to run it
* - 4 if it ran out of memory
*/
virtual int shell(Common::Array<String> args, bool active) {
return 0;
}
/* say the argument using text-to-speech
*/
virtual GeasResult speak(String) {
return r_not_supported;
}
virtual ~GeasInterface() {}
/* This is a notification that some object has changed, and
* the interpreter may want to update the inventory or room object
* listings.
*/
virtual void update_sidebars() { }
};
/* Callback for passing information from the UI to the execution core
*/
class GeasRunner {
protected:
GeasInterface *gi;
public:
GeasRunner(GeasInterface *_gi) : gi(_gi) {}
virtual bool is_running() const = 0;
virtual GeasState *getState() = 0;
virtual String get_banner() = 0;
virtual void run_command(String) = 0;
virtual v2string get_inventory() = 0;
virtual v2string get_room_contents() = 0;
virtual vstring get_status_vars() = 0;
virtual Common::Array<bool> get_valid_exits() = 0;
virtual void tick_timers() = 0;
virtual ~GeasRunner() { }
virtual void set_game(const String &fname) = 0;
static GeasRunner *get_runner(GeasInterface *gi);
};
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,343 @@
/* 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/quest/geas_state.h"
#include "glk/quest/geas_runner.h"
#include "glk/quest/geas_util.h"
#include "glk/quest/read_file.h"
#include "glk/quest/streams.h"
namespace Glk {
namespace Quest {
void Serializer::sync(bool &b) {
byte v = b ? 1 : 0;
syncAsByte(v);
if (isLoading())
b = v != 0;
}
void Serializer::sync(String &s) {
Common::String str = s;
Common::Serializer::syncString(str);
if (isLoading())
s = String(str.c_str());
}
void Serializer::sync(PropertyRecord &pr) {
sync(pr.name);
sync(pr.data);
}
void Serializer::sync(ObjectRecord &pr) {
sync(pr.name);
sync(pr.hidden);
sync(pr.invisible);
sync(pr.parent);
}
void Serializer::sync(ExitRecord &er) {
sync(er.src);
sync(er.dest);
}
void Serializer::sync(TimerRecord &tr) {
sync(tr.name);
sync(tr.is_running);
syncAsUint32LE(tr.interval);
syncAsUint32LE(tr.timeleft);
}
void Serializer::sync(SVarRecord &svr) {
svr.sync(*this);
}
void Serializer::sync(IVarRecord &ivr) {
ivr.sync(*this);
}
void Serializer::sync(GeasState &gs) {
sync(gs.location);
sync(gs.props);
sync(gs.objs);
sync(gs.exits);
sync(gs.timers);
sync(gs.svars);
sync(gs.ivars);
}
/*----------------------------------------------------------------------*/
void SVarRecord::sync(Serializer &s) {
s.sync(name);
uint count = data.size();
s.syncAsUint32LE(count);
if (s.isLoading())
data.resize(count);
for (uint i = 0; i < size(); ++i)
s.sync(data[i]);
}
/*----------------------------------------------------------------------*/
void IVarRecord::sync(Serializer &s) {
s.sync(name);
uint count = data.size();
s.syncAsUint32LE(count);
if (s.isLoading())
data.resize(count);
for (uint i = 0; i < size(); ++i)
s.syncAsSint32LE(data[i]);
}
/*----------------------------------------------------------------------*/
GeasState::GeasState(GeasInterface &gi, const GeasFile &gf) {
running = false;
cerr << "GeasState::GeasState()" << endl;
for (uint i = 0; i < gf.size("game"); i ++) {
//const GeasBlock &go = gf.game[i];
//register_block ("game", "game");
ObjectRecord data;
data.name = "game";
data.parent = "";
data.hidden = false;
data.invisible = true;
objs.push_back(data);
}
cerr << "GeasState::GeasState() done setting game" << endl;
for (uint i = 0; i < gf.size("room"); i ++) {
const GeasBlock &go = gf.block("room", i);
ObjectRecord data;
//data.name = go.lname;
data.name = go.name;
data.parent = "";
data.hidden = data.invisible = true;
//register_block (data.name, "room");
objs.push_back(data);
}
cerr << "GeasState::GeasState() done setting rooms" << endl;
for (uint i = 0; i < gf.size("object"); i++) {
const GeasBlock &go = gf.block("object", i);
ObjectRecord data;
//data.name = go.lname;
data.name = go.name;
if (go.parent == "")
data.parent = "";
else
//data.parent = lcase (param_contents (go.parent));
data.parent = param_contents(go.parent);
//register_block (data.name, "object");
data.hidden = data.invisible = false;
objs.push_back(data);
}
cerr << "GeasState::GeasState() done setting objects" << endl;
for (uint i = 0; i < gf.size("timer"); i ++) {
const GeasBlock &go = gf.block("timer", i);
//cerr << "GS::GS: Handling timer " << go << "\n";
TimerRecord tr;
String interval = "", status = "";
for (uint j = 0; j < go.data.size(); j ++) {
String line = go.data[j];
uint c1, c2;
String tok = first_token(line, c1, c2);
if (tok == "interval") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
gi.debug_print(nonparam("interval", line));
else
interval = param_contents(tok);
} else if (tok == "enabled" || tok == "disabled") {
if (status != "")
gi.debug_print("Repeated status for timer");
else
status = tok;
} else if (tok == "action") {
} else {
gi.debug_print("Bad timer line " + line);
}
}
//tr.name = go.lname;
tr.name = go.name;
tr.is_running = (status == "enabled");
tr.interval = tr.timeleft = parse_int(interval);
//register_block (tr.name, "timer");
timers.push_back(tr);
}
cerr << "GeasState::GeasState() done with timers" << endl;
for (uint i = 0; i < gf.size("variable"); i ++) {
const GeasBlock &go(gf.block("variable", i));
cerr << "GS::GS: Handling variable #" << i << ": " << go << endl;
String vartype;
String value;
for (uint j = 0; j < go.data.size(); j ++) {
String line = go.data[j];
cerr << " Line #" << j << " of var: \"" << line << "\"" << endl;
uint c1, c2;
String tok = first_token(line, c1, c2);
if (tok == "type") {
tok = next_token(line, c1, c2);
if (tok == "")
gi.debug_print(String("Missing variable type in ")
+ string_geas_block(go));
else if (vartype != "")
gi.debug_print(String("Redefining var. type in ")
+ string_geas_block(go));
else if (tok == "numeric" || tok == "String")
vartype = tok;
else
gi.debug_print(String("Bad var. type ") + line);
} else if (tok == "value") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
gi.debug_print(String("Expected parameter in " + line));
else
value = param_contents(tok);
} else if (tok == "display" || tok == "onchange") {
} else {
gi.debug_print(String("Bad var. line: ") + line);
}
}
if (vartype == "" || vartype == "numeric") {
IVarRecord ivr;
//ivr.name = go.lname;
ivr.name = go.name;
ivr.set(0, parse_int(value));
ivars.push_back(ivr);
//register_block (ivr.name, "numeric");
} else {
SVarRecord svr;
//svr.name = go.lname;
svr.name = go.name;
svr.set(0, value);
svars.push_back(svr);
//register_block (svr.name, "String");
}
}
//cerr << obj_types << endl;
cerr << "GeasState::GeasState() done with variables" << endl;
}
void GeasState::load(Common::SeekableReadStream *rs) {
Serializer s(rs, nullptr);
s.sync(*this);
}
void GeasState::save(Common::WriteStream *ws) {
Serializer s(nullptr, ws);
s.sync(*this);
}
/*----------------------------------------------------------------------*/
Common::WriteStream &operator<<(Common::WriteStream &o, const PropertyRecord &pr) {
o << pr.name << ", data == " << pr.data;
return o;
}
Common::WriteStream &operator<<(Common::WriteStream &o, const ObjectRecord &objr) {
o << objr.name << ", parent == " << objr.parent;
if (objr.hidden)
o << ", hidden";
if (objr.invisible)
o << ", invisible";
return o;
}
Common::WriteStream &operator<<(Common::WriteStream &o, const ExitRecord er) {
return o << er.src << ": " << er.dest;
}
Common::WriteStream &operator<<(Common::WriteStream &o, const TimerRecord &tr) {
return o << tr.name << ": " << (tr.is_running ? "" : "not ") << "running ("
<< tr.timeleft << " // " << tr.interval << ")";
}
Common::WriteStream &operator<<(Common::WriteStream &o, const SVarRecord &sr) {
o << sr.name << ": ";
if (sr.size() == 0)
o << "(empty)";
else if (sr.size() <= 1)
o << "<" << sr.get(0) << ">";
else
for (uint i = 0; i < sr.size(); i++) {
o << i << ": <" << sr.get(i) << ">";
if (i + 1 < sr.size())
o << ", ";
}
return o;
}
Common::WriteStream &operator<<(Common::WriteStream &o, const IVarRecord &ir) {
o << ir.name << ": ";
if (ir.size() == 0)
o << "(empty)";
else if (ir.size() <= 1)
o << ir.get(0);
else
for (uint i = 0; i < ir.size(); i++) {
o << i << ": " << ir.get(i);
if (i + 1 < ir.size())
o << ", ";
}
return o;
}
Common::WriteStream &operator<<(Common::WriteStream &o, const GeasState &gs) {
o << "location == " << gs.location << "\nprops: \n";
for (uint i = 0; i < gs.props.size(); i++)
o << " " << i << ": " << gs.props[i] << "\n";
o << "objs:\n";
for (uint i = 0; i < gs.objs.size(); i++)
o << " " << i << ": " << gs.objs[i] << "\n";
o << "exits:\n";
for (uint i = 0; i < gs.exits.size(); i++)
o << " " << i << ": " << gs.exits[i] << "\n";
o << "timers:\n";
for (uint i = 0; i < gs.timers.size(); i++)
o << " " << i << ": " << gs.timers[i] << "\n";
o << "String variables:\n";
for (uint i = 0; i < gs.svars.size(); i++)
o << " " << i << ": " << gs.svars[i] << "\n";
o << "integer variables:\n";
for (uint i = 0; i < gs.svars.size(); i++)
o << " " << i << ": " << gs.svars[i] << "\n";
return o;
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,202 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_STATE
#define GLK_QUEST_GEAS_STATE
#include "glk/quest/string.h"
#include "common/array.h"
#include "common/stream.h"
#include "common/serializer.h"
namespace Glk {
namespace Quest {
struct GeasFile;
struct GeasState;
class Serializer;
class GeasInterface;
struct PropertyRecord {
String name, data;
PropertyRecord() {}
PropertyRecord(const String &in_name, const String &in_data) : name(in_name), data(in_data) {}
};
struct ObjectRecord {
String name, parent;
bool hidden, invisible;
//ObjectRecord (String in_name, String in_parent) : name (in_name), parent (in_parent), hidden (false), concealed (false) {}
};
struct ExitRecord {
String src, dest;
ExitRecord() {}
ExitRecord(const String &in_src, const String &in_dest) : src(in_src), dest(in_dest) {}
};
struct TimerRecord {
String name;
bool is_running;
uint interval, timeleft;
};
struct SVarRecord {
private:
Common::Array<String> data;
public:
String name;
SVarRecord() {}
SVarRecord(String in_name) : name(in_name) {
set(0, "");
}
uint size() const {
return data.size();
}
uint max() const {
return size() - 1;
}
void set(uint i, String val) {
if (i >= size()) data.resize(i + 1);
data[i] = val;
}
String get(uint i) const {
if (i < size()) return data[i];
return "!";
}
void set(String val) {
data[0] = val;
}
String get() const {
return data[0];
}
void sync(Serializer &s);
};
struct IVarRecord {
private:
Common::Array<int> data;
public:
String name;
IVarRecord() {}
IVarRecord(String in_name) : name(in_name) {
set(0, 0);
}
uint size() const {
return data.size();
}
uint max() const {
return size() - 1;
}
void set(uint i, int val) {
if (i >= size()) data.resize(i + 1);
data[i] = val;
}
int get(uint i) const {
if (i < size()) return data[i];
else return -32767;
}
void set(int val) {
data[0] = val;
}
int get() const {
return data[0];
}
void sync(Serializer &s);
};
class Serializer : public Common::Serializer {
public:
Serializer(Common::SeekableReadStream *in, Common::WriteStream *out) : Common::Serializer(in, out) {}
void sync(bool &b);
void sync(String &s);
void sync(PropertyRecord &pr);
void sync(ObjectRecord &pr);
void sync(ExitRecord &er);
void sync(TimerRecord &tr);
void sync(SVarRecord &svr);
void sync(IVarRecord &ivr);
void sync(GeasState &gs);
template <class T> void sync(Common::Array<T> &v) {
uint count = v.size();
syncAsUint32LE(count);
if (isLoading())
v.resize(count);
for (uint idx = 0; idx < count; ++idx)
sync(v[idx]);
}
};
struct GeasState {
//private:
//std::auto_ptr<GeasFile> gf;
public:
bool running;
String location;
Common::Array<PropertyRecord> props;
Common::Array<ObjectRecord> objs;
Common::Array<ExitRecord> exits;
Common::Array<TimerRecord> timers;
Common::Array<SVarRecord> svars;
Common::Array<IVarRecord> ivars;
//std::map <String, String> obj_types;
//void register_block (String blockname, String blocktype);
GeasState() : running(false) {}
GeasState(GeasInterface &, const GeasFile &);
/**
* Save the state
*/
void load(Common::SeekableReadStream *rs);
/**
* Save the state
*/
void save(Common::WriteStream *ws);
};
Common::WriteStream &operator<<(Common::WriteStream &o, const StringMap &m);
Common::WriteStream &operator<<(Common::WriteStream &o, const PropertyRecord &pr);
Common::WriteStream &operator<<(Common::WriteStream &o, const ObjectRecord &objr);
Common::WriteStream &operator<<(Common::WriteStream &o, const ExitRecord er);
Common::WriteStream &operator<<(Common::WriteStream &o, const TimerRecord &tr);
Common::WriteStream &operator<<(Common::WriteStream &o, const SVarRecord &sr);
Common::WriteStream &operator<<(Common::WriteStream &o, const IVarRecord &ir);
Common::WriteStream &operator<<(Common::WriteStream &o, const GeasState &gs);
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,246 @@
/* 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/quest/geas_util.h"
#include "glk/quest/geas_file.h"
#include "glk/quest/streams.h"
#include "glk/quest/string.h"
namespace Glk {
namespace Quest {
int eval_int(String s) {
cerr << "eval_int (" << s << ")" << endl;
uint index = 0, index2;
String tmp;
while (index < s.length() && Common::isSpace(s[index])) {
cerr << " index == " << index << endl;
index ++;
}
if (index == s.length() || !Common::isDigit(s[index])) {
cerr << "Failed to match, returning 0" << endl;
return 0;
}
for (index2 = index; index2 < s.length() && Common::isDigit(s[index2]); index2 ++) {
cerr << " index2 == " << index2 << endl;
}
//;
tmp = s.substr(index, index2 - index);
cerr << "tmp == < " << tmp << ">" << endl;
//cerr << "index == " << index << ", index2 == " << index2
// << ", tmp == " << tmp << endl;
int arg1 = atoi(tmp.c_str());
cerr << "arg1 == " << arg1 << endl;
index = index2;
while (index < s.length() && Common::isSpace(s[index]))
++ index;
if (index == s.length())
return arg1;
//cerr << "index == " << index << ", s.length() == " << s.length() << endl;
char symbol = s[index];
//cerr << "symbol == " << symbol << "; find --> "
// << String("+-*/").find (symbol) << endl;
if (String("+-*/").find(symbol) == (int)String::npos)
return arg1;
++ index;
while (index < s.length() && Common::isSpace(s[index]))
++ index;
if (index == s.length() || ! Common::isDigit(s[index])) {
if (symbol == '*')
return 0;
return arg1;
}
index2 = index + 1;
while (index2 < s.length() && Common::isDigit(s[index2]))
++ index2;
tmp = s.substr(index, index2 - index);
int arg2 = atoi(tmp.c_str());
switch (symbol) {
case '+':
return arg1 + arg2;
case '-':
return arg1 - arg2;
case '*':
return arg1 * arg2;
case '/':
return arg1 / arg2;
// TODO: division should use accountant's round
default:
break;
}
return 0;
}
String trim_braces(String s) {
if (s.length() > 1 && s[0] == '[' && s[s.length() - 1] == ']')
return s.substr(1, s.length() - 2);
else
return s;
}
bool is_param(String s) {
return s.length() > 1 && s[0] == '<' && s[s.length() - 1] == '>';
}
String param_contents(String s) {
//cerr << "param_contents (" << s << ")" << endl;
assert(is_param(s));
return s.substr(1, s.length() - 2);
}
String nonparam(String type, String var) {
return "Non-parameter for " + type + " in \"" + var + "\"";
}
//ostream &operator << (ostream &o, const GeasBlock &gb) { return o; }
//String trim (String s, trim_modes) { return s; }
String string_geas_block(const GeasBlock &gb) {
ostringstream oss;
oss << gb; // temporary removed TODO
return oss.str();
}
bool starts_with(String a, String b) {
return (a.length() >= b.length()) && (a.substr(0, b.length()) == b);
}
bool ends_with(String a, String b) {
return (a.length() >= b.length()) &&
(a.substr(a.length() - b.length(), b.length()) == b);
}
bool starts_with_i(String a, String b) {
return (a.length() >= b.length()) && ci_equal(a.substr(0, b.length()), b);
// return starts_with (lcase(a), lcase(b));
}
bool ends_with_i(String a, String b) {
return (a.length() >= b.length()) &&
ci_equal(a.substr(a.length() - b.length(), b.length()), b);
//return ends_with (lcase(a), lcase(b));
}
String pcase(String s) {
if (s.length() == 0)
return s;
if (Common::isLower(s[0]))
s[0] = toupper(s[0]);
return s;
}
String ucase(String s) {
for (uint i = 0; i < s.length(); i ++)
s[i] = toupper(s[i]);
return s;
}
// There's a good chance s is already all-lowercase, in which case
// the test will avoid making a copy
String lcase(String s) {
for (uint i = 0; i < s.length(); i ++)
if (Common::isUpper(s[i]))
s[i] = tolower(s[i]);
return s;
}
Common::Array<String> split_param(String s) {
Common::Array<String> rv;
int c1 = 0, c2;
for (;;) {
c2 = s.find(';', c1);
if (c2 == -1) {
rv.push_back(s.substr(c1).trim());
return rv;
}
rv.push_back(s.substr(c1, c2 - c1).trim());
c1 = c2 + 1;
}
}
Common::Array<String> split_f_args(String s) {
Common::Array<String> rv = split_param(s);
for (uint i = 0; i < rv.size(); i ++) {
String tmp = rv[i];
if (tmp[0] == '_')
rv[i][0] = ' ';
if (tmp[tmp.length() - 1] == '_')
rv[i][tmp.length() - 1] = ' ';
}
return rv;
}
void show_split(String s) {
Common::Array<String> tmp = split_param(s);
cerr << "Splitting <" << s << ">: ";
for (uint i = 0; i < tmp.size(); i ++)
cerr << "<" << tmp[i] << ">, ";
cerr << "\n";
}
// Logger::Nullstreambuf Logger::cnull;
Logger::Logger() { // : logfilestr_(NULL), cerrbuf_(NULL) {
/*
cerr.flush();
const char *const logfile = getenv("GEAS_LOGFILE");
if (logfile) {
ofstream *filestr = new ofstream(logfile);
if (filestr->fail())
delete filestr;
else {
logfilestr_ = filestr;
cerrbuf_ = cerr.rdbuf(filestr->rdbuf());
}
}
if (!cerrbuf_)
cerrbuf_ = cerr.rdbuf(&cnull);
*/
}
Logger::~Logger() {
/*
cerr.flush();
cerr.rdbuf(cerrbuf_);
cerrbuf_ = NULL;
if (logfilestr_) {
logfilestr_->close();
delete logfilestr_;
logfilestr_ = NULL;
}
*/
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,96 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_GEAS_UTIL
#define GLK_QUEST_GEAS_UTIL
#include "glk/quest/read_file.h"
#include "glk/quest/streams.h"
namespace Glk {
namespace Quest {
typedef Common::Array<String> vstring;
inline int parse_int(const String &s) {
return atoi(s.c_str());
}
vstring split_param(String s);
vstring split_f_args(String s);
bool is_param(String s);
String param_contents(String s);
String nonparam(String, String);
String string_geas_block(const GeasBlock &);
bool starts_with(String, String);
bool ends_with(String, String);
String string_int(int i);
String trim_braces(String s);
int eval_int(String s);
String pcase(String s);
String ucase(String s);
String lcase(String s);
template<class T> Common::WriteStream &operator<<(Common::WriteStream &o, Common::Array<T> v) {
o << "{ '";
for (uint i = 0; i < v.size(); i ++) {
o << v[i];
if (i + 1 < v.size())
o << "', '";
}
o << "' }";
return o;
}
template <class KEYTYPE, class VALTYPE>
bool has(Common::HashMap<KEYTYPE, VALTYPE, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> m, KEYTYPE key) {
return m.contains(key);
}
class Logger {
public:
Logger();
~Logger();
private:
class Nullstreambuf : public Common::WriteStream {
uint32 write(const void *dataPtr, uint32 dataSize) override { return dataSize; }
int64 pos() const override { return 0; }
};
// Common::WriteStream *logfilestr_;
// std::streambuf *cerrbuf_;
// static Nullstreambuf cnull;
};
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,102 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_LIMIT_STACK
#define GLK_QUEST_LIMIT_STACK
namespace Glk {
namespace Quest {
template <class T> class LimitStack {
uint stack_size, cur_ptr, end_ptr;
Common::Array<T> data;
//bool last_push;
uint dofwd(uint i) const {
i ++;
return i == stack_size ? 0 : i;
}
uint dobwd(uint i) const {
return (i == 0 ? stack_size : i) - 1;
}
void fwd(uint &i) const {
i = dofwd(i);
}
void bwd(uint &i) const {
i = dobwd(i);
}
/*
void fwd (uint &i) { i ++; if (i == stack_size) i = 0; }
void bwd (uint &i) { i = (i == 0 ? stack_size : i) - 1; }
uint dofwd (uint i) { uint rv = i; fwd(rv); return rv; }
uint dobwd (uint i) { uint rv = i; bwd(rv); return rv; }
*/
public:
LimitStack(uint maxSize) : stack_size(maxSize), cur_ptr(0), end_ptr(maxSize - 1), data(Common::Array<T> (maxSize)) { }
void push(T &item) {
if (cur_ptr == end_ptr)
fwd(end_ptr);
data[cur_ptr] = item;
fwd(cur_ptr);
}
T &pop() {
assert(!is_empty());
bwd(cur_ptr);
return data[cur_ptr];
}
bool is_empty() {
return dobwd(cur_ptr) == end_ptr;
}
uint size() {
if (cur_ptr > end_ptr)
return cur_ptr - end_ptr - 1;
else
return (cur_ptr + stack_size) - end_ptr - 1;
}
void dump(Common::WriteStream &o) {
o << size() << ": < ";
for (uint i = dobwd(cur_ptr); i != end_ptr; bwd(i))
o << data[i] << " ";
o << ">";
}
T &peek() {
return data[dobwd(cur_ptr)];
}
};
template<class T> Common::WriteStream &operator<< (Common::WriteStream &o, LimitStack<T> st) {
st.dump(o);
return o;
}
} // End of namespace Quest
} // End of namespace Glk
#endif

181
engines/glk/quest/quest.cpp Normal file
View File

@@ -0,0 +1,181 @@
/* 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/quest/quest.h"
#include "glk/quest/geas_glk.h"
#include "glk/quest/geas_glk.h"
#include "glk/quest/streams.h"
#include "common/config-manager.h"
#include "common/translation.h"
namespace Glk {
namespace Quest {
Quest *g_vm;
Quest::Quest(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
_saveSlot(-1), _runner(nullptr) {
g_vm = this;
}
void Quest::runGame() {
// Check for savegame
_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
if (!initialize()) {
GUIErrorMessage(_("Could not start Quest game"));
return;
}
playGame();
deinitialize();
}
void Quest::playGame() {
char cur_buf[1024];
char buf[200];
// Check for savegame to load immediate
_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
// Set initial game state
_runner->set_game(String(getFilename().c_str()));
if (_saveSlot != -1) {
int saveSlot = _saveSlot;
_saveSlot = -1;
if (loadGameState(saveSlot).getCode() == Common::kNoError)
_runner->run_command("look");
}
banner = _runner->get_banner();
draw_banner();
while (_runner->is_running()) {
if (inputwin != mainglkwin)
glk_window_clear(inputwin);
else
glk_put_cstring("\n");
Common::sprintf_s(cur_buf, "> ");
glk_put_string_stream(inputwinstream, cur_buf);
glk_request_line_event(inputwin, buf, (sizeof buf) - 1, 0);
event_t ev;
ev.type = evtype_None;
while (ev.type != evtype_LineInput) {
glk_select(&ev);
if (shouldQuit())
return;
switch (ev.type) {
case evtype_LineInput:
if (ev.window == inputwin) {
String cmd = String(buf, ev.val1);
if (inputwin == mainglkwin)
ignore_lines = 2;
_runner->run_command(cmd);
}
break;
case evtype_Timer:
_runner->tick_timers();
break;
case evtype_Arrange:
case evtype_Redraw:
draw_banner();
break;
default:
break;
}
}
}
}
bool Quest::initialize() {
Streams::initialize();
glk_stylehint_set(wintype_TextBuffer, style_User2, stylehint_ReverseColor, 1);
// Open the main window
mainglkwin = glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 1);
if (!mainglkwin)
return false;
glk_set_window(mainglkwin);
glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
bannerwin = glk_window_open(mainglkwin,
winmethod_Above | winmethod_Fixed,
1, wintype_TextGrid, 0);
if (use_inputwindow)
inputwin = glk_window_open(mainglkwin,
winmethod_Below | winmethod_Fixed,
1, wintype_TextBuffer, 0);
else
inputwin = nullptr;
if (!inputwin)
inputwin = mainglkwin;
inputwinstream = glk_window_get_stream(inputwin);
if (!glk_gestalt(gestalt_Timer, 0)) {
const char *err = "\nNote -- The underlying Glk library does not support"
" timers. If this game tries to use timers, then some"
" functionality may not work correctly.\n\n";
glk_put_string(err);
}
glk_request_timer_events(1000);
ignore_lines = 0;
_runner = GeasRunner::get_runner(new GeasGlkInterface());
return true;
}
void Quest::deinitialize() {
Streams::deinitialize();
delete _runner;
}
Common::Error Quest::readSaveData(Common::SeekableReadStream *rs) {
GeasState *gs = _runner->getState();
gs->load(rs);
return Common::kNoError;
}
Common::Error Quest::writeGameData(Common::WriteStream *ws) {
GeasState *gs = _runner->getState();
gs->save(ws);
return Common::kNoError;
}
} // End of namespace Quest
} // End of namespace Glk

111
engines/glk/quest/quest.h Normal file
View File

@@ -0,0 +1,111 @@
/* 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/>.
*
*/
/* Based on Geas interpreter version 3.53 */
#ifndef GLK_QUEST_QUEST
#define GLK_QUEST_QUEST
#include "glk/glk_api.h"
#include "glk/quest/string.h"
#include "glk/quest/geas_runner.h"
namespace Glk {
namespace Quest {
/**
* Quest game interpreter
*/
class Quest : public GlkAPI {
private:
int _saveSlot;
GeasRunner *_runner;
public:
String banner;
private:
/**
* Engine initialization
*/
bool initialize();
/**
* Engine cleanup
*/
void deinitialize();
/**
* Inner gameplay method
*/
void playGame();
public:
/**
* Constructor
*/
Quest(OSystem *syst, const GlkGameDescription &gameDesc);
/**
* Run the game
*/
void runGame() override;
/**
* Returns the running interpreter type
*/
InterpreterType getInterpreterType() const override {
return INTERPRETER_QUEST;
}
/**
* Returns true if a savegame can be loaded
*/
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
return _runner != nullptr && GlkAPI::canLoadGameStateCurrently();
}
/**
* Returns true if the game can be saved
*/
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
return _runner != nullptr && GlkAPI::canLoadGameStateCurrently();
}
/**
* Savegames aren't supported for Quest games
*/
Common::Error readSaveData(Common::SeekableReadStream *rs) override;
/**
* Savegames aren't supported for Quest games
*/
Common::Error writeGameData(Common::WriteStream *ws) override;
/**
* Returns true if a savegame is being loaded directly from the ScummVM launcher
*/
bool loadingSavegame() const { return _saveSlot != -1; }
};
extern Quest *g_vm;
} // End of namespace Quest
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_READ_FILE
#define GLK_QUEST_READ_FILE
#include "glk/quest/geas_file.h"
#include "glk/quest/string.h"
#include "common/array.h"
namespace Glk {
namespace Quest {
extern Common::Array<String> tokenize(String s);
extern String next_token(String full, uint &tok_start, uint &tok_end, bool cvt_paren = false);
extern String first_token(String s, uint &t_start, uint &t_end);
extern String nth_token(String s, int n);
extern String get_token(String s, bool cvt_paren = false);
extern bool find_token(String s, String tok, int &tok_start, int &tok_end, bool cvt_paren = false);
extern GeasFile read_geas_file(GeasInterface *gi, const String &filename);
enum trim_modes { TRIM_SPACES, TRIM_UNDERSCORE, TRIM_BRACE };
extern String trim(String, trim_modes mode = TRIM_SPACES);
//Common::WriteStream &operator<< (Common::WriteStream &o, const Common::Array<String> &v);
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,84 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_RESERVED_WORDS
#define GLK_QUEST_RESERVED_WORDS
#include "glk/quest/string.h"
#include "common/hashmap.h"
#include "common/stream.h"
namespace Glk {
namespace Quest {
class reserved_words {
private:
StringBoolMap _data;
public:
/**
* Constructor
*/
reserved_words(const char *c, ...) {
va_list ap;
va_start(ap, c);
while (c != nullptr) {
_data[String(c)] = true;
c = va_arg(ap, const char *);
}
va_end(ap);
}
/**
* Returns true if the passed string is a reserved word
*/
bool operator[](const String &s) const {
return has(s);
}
/**
* Returns true if the passed string is a reserved word
*/
bool has(const String &s) const {
return _data.contains(s) && _data[s];
}
/**
* Dumps the list of reserved words to the passed output stream
*/
void dump(Common::WriteStream &o) const {
o.writeString("RW {");
for (StringBoolMap::iterator i = _data.begin(); i != _data.end(); ++i) {
if (i != _data.begin())
o.writeString(", ");
o.writeString((*i)._key);
}
o.writeString("}");
}
};
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,85 @@
/* 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/quest/streams.h"
#include "common/debug.h"
#include "common/str.h"
namespace Glk {
namespace Quest {
ConsoleStream *g_cerr;
const char endl = '\n';
void Streams::initialize() {
g_cerr = new ConsoleStream();
}
void Streams::deinitialize() {
delete g_cerr;
}
uint32 ConsoleStream::write(const void *dataPtr, uint32 dataSize) {
if (gDebugLevel > 0) {
Common::String s((const char *)dataPtr, (const char *)dataPtr + dataSize);
debug("%s", s.c_str());
}
return dataSize;
}
/*--------------------------------------------------------------------------*/
String ostringstream::str() {
return String((const char *)getData(), (const char *)getData() + size());
}
/*--------------------------------------------------------------------------*/
Common::WriteStream &operator<<(Common::WriteStream &ws, const String &s) {
ws.writeString(s);
return ws;
}
Common::WriteStream &operator<<(Common::WriteStream &ws, const char *s) {
ws.write(s, strlen(s));
return ws;
}
Common::WriteStream &operator<<(Common::WriteStream &ws, char c) {
ws.writeByte(c);
return ws;
}
Common::WriteStream &operator<<(Common::WriteStream &ws, int i) {
Common::String s = Common::String::format("%d", i);
ws.writeString(s);
return ws;
}
Common::WriteStream &operator<<(Common::WriteStream &ws, uint i) {
Common::String s = Common::String::format("%u", i);
ws.writeString(s);
return ws;
}
} // End of namespace Quest
} // End of namespace Glk

View File

@@ -0,0 +1,86 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_STREAMS
#define GLK_QUEST_STREAMS
#include "glk/quest/string.h"
#include "common/memstream.h"
#include "common/stream.h"
namespace Glk {
namespace Quest {
/**
* Write stream wrapper around ScummVM debug calls. Can only handle text being written
*/
class ConsoleStream : public Common::WriteStream {
public:
uint32 write(const void *dataPtr, uint32 dataSize) override;
int64 pos() const override { return 0; }
};
class ostringstream : public Common::MemoryWriteStreamDynamic {
public:
ostringstream() : Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES) {}
String str();
};
class StringStream : public ostringstream {
public:
StringStream &operator>>(String &rhs) {
rhs = str();
return *this;
}
};
/**
* Simple wrapper for managing streams initialization
*/
class Streams {
public:
/**
* Initialization
*/
static void initialize();
/**
* Deinitialization
*/
static void deinitialize();
};
extern ConsoleStream *g_cerr;
extern const char endl;
#define cerr (*g_cerr)
Common::WriteStream &operator<<(Common::WriteStream &, const String &);
Common::WriteStream &operator<<(Common::WriteStream &, const char *);
Common::WriteStream &operator<<(Common::WriteStream &, char);
Common::WriteStream &operator<<(Common::WriteStream &, int);
Common::WriteStream &operator<<(Common::WriteStream &, uint);
Common::WriteStream &operator<<(Common::WriteStream &, size_t);
} // End of namespace Quest
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,98 @@
/* 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/quest/string.h"
namespace Glk {
namespace Quest {
CI_EQUAL ci_equal_obj;
CI_LESS ci_less_obj;
CI_LESS_EQ ci_less_eq_obj;
String operator+(const String &x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const char *x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const String &x, const char *y) {
String temp(x);
temp += y;
return temp;
}
String operator+(char x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const String &x, char y) {
String temp(x);
temp += y;
return temp;
}
/*--------------------------------------------------------------------------*/
// Code for testing case insensitively by John Harrison
bool c_equal_i(char ch1, char ch2) {
return tolower((unsigned char)ch1) == tolower((unsigned char)ch2);
}
size_t ci_find(const String &str1, const String &str2) {
const char *pos = strstr(str1.c_str(), str2.c_str());
return !pos ? String::npos : pos - str1.c_str();
}
static int my_stricmp(const String &s1, const String &s2) {
return s1.compareToIgnoreCase(s2);
}
bool ci_equal(const String &str1, const String &str2) {
return my_stricmp(str1, str2) == 0;
}
bool ci_less_eq(const String &str1, const String &str2) {
return my_stricmp(str1, str2) <= 0;
}
bool ci_less(const String &str1, const String &str2) {
return my_stricmp(str1, str2) < 0;
}
bool ci_notequal(const String &str1, const String &str2) {
return !ci_equal(str1, str2);
}
bool ci_gt_eq(const String &str1, const String &str2) {
return my_stricmp(str1, str2) >= 0;
}
bool ci_gt(const String &str1, const String &str2) {
return my_stricmp(str1, str2) > 0;
}
} // End of namespace Quest
} // End of namespace Glk

134
engines/glk/quest/string.h Normal file
View File

@@ -0,0 +1,134 @@
/* 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/>.
*
*/
#ifndef GLK_QUEST_STRING
#define GLK_QUEST_STRING
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/str.h"
namespace Glk {
namespace Quest {
class String;
typedef Common::HashMap<String, String, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> StringMap;
typedef Common::HashMap<String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> StringBoolMap;
typedef Common::HashMap<String, Common::Array<int>, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> StringArrayIntMap;
typedef Common::HashMap<String, Common::Array<String>, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> StringArrayStringMap;
class String : public Common::String {
public:
String() : Common::String() {}
String(const char *str) : Common::String(str) {}
String(const char *str, uint32 len) : Common::String(str, len) {}
String(const char *beginP, const char *endP) : Common::String(beginP, endP) {}
String(const String &str) : Common::String(str) {}
explicit String(char c) : Common::String(c) {}
String& operator=(const String &str) { this->Common::String::operator=(str); return *this; }
char &operator[](int idx) {
assert(_str && idx >= 0 && idx < (int)_size);
return _str[idx];
}
inline uint length() const {
return size();
}
String substr(size_t pos, size_t len) const {
return String(c_str() + pos, c_str() + pos + len);
}
String substr(size_t pos) const {
return String(c_str() + pos);
}
int find(char c, int pos = 0) const {
const char *p = strchr(c_str() + pos, c);
return p ? p - c_str() : -1;
}
int find(const Common::String &s, int pos = 0) const {
const char *p = strstr(c_str() + pos, s.c_str());
return p ? p - c_str() : -1;
}
int rfind(char c) const {
const char *p = strrchr(c_str(), c);
return p ? p - c_str() : -1;
}
String trim() const {
String result = *this;
static_cast<Common::String>(result).trim();
return result;
}
};
// Append two strings to form a new (temp) string
String operator+(const String &x, const String &y);
String operator+(const char *x, const String &y);
String operator+(const String &x, const char *y);
String operator+(const String &x, char y);
String operator+(char x, const String &y);
bool c_equal_i(char ch1, char ch2);
size_t ci_find(const String &str1, const String &str2);
bool ci_equal(const String &str1, const String &str2);
bool ci_less_eq(const String &str1, const String &str2);
bool ci_less(const String &str1, const String &str2);
bool ci_notequal(const String &str1, const String &str2);
bool ci_gt_eq(const String &str1, const String &str2);
bool ci_gt(const String &str1, const String &str2);
class CI_EQUAL {
public:
bool operator()(const String &str1, const String &str2) {
return ci_equal(str1, str2);
}
};
class CI_LESS_EQ {
public:
bool operator()(const String &str1, const String &str2) {
return ci_less_eq(str1, str2);
}
};
class CI_LESS {
public:
bool operator()(const String &str1, const String &str2) {
return ci_less(str1, str2);
}
};
extern CI_EQUAL ci_equal_obj;
extern CI_LESS ci_less_obj;
extern CI_LESS_EQ ci_less_eq_obj;
} // End of namespace Quest
} // End of namespace Glk
#endif

471
engines/glk/quest/uncas.pl Normal file
View File

@@ -0,0 +1,471 @@
#!/usr/bin/perl
###############################################################################
# #
# Copyright (C) 2006 by Mark J. Tilford #
# #
# This file is part of Geas. #
# #
# Geas 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. #
# #
# Geas 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 Geas; if not, write to the Free Software #
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #
# #
###############################################################################
use strict;
sub mtext {
my $str = shift;
my $rv = chr(254);
foreach (split //, $str) {
$rv . = chr(255 - ord $_);
}
return $rv . chr(0);
}
sub obfus {
my $str = shift;
my $rv = chr(10);
foreach (split //, $str) {
$rv . = chr(255 - ord $_);
}
return $rv . chr(0);
}
my $is_raw = 0;
my @hash_data =
([1, 'game'], [2, 'procedure'], [3, 'room'], [4, 'object'],
[5, 'character'], [6, 'text'], [7, 'selection'], [8, 'define'],
[9, 'end'], [11, 'asl-version'], [12, 'game'], [13, 'version'],
[14, 'author'], [15, 'copyright'], [16, 'info'], [17, 'start'],
[18, 'possitems'], [19, 'startitems'], [20, 'prefix'], [21, 'look'],
[22, 'out'], [23, 'gender'], [24, 'speak'], [25, 'take'], [26, 'alias'],
[27, 'place'], [28, 'east'], [29, 'north'], [30, 'west'], [31, 'south'],
[32, 'give'], [33, 'hideobject'], [34, 'hidechar'], [35, 'showobject'],
[36, 'showchar'], [37, 'collectable'], [38, 'collecatbles'],
[39, 'command'], [40, 'use'], [41, 'hidden'], [42, 'script'],
[43, 'font'], [44, 'default'], [45, 'fontname'], [46, 'fontsize'],
[47, 'startscript'], [48, 'nointro'], [49, 'indescription'],
[50, 'description'], [51, 'function'], [52, 'setvar'], [53, 'for'],
[54, 'error'], [55, 'synonyms'], [56, 'beforeturn'], [57, 'afterturn'],
[58, 'invisible'], [59, 'nodebug'], [60, 'suffix'], [61, 'startin'],
[62, 'northeast'], [63, 'northwest'], [64, 'southeast'],
[65, 'southwest'], [66, 'items'], [67, 'examine'], [68, 'detail'],
[69, 'drop'], [70, 'everywhere'], [71, 'nowhere'], [72, 'on'],
[73, 'anything'], [74, 'article'], [75, 'gain'], [76, 'properties'],
[77, 'type'], [78, 'action'], [79, 'displaytype'], [80, 'override'],
[81, 'enabled'], [82, 'disabled'], [83, 'variable'], [84, 'value'],
[85, 'display'], [86, 'nozero'], [87, 'onchange'], [88, 'timer'],
[89, 'alt'], [90, 'lib'], [91, 'up'], [92, 'down'], [93, 'gametype'],
[94, 'singleplayer'], [95, 'multiplayer'], [150, 'do'], [151, 'if'],
[152, 'got'], [153, 'then'], [154, 'else'], [155, 'has'], [156, 'say'],
[157, 'playwav'], [158, 'lose'], [159, 'msg'], [160, 'not'],
[161, 'playerlose'], [162, 'playerwin'], [163, 'ask'], [164, 'goto'],
[165, 'set'], [166, 'show'], [167, 'choice'], [168, 'choose'],
[169, 'is'], [170, 'setstring'], [171, 'displaytext'], [172, 'exec'],
[173, 'pause'], [174, 'clear'], [175, 'debug'], [176, 'enter'],
[177, 'movechar'], [178, 'moveobject'], [179, 'revealchar'],
[180, 'revealobject'], [181, 'concealchar'], [182, 'concealobject'],
[183, 'mailto'], [184, 'and'], [185, 'or'], [186, 'outputoff'],
[187, 'outputon'], [188, 'here'], [189, 'playmidi'], [190, 'drop'],
[191, 'helpmsg'], [192, 'helpdisplaytext'], [193, 'helpclear'],
[194, 'helpclose'], [195, 'hide'], [196, 'show'], [197, 'move'],
[198, 'conceal'], [199, 'reveal'], [200, 'numeric'], [201, 'string'],
[202, 'collectable'], [203, 'property'], [204, 'create'], [205, 'exit'],
[206, 'doaction'], [207, 'close'], [208, 'each'], [209, 'in'],
[210, 'repeat'], [211, 'while'], [212, 'until'], [213, 'timeron'],
[214, 'timeroff'], [215, 'stop'], [216, 'panes'], [217, 'on'],
[218, 'off'], [219, 'return'], [220, 'playmod'], [221, 'modvolume'],
[222, 'clone'], [223, 'shellexe'], [224, 'background'],
[225, 'foreground'], [226, 'wait'], [227, 'picture'], [228, 'nospeak'],
[229, 'animate'], [230, 'persist'], [231, 'inc'], [232, 'dec'],
[233, 'flag'], [234, 'dontprocess'], [235, 'destroy'],
[236, 'beforesave'], [237, 'onload']);
my % tokens = ();
my % rtokens = ();
foreach (@hash_data) {
if ($_->[0] >= 0 && $_->[0] < 256) {
if ($_->[1] eq '') {
$_->[1] = "[?" . $_->[0] . "?]";
}
$rtokens{chr($_->[0])} = $_->[1];
$tokens{$_->[1]} = chr($_->[0]);
}
}
#print "{";
#for (my $i = 0; $i < 256; $i ++) {
# print "\"", $rtokens{chr($i)}, "\", ";
#}
#print "}\n";
#die;
my % text_block_starters = map { $_ => 1 } qw / text synonyms type /;
sub uncompile_fil {
my $IFH;
open($IFH, "<", $_[0]);
binmode $IFH;
$ / = undef;
my $dat = < $IFH >;
#print "uncompile_fil : ";
#print "\$IFH == '$IFH',";
#print "\$dat == '$dat'\n";
my @dat = split //, $dat;
my $OFH;
if (@_ == 1) {
push @_, "&STDOUT";
}
open $OFH, ">$_[1]" or die "Can't open '$_[1]' for output: $!";
my @output = ();
my $curline = "";
my $obfus = 0;
my $expect_text == 0;
my($ch, $chn, $tok);
for (my $n = 8; $n < @dat; $n ++) {
$ch = $dat[$n];
$chn = ord $ch;
$tok = $rtokens{$ch};
if ($obfus == 1 && $chn == 0) {
#print $OFH "> ";
$curline . = "> ";
$obfus = 0;
}
elsif($obfus == 1) {
#print $OFH chr (255 - $chn);
$curline . = chr(255 - $chn);
}
elsif($obfus == 2 && $chn == 254) {
$obfus = 0;
#print $OFH " ";
$curline . = " ";
}
elsif($obfus == 2) {
#print $OFH chr ($chn);
$curline . = chr($chn);
}
elsif($expect_text == 2) {
if ($chn == 253) {
$expect_text = 0;
##print $OFH "\n";
push @output, $curline;
$curline = "";
}
elsif($chn == 0) {
#print $OFH "\n";
push @output, $curline;
$curline = "";
}
else {
#print $OFH chr (255 - $chn);
$curline . = chr(255 - $chn);
}
}
elsif($obfus == 0 && $chn == 10) {
#print $OFH "<";
$curline . = "<";
$obfus = 1;
}
elsif($obfus == 0 && $chn == 254) {
$obfus = 2;
}
elsif($chn == 255) {
if ($expect_text == 1) {
$expect_text = 2;
}
#print $OFH "\n";
push @output, $curline;
$curline = "";
}
else {
if (($tok eq 'text' || $tok eq 'synonyms' || $tok eq 'type') &&
$dat[$n - 1] eq chr(8)) {
$expect_text = 1;
}
#print $OFH "$tok ";
$curline . = "$tok ";
}
}
push @output, $curline;
$curline = "";
if (!$is_raw) {
@output = pretty_print(reinline(@output));
}
foreach (@output) {
print $OFH $_, "\r\n";
}
}
sub list_grab_file {
my $IFH;
open($IFH, "<:crlf", $_[0]);
my @rv = < $IFH >;
chomp @rv;
return @rv;
}
sub compile_fil {
my @dat = list_grab_file($ARGV[0]);
my $OFH;
open $OFH, ">$ARGV[1]";
print $OFH "QCGF200".chr(0);
# Mode 0 == normal, mode 1 == block text
my $mode = 0;
for (my $n = 0; $n < @dat; $n ++) {
my $l = $dat[$n];
while (substr($l, length($l) - 1, 1) eq '_' && $n < @dat) {
$n ++;
$l = substr($l, 0, length($l) - 1) . $dat[$n];
}
if ($l = ~ / ^ !include *<([\S] *)> /) {
@dat = (@dat[0..$n], list_grab_file($1), @dat[$n + 1..$#dat]);
}
elsif($l = ~ / ^ !addto.* /) {
# TODO
}
else {
my $i = 0;
my $max = length $l;
my @l = split //, $l;
if ($mode == 1) {
if ($l = ~ / ^\s * end\s * define\s*$ /) {
print $OFH chr(253);
$mode = 0;
# FALL THROUGH
} else {
#print $OFH chr(0);
foreach (split //, $l) {
print $OFH chr(255 - ord $_);
}
next;
}
}
if ($l = ~ / ^\s*$ /) {
next;
}
if ($l = ~ / ^\s * define\s * (text | synonyms | type) /) {
#[\s$]
$mode = 1;
}
while ($i < $max) {
while ($i <= $max && $l[$i] = ~ / \s /) {
++ $i;
}
if ($i == $max) {
next;
}
my $j = $i + 1;
if ($l[$i] eq '<') {
while ($j < $max && $l[$j] ne '>') {
++ $j;
}
if ($l[$j] eq '>') {
print $OFH obfus(substr($l, $i + 1, $j - $i - 1));
$i = $j + 1;
next;
}
$j = $i + 1;
while ($j < $max && $l[$j] ne ' ') {
++ $j;
}
print $OFH chr(254). substr($l, $i + 1, $j - $i - 1). chr(0);
$i = $j + 1;
next;
}
while ($j < $max && $l[$j] ne ' ') {
++ $j;
}
my $str = substr($l, $i, $j - $i);
if (defined $tokens{$str}) {
print $OFH $tokens{$str};
}
else {
print $OFH chr(254). $str. chr(254);
}
$i = $j + 1;
}
}
print $OFH chr(255);
}
}
sub is_define_t {
my($line, $type) = (@_);
return ($line = ~ / ^ *define[\s] + $type + /);
}
sub is_define {
my($line) = (@_);
return ($line = ~ / ^ *define[\s] + [^\s] /);
}
sub is_end_define { return (shift = ~ / ^ *end + define *$ /); }
sub trim {
my $tmp = trim1($_[0]);
#print "trimming ($_[0]) -> ($tmp)\n";
return $tmp;
}
sub trim1 {
if ($_[0] = ~ / ^[\s] * (.* ?)[\s]*$ /) {
return $1;
}
print "* * * Huh on trimming '$_[0]' * * *\n";
}
sub reinline {
my % reinlined = ();
my @head_prog = ();
my @rest_prog = ();
while (@_) {
push @rest_prog, (pop @_);
}
while (@rest_prog) {
my $line = pop @rest_prog;
#print "processing $line\n";
if ($line = ~ / ^ (.* |)do ( < !intproc[0 - 9] * >) * (.*)$ /) {
#print " reinlining...\n";
my($prefix, $func_name, $suffix) = ($1, $2, $3);
$prefix = trim($prefix);
$suffix = trim($suffix);
$reinlined{$func_name} = 1;
for (my $line_num = 0; $line_num < @rest_prog; $line_num ++) {
if ($rest_prog[$line_num] = ~ / ^ *define + procedure + $func_name *$ /) {
my $end_line = $line_num;
while (!is_end_define($rest_prog[$end_line])) {
#print " checking $rest_prog[$end_line]\n";
-- $end_line;
}
++ $end_line;
#print " backpushing } ".$suffix."\n";
#push @rest_prog, trim ("} " . $suffix);
if ($suffix ne '') {
push @rest_prog, $suffix;
}
push @rest_prog, "}";
while ($end_line < $line_num) {
push @rest_prog, $rest_prog[$end_line];
#print " backpushing $rest_prog[$end_line]\n";
$end_line ++;
}
#print " backpushing $prefix {\n";
push @rest_prog, trim($prefix." {");
$line_num = scalar @rest_prog;
}
}
}
else {
push @head_prog, $line;
}
}
my @rv = ();
for (my $n = 0; $n < @head_prog; $n ++) {
if ($head_prog[$n] = ~ / ^define procedure(<.*>) *$ / &&
$reinlined{$1}) {
while (!is_end_define($head_prog[$n])) {
++ $n;
}
}
else {
push @rv, $head_prog[$n];
}
}
#for (my $n = 0; $n < @rv; $n ++) {
# print "$n: $rv[$n]\n";
#}
return @rv;
}
sub pretty_print {
my $indent = 0;
my $not_in_text_mode = 1;
my @rv = ();
for (my $n = 0; $n < @_; $n ++) {
my $line = $_[$n];
if (is_end_define($line)) {
-- $indent;
$not_in_text_mode = 1;
}
/ { /; if ($line = ~ / ^} /) {
-- $indent;
}
###if (is_define ($line) && ($n == 0 || !is_define ($_[$n-1]))) { print "\n"; }
if (is_define($line) && ($n == 0 || !is_define($_[$n - 1]))) {
push @rv, "";
}
###if ($in_text_mode == 0) { print " "x$indent; }
push @rv, (" "x($indent*$not_in_text_mode)).trim($line);
###print $line, " line $n, indent $indent, text $in_text_mode\n";
###print $line, "\n";
if (is_end_define($line) && $n < @_ && !is_end_define($_[$n + 1])
&& !is_define($_[$n + 1])) {
###print "\n";
push @rv, "";
}
if (is_define($line)) {
++ $indent;
}
if ($line = ~ / {$ /) {
++ $indent;
} /
} /;
if ($line = ~ / ^ *define + text /) {
$not_in_text_mode = 0;
}
}
return @rv;
}
sub error_msg {
die "Usage: 'perl uncas.pl file.asl file2.cas' to compile to file\n".
" 'perl uncas.pl file.cas' to decompile to console\n".
" 'perl uncas.pl file.cas file2.asl' to decompile to file\n";
}
if ($ARGV[0] eq '-raw') {
$is_raw = 1;
shift @ARGV;
}
if ($ARGV[0] = ~ / \.asl$ /) {
if (@ARGV != 2) {
error_msg();
}
compile_fil(@ARGV);
}
elsif($ARGV[0] = ~ / \.cas$ /) {
#print "compile_fil (", join (", ", @ARGV), ")\n";
if (@ARGV != 1 && @ARGV != 2) {
error_msg();
}
uncompile_fil(@ARGV);
}