Initial commit
This commit is contained in:
100
engines/glk/quest/detection.cpp
Normal file
100
engines/glk/quest/detection.cpp
Normal 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
|
||||
64
engines/glk/quest/detection.h
Normal file
64
engines/glk/quest/detection.h
Normal 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
|
||||
136
engines/glk/quest/detection_tables.h
Normal file
136
engines/glk/quest/detection_tables.h
Normal 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
|
||||
656
engines/glk/quest/geas_file.cpp
Normal file
656
engines/glk/quest/geas_file.cpp
Normal 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
|
||||
122
engines/glk/quest/geas_file.h
Normal file
122
engines/glk/quest/geas_file.h
Normal 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
|
||||
234
engines/glk/quest/geas_glk.cpp
Normal file
234
engines/glk/quest/geas_glk.cpp
Normal 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
|
||||
91
engines/glk/quest/geas_glk.h
Normal file
91
engines/glk/quest/geas_glk.h
Normal 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
|
||||
214
engines/glk/quest/geas_impl.h
Normal file
214
engines/glk/quest/geas_impl.h
Normal 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
|
||||
3683
engines/glk/quest/geas_runner.cpp
Normal file
3683
engines/glk/quest/geas_runner.cpp
Normal file
File diff suppressed because it is too large
Load Diff
221
engines/glk/quest/geas_runner.h
Normal file
221
engines/glk/quest/geas_runner.h
Normal 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
|
||||
343
engines/glk/quest/geas_state.cpp
Normal file
343
engines/glk/quest/geas_state.cpp
Normal 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
|
||||
202
engines/glk/quest/geas_state.h
Normal file
202
engines/glk/quest/geas_state.h
Normal 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
|
||||
246
engines/glk/quest/geas_util.cpp
Normal file
246
engines/glk/quest/geas_util.cpp
Normal 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
|
||||
96
engines/glk/quest/geas_util.h
Normal file
96
engines/glk/quest/geas_util.h
Normal 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
|
||||
102
engines/glk/quest/limit_stack.h
Normal file
102
engines/glk/quest/limit_stack.h
Normal 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
181
engines/glk/quest/quest.cpp
Normal 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
111
engines/glk/quest/quest.h
Normal 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
|
||||
1036
engines/glk/quest/read_file.cpp
Normal file
1036
engines/glk/quest/read_file.cpp
Normal file
File diff suppressed because it is too large
Load Diff
48
engines/glk/quest/read_file.h
Normal file
48
engines/glk/quest/read_file.h
Normal 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
|
||||
84
engines/glk/quest/reserved_words.h
Normal file
84
engines/glk/quest/reserved_words.h
Normal 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
|
||||
85
engines/glk/quest/streams.cpp
Normal file
85
engines/glk/quest/streams.cpp
Normal 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
|
||||
86
engines/glk/quest/streams.h
Normal file
86
engines/glk/quest/streams.h
Normal 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
|
||||
98
engines/glk/quest/string.cpp
Normal file
98
engines/glk/quest/string.cpp
Normal 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
134
engines/glk/quest/string.h
Normal 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
471
engines/glk/quest/uncas.pl
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user