scummvm/engines/glk/quest/geas_file.cpp
2021-12-26 18:48:43 +01:00

659 lines
19 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/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 obj_tag_property("look", "examine", "speak", "take", "alias", "prefix", "suffix", "detail", "displaytype", "gender", "article", "hidden", "invisible", (char *) nullptr);
// 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;
Set<String> rv;
get_obj_keys(obj, rv);
return rv;
}
void GeasFile::get_obj_keys(String obj, Set<String> &rv) const {
cerr << "get_obj_keys (gf, <" << obj << ">)\n";
//Set<String> rv;
uint c1, c2;
String tok, line;
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