mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
872 lines
22 KiB
C++
872 lines
22 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/>.
|
||
*
|
||
*/
|
||
|
||
/*
|
||
* Based on ScottFree interpreter version 1.14 developed by Swansea
|
||
* University Computer Society without disassembly of any other game
|
||
* drivers, only of game databases as permitted by EEC law (for purposes
|
||
* of compatibility).
|
||
*
|
||
* Licensed under GPLv2
|
||
*
|
||
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
|
||
*/
|
||
|
||
#include "common/str.h"
|
||
#include "glk/scott/scott.h"
|
||
#include "glk/scott/globals.h"
|
||
#include "glk/scott/command_parser.h"
|
||
|
||
namespace Glk {
|
||
namespace Scott {
|
||
|
||
#define MAX_WORDLENGTH 128
|
||
#define MAX_WORDS 128
|
||
#define MAX_BUFFER 128
|
||
|
||
void freeStrings() {
|
||
if (_G(_firstErrorMessage) != nullptr) {
|
||
delete[] _G(_firstErrorMessage);
|
||
_G(_firstErrorMessage) = nullptr;
|
||
}
|
||
if (_G(_wordsInInput) == 0) {
|
||
if (_G(_unicodeWords) != nullptr || _G(_charWords) != nullptr) {
|
||
g_scott->fatal("ERROR! Wordcount 0 but word arrays not empty!\n");
|
||
}
|
||
return;
|
||
}
|
||
for (int i = 0; i < _G(_wordsInInput); i++) {
|
||
if (_G(_unicodeWords)[i] != nullptr)
|
||
delete _G(_unicodeWords)[i];
|
||
if (_G(_charWords)[i] != nullptr)
|
||
delete _G(_charWords)[i];
|
||
}
|
||
|
||
delete _G(_unicodeWords);
|
||
_G(_unicodeWords) = nullptr;
|
||
delete _G(_charWords);
|
||
_G(_charWords) = nullptr;
|
||
_G(_wordsInInput) = 0;
|
||
}
|
||
|
||
void createErrorMessage(const char *fchar, glui32 *second, const char *tchar) {
|
||
if (_G(_firstErrorMessage) != nullptr)
|
||
return;
|
||
glui32 *first = toUnicode(fchar);
|
||
glui32 *third = toUnicode(tchar);
|
||
glui32 buffer[MAX_BUFFER];
|
||
int i, j = 0, k = 0;
|
||
for (i = 0; first[i] != 0 && i < MAX_BUFFER; i++)
|
||
buffer[i] = first[i];
|
||
if (second != nullptr) {
|
||
for (j = 0; second[j] != 0 && i + j < MAX_BUFFER; j++)
|
||
buffer[i + j] = second[j];
|
||
}
|
||
if (third != nullptr) {
|
||
for (k = 0; third[k] != 0 && i + j + k < MAX_BUFFER; k++)
|
||
buffer[i + j + k] = third[k];
|
||
delete[] third;
|
||
}
|
||
int length = i + j + k;
|
||
_G(_firstErrorMessage) = new glui32[(length + 1) * 4];
|
||
memcpy(_G(_firstErrorMessage), buffer, length * 4);
|
||
_G(_firstErrorMessage)[length] = 0;
|
||
delete[] first;
|
||
}
|
||
|
||
void printPendingError(void) {
|
||
if (_G(_firstErrorMessage)) {
|
||
g_scott->glk_put_string_stream_uni(g_scott->glk_window_get_stream(_G(_bottomWindow)), _G(_firstErrorMessage));
|
||
delete[] _G(_firstErrorMessage);
|
||
_G(_firstErrorMessage) = nullptr;
|
||
_G(_stopTime) = 1;
|
||
}
|
||
}
|
||
|
||
char **LineInput(void) {
|
||
event_t ev;
|
||
glui32 unibuf[512];
|
||
|
||
do {
|
||
g_scott->display(_G(_bottomWindow), "\n%s", _G(_sys)[WHAT_NOW].c_str());
|
||
g_scott->glk_request_line_event_uni(_G(_bottomWindow), unibuf, (glui32)511, 0);
|
||
|
||
while (ev.type != evtype_Quit) {
|
||
g_scott->glk_select(&ev);
|
||
if (ev.type == evtype_Quit)
|
||
return nullptr;
|
||
|
||
if (ev.type == evtype_LineInput)
|
||
break;
|
||
else
|
||
g_scott->updates(ev);
|
||
}
|
||
|
||
unibuf[ev.val1] = 0;
|
||
|
||
if (_G(_transcript)) {
|
||
g_scott->glk_put_string_stream_uni(_G(_transcript), unibuf);
|
||
g_scott->glk_put_char_stream_uni(_G(_transcript), 10);
|
||
}
|
||
|
||
_G(_charWords) = splitIntoWords(unibuf, ev.val1);
|
||
|
||
if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
|
||
g_scott->output(_G(_sys)[HUH]);
|
||
else {
|
||
return _G(_charWords);
|
||
}
|
||
|
||
} while (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr);
|
||
return nullptr;
|
||
}
|
||
|
||
int matchYMCA(glui32 *string, int length, int index) {
|
||
const char *ymca = "y.m.c.a.";
|
||
int i;
|
||
for (i = 0; i < 8; i++) {
|
||
if (i + index > length || string[index + i] != static_cast<unsigned int>(ymca[i]))
|
||
return i;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
char **splitIntoWords(glui32 *string, int length) {
|
||
if (length < 1) {
|
||
return nullptr;
|
||
}
|
||
|
||
g_scott->glk_buffer_to_lower_case_uni(string, 256, length);
|
||
g_scott->glk_buffer_canon_normalize_uni(string, 256, length);
|
||
|
||
int startpos[MAX_WORDS];
|
||
int wordlength[MAX_WORDS];
|
||
|
||
int words_found = 0;
|
||
int word_index = 0;
|
||
int foundspace = 0;
|
||
int foundcomma = 0;
|
||
startpos[0] = 0;
|
||
wordlength[0] = 0;
|
||
int lastwasspace = 1;
|
||
for (int i = 0; string[i] != 0 && i < length && word_index < MAX_WORDS; i++) {
|
||
foundspace = 0;
|
||
switch (string[i]) {
|
||
case 'y': {
|
||
int ymca = matchYMCA(string, length, i);
|
||
if (ymca > 3) {
|
||
/* Start a new word */
|
||
startpos[words_found] = i;
|
||
wordlength[words_found] = ymca;
|
||
words_found++;
|
||
wordlength[words_found] = 0;
|
||
i += ymca;
|
||
if (i < length)
|
||
foundspace = 1;
|
||
lastwasspace = 0;
|
||
}
|
||
} break;
|
||
/* Unicode space and tab variants */
|
||
case ' ':
|
||
case '\t':
|
||
case '!':
|
||
case '?':
|
||
case '\"':
|
||
case 0x83: // ¿
|
||
case 0x80: // ¡
|
||
case 0xa0: // non-breaking space
|
||
case 0x2000: // en quad
|
||
case 0x2001: // em quad
|
||
case 0x2003: // em
|
||
case 0x2004: // three-per-em
|
||
case 0x2005: // four-per-em
|
||
case 0x2006: // six-per-em
|
||
case 0x2007: // figure space
|
||
case 0x2009: // thin space
|
||
case 0x200A: // hair space
|
||
case 0x202f: // narrow no-break space
|
||
case 0x205f: // medium mathematical space
|
||
case 0x3000: // ideographic space
|
||
foundspace = 1;
|
||
break;
|
||
case '.':
|
||
case ',':
|
||
foundcomma = 1;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
if (!foundspace) {
|
||
if (lastwasspace || foundcomma) {
|
||
/* Start a new word */
|
||
startpos[words_found] = i;
|
||
words_found++;
|
||
wordlength[words_found] = 0;
|
||
}
|
||
wordlength[words_found - 1]++;
|
||
lastwasspace = 0;
|
||
} else {
|
||
/* Check if the last character of previous word was a period or comma */
|
||
lastwasspace = 1;
|
||
foundcomma = 0;
|
||
}
|
||
}
|
||
|
||
if (words_found == 0) {
|
||
return nullptr;
|
||
}
|
||
|
||
wordlength[words_found]--; /* Don't count final newline character */
|
||
|
||
/* Now we've created two arrays, one for starting positions
|
||
and one for word length. Now we convert these into an array of strings */
|
||
glui32 **words = new glui32 *[words_found];
|
||
char **words8 = new char *[words_found];
|
||
|
||
for (int i = 0; i < words_found; i++) {
|
||
words[i] = new glui32[(wordlength[i] + 1) * 4];
|
||
memcpy(words[i], string + startpos[i], wordlength[i] * 4);
|
||
words[i][wordlength[i]] = 0;
|
||
words8[i] = fromUnicode(words[i], wordlength[i]);
|
||
}
|
||
_G(_unicodeWords) = words;
|
||
_G(_wordsInInput) = words_found;
|
||
|
||
return words8;
|
||
}
|
||
|
||
int findVerb(const char *string, Common::StringArray *list) {
|
||
*list = _G(_verbs);
|
||
int verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (verb) {
|
||
return verb;
|
||
}
|
||
*list = _G(_directions);
|
||
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (verb) {
|
||
if (verb == 13)
|
||
verb = 4;
|
||
if (verb > 6)
|
||
verb -= 6;
|
||
return verb;
|
||
}
|
||
*list = _G(_abbreviations);
|
||
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (verb) {
|
||
verb = whichWord(_G(_abbreviationsKey)[verb].c_str(), _G(_verbs), _G(_gameHeader)->_wordLength);
|
||
if (verb) {
|
||
list = &_G(_verbs);
|
||
return verb;
|
||
}
|
||
}
|
||
|
||
int stringlength = strlen(string);
|
||
|
||
*list = _G(_skipList);
|
||
verb = whichWord(string, *list, stringlength);
|
||
if (verb) {
|
||
return 0;
|
||
}
|
||
*list = _G(_nouns);
|
||
verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (verb) {
|
||
return verb;
|
||
}
|
||
|
||
*list = _G(_extraCommands);
|
||
verb = whichWord(string, *list, stringlength);
|
||
if (verb) {
|
||
verb = _G(_extraCommandsKey)[verb];
|
||
return verb + _G(_gameHeader)->_numWords;
|
||
}
|
||
|
||
*list = _G(_extraNouns);
|
||
verb = whichWord(string, *list, stringlength);
|
||
if (verb) {
|
||
verb = _G(_extraNounsKey)[verb];
|
||
return verb + _G(_gameHeader)->_numWords;
|
||
}
|
||
|
||
*list = _G(_delimiterList);
|
||
verb = whichWord(string, *list, stringlength);
|
||
if (!verb)
|
||
*list = Common::StringArray();
|
||
return verb;
|
||
}
|
||
|
||
int findExtraneousWords(int *index, int noun) {
|
||
/* Looking for extraneous words that should invalidate the command */
|
||
int originalIndex = *index;
|
||
if (*index >= _G(_wordsInInput)) {
|
||
return 0;
|
||
}
|
||
Common::StringArray list;
|
||
int verb = 0;
|
||
int stringlength = strlen(_G(_charWords)[*index]);
|
||
|
||
list = _G(_skipList);
|
||
do {
|
||
verb = whichWord(_G(_charWords)[*index], _G(_skipList), stringlength);
|
||
if (verb)
|
||
*index = *index + 1;
|
||
} while (verb && *index < _G(_wordsInInput));
|
||
|
||
if (*index >= _G(_wordsInInput))
|
||
return 0;
|
||
|
||
verb = findVerb(_G(_charWords)[*index], &list);
|
||
|
||
if (list == _G(_delimiterList)) {
|
||
if (*index > originalIndex)
|
||
*index = *index - 1;
|
||
return 0;
|
||
}
|
||
|
||
if (list == _G(_nouns) && noun) {
|
||
if (g_scott->mapSynonym(noun) == g_scott->mapSynonym(verb)) {
|
||
*index = *index + 1;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (list.empty()) {
|
||
if (*index >= _G(_wordsInInput))
|
||
*index = _G(_wordsInInput) - 1;
|
||
createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[*index], _G(_sys)[IS].c_str());
|
||
} else {
|
||
createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
Command *commandFromStrings(int index, Command *previous);
|
||
|
||
Command *createCommandStruct(int verb, int noun, int verbIndex, int nounIndex, Command *previous) {
|
||
Command *command = new Command;
|
||
command->_verb = verb;
|
||
command->_noun = noun;
|
||
command->_allFlag = 0;
|
||
command->_item = 0;
|
||
command->_previous = previous;
|
||
command->_verbWordIndex = verbIndex;
|
||
if (noun && nounIndex > 0) {
|
||
command->_nounWordIndex = nounIndex - 1;
|
||
} else {
|
||
command->_nounWordIndex = 0;
|
||
}
|
||
command->_next = commandFromStrings(nounIndex, command);
|
||
return command;
|
||
}
|
||
|
||
int findNoun(const char *string, Common::StringArray *list) {
|
||
*list = _G(_nouns);
|
||
int noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (noun) {
|
||
return noun;
|
||
}
|
||
|
||
*list = _G(_directions);
|
||
noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (noun) {
|
||
if (noun > 6)
|
||
noun -= 6;
|
||
*list = _G(_nouns);
|
||
return noun;
|
||
}
|
||
|
||
int stringLength = strlen(string);
|
||
|
||
*list = _G(_extraNouns);
|
||
|
||
noun = whichWord(string, *list, stringLength);
|
||
if (noun) {
|
||
noun = _G(_extraNounsKey)[noun];
|
||
return noun + _G(_gameHeader)->_numWords;
|
||
}
|
||
|
||
*list = _G(_skipList);
|
||
noun = whichWord(string, *list, stringLength);
|
||
if (noun) {
|
||
return 0;
|
||
}
|
||
|
||
*list = _G(_verbs);
|
||
noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
|
||
if (noun) {
|
||
return noun;
|
||
}
|
||
|
||
*list = _G(_delimiterList);
|
||
noun = whichWord(string, *list, stringLength);
|
||
|
||
if (!noun)
|
||
*list = Common::StringArray();
|
||
return 0;
|
||
}
|
||
|
||
Command *commandFromStrings(int index, Command *previous) {
|
||
if (index < 0 || index >= _G(_wordsInInput)) {
|
||
return nullptr;
|
||
}
|
||
Common::StringArray list;
|
||
int verb = 0;
|
||
int i = index;
|
||
|
||
do {
|
||
/* Checking if it is a verb */
|
||
verb = findVerb(_G(_charWords)[i++], &list);
|
||
} while ((list == _G(_skipList) || list == _G(_delimiterList)) && i < _G(_wordsInInput));
|
||
|
||
int verbindex = i - 1;
|
||
|
||
if (list == _G(_directions)) {
|
||
/* It is a direction */
|
||
if (verb == 0 || findExtraneousWords(&i, 0) != 0)
|
||
return nullptr;
|
||
return createCommandStruct(GO, verb, 0, i, previous);
|
||
}
|
||
|
||
int found_noun_at_verb_position = 0;
|
||
int lastverb = 0;
|
||
|
||
if (list == _G(_nouns) || list == _G(_extraNouns)) {
|
||
/* It is a noun */
|
||
/* If we find no verb, we try copying the verb from the previous command */
|
||
if (previous) {
|
||
lastverb = previous->_verb;
|
||
}
|
||
/* Unless the game is German, where we allow the noun to come before the
|
||
* verb */
|
||
if (CURRENT_GAME != GREMLINS_GERMAN && CURRENT_GAME != GREMLINS_GERMAN_C64) {
|
||
if (!previous) {
|
||
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
|
||
return nullptr;
|
||
} else {
|
||
verbindex = previous->_verbWordIndex;
|
||
}
|
||
if (findExtraneousWords(&i, verb) != 0)
|
||
return nullptr;
|
||
|
||
return createCommandStruct(lastverb, verb, verbindex, i, previous);
|
||
} else {
|
||
found_noun_at_verb_position = 1;
|
||
}
|
||
}
|
||
|
||
if (list.empty() || list == _G(_skipList)) {
|
||
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
|
||
return nullptr;
|
||
}
|
||
|
||
if (i == _G(_wordsInInput)) {
|
||
if (lastverb) {
|
||
return createCommandStruct(lastverb, verb, previous->_verbWordIndex, i, previous);
|
||
} else if (found_noun_at_verb_position) {
|
||
createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
|
||
return nullptr;
|
||
} else {
|
||
return createCommandStruct(verb, 0, i - 1, i, previous);
|
||
}
|
||
}
|
||
|
||
int noun = 0;
|
||
|
||
do {
|
||
/* Check if it is a noun */
|
||
noun = findNoun(_G(_charWords)[i++], &list);
|
||
} while (list == _G(_skipList) && i < _G(_wordsInInput));
|
||
|
||
if (list == _G(_nouns) || list == _G(_extraNouns)) {
|
||
/* It is a noun */
|
||
|
||
/* Check if it is an ALL followed by EXCEPT */
|
||
int except = 0;
|
||
if (list == _G(_extraNouns) && i < _G(_wordsInInput) && noun - _G(_gameHeader)->_numWords == ALL) {
|
||
int stringlength = strlen(_G(_charWords)[i]);
|
||
except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
|
||
}
|
||
if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, noun) != 0)
|
||
return nullptr;
|
||
if (found_noun_at_verb_position) {
|
||
int realverb = whichWord(_G(_charWords)[i - 1], _G(_verbs), _G(_gameHeader)->_wordLength);
|
||
if (realverb) {
|
||
noun = verb;
|
||
verb = realverb;
|
||
} else if (lastverb) {
|
||
noun = verb;
|
||
verb = lastverb;
|
||
}
|
||
}
|
||
return createCommandStruct(verb, noun, verbindex, i, previous);
|
||
}
|
||
|
||
if (list == _G(_delimiterList)) {
|
||
/* It is a delimiter */
|
||
return createCommandStruct(verb, 0, verbindex, i, previous);
|
||
}
|
||
|
||
if (list == _G(_verbs) && found_noun_at_verb_position) {
|
||
/* It is a verb */
|
||
/* Check if it is an ALL followed by EXCEPT */
|
||
int except = 0;
|
||
if (i < _G(_wordsInInput) && verb - _G(_gameHeader)->_numWords == ALL) {
|
||
int stringlength = strlen(_G(_charWords)[i]);
|
||
except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
|
||
}
|
||
if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, 0) != 0)
|
||
return nullptr;
|
||
return createCommandStruct(noun, verb, i - 1, i, previous);
|
||
}
|
||
|
||
createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[IS].c_str());
|
||
return nullptr;
|
||
}
|
||
|
||
int createAllCommands(Command *command) {
|
||
|
||
Common::Array<int> exceptions(_G(_gameHeader)->_numItems);
|
||
int exceptioncount = 0;
|
||
|
||
int location = CARRIED;
|
||
if (command->_verb == TAKE)
|
||
location = MY_LOC;
|
||
|
||
Command *next = command->_next;
|
||
/* Check if the ALL command is followed by EXCEPT */
|
||
/* and if it is, build an array of items to be excepted */
|
||
while (next && next->_verb == _G(_gameHeader)->_numWords + EXCEPT) {
|
||
for (int i = 0; i <= _G(_gameHeader)->_numItems; i++) {
|
||
if (!_G(_items)[i]._autoGet.empty() && scumm_strnicmp(_G(_items)[i]._autoGet.c_str(), _G(_charWords)[next->_nounWordIndex], _G(_gameHeader)->_wordLength) == 0) {
|
||
exceptions[exceptioncount++] = i;
|
||
}
|
||
}
|
||
/* Remove the EXCEPT command from the linked list of commands */
|
||
next = next->_next;
|
||
delete command->_next;
|
||
command->_next = next;
|
||
}
|
||
|
||
Command *c = command;
|
||
int found = 0;
|
||
for (int i = 0; i < _G(_gameHeader)->_numItems; i++) {
|
||
if (!_G(_items)[i]._autoGet.empty() && _G(_items)[i]._autoGet[0] != '*' && _G(_items)[i]._location == location) {
|
||
int exception = 0;
|
||
for (int j = 0; j < exceptioncount; j++) {
|
||
if (exceptions[j] == i) {
|
||
exception = 1;
|
||
break;
|
||
}
|
||
}
|
||
if (!exception) {
|
||
if (found) {
|
||
c->_next = new Command;
|
||
c->_next->_previous = c;
|
||
c = c->_next;
|
||
}
|
||
found = 1;
|
||
c->_verb = command->_verb;
|
||
c->_noun = whichWord(_G(_items)[i]._autoGet.c_str(), _G(_nouns), _G(_gameHeader)->_wordLength);
|
||
c->_item = i;
|
||
c->_next = nullptr;
|
||
c->_nounWordIndex = 0;
|
||
c->_allFlag = 1;
|
||
}
|
||
}
|
||
}
|
||
if (found == 0) {
|
||
if (command->_verb == TAKE)
|
||
createErrorMessage(_G(_sys)[NOTHING_HERE_TO_TAKE].c_str(), nullptr, nullptr);
|
||
else
|
||
createErrorMessage(_G(_sys)[YOU_HAVE_NOTHING].c_str(), nullptr, nullptr);
|
||
return 0;
|
||
} else {
|
||
c->_next = next;
|
||
c->_allFlag = 1 | LASTALL;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
int getInput(int *vb, int *no) {
|
||
if (_G(_currentCommand) && _G(_currentCommand)->_next) {
|
||
_G(_currentCommand) = _G(_currentCommand)->_next;
|
||
} else {
|
||
printPendingError();
|
||
if (_G(_currentCommand))
|
||
freeCommands();
|
||
_G(_charWords) = LineInput();
|
||
|
||
if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
|
||
return 0;
|
||
|
||
_G(_currentCommand) = commandFromStrings(0, nullptr);
|
||
}
|
||
|
||
if (_G(_currentCommand) == nullptr) {
|
||
printPendingError();
|
||
return 1;
|
||
}
|
||
|
||
/* We use NumWords + verb for our extra commands */
|
||
/* such as UNDO and TRANSCRIPT */
|
||
if (_G(_currentCommand)->_verb > _G(_gameHeader)->_numWords) {
|
||
if (!g_scott->performExtraCommand(0)) {
|
||
createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
|
||
}
|
||
return 1;
|
||
/* And NumWords + noun for our extra nouns */
|
||
/* such as ALL */
|
||
} else if (_G(_currentCommand)->_noun > _G(_gameHeader->_numWords)) {
|
||
_G(_currentCommand)->_noun -= _G(_gameHeader)->_numWords;
|
||
if (_G(_currentCommand)->_noun == ALL) {
|
||
if (_G(_currentCommand)->_verb != TAKE && _G(_currentCommand)->_verb != DROP) {
|
||
createErrorMessage(_G(_sys)[CANT_USE_ALL].c_str(), nullptr, nullptr);
|
||
return 1;
|
||
}
|
||
if (!createAllCommands(_G(_currentCommand)))
|
||
return 1;
|
||
} else if (_G(_currentCommand)->_noun == IT) {
|
||
_G(_currentCommand)->_noun = _G(_lastNoun);
|
||
}
|
||
}
|
||
|
||
*vb = _G(_currentCommand)->_verb;
|
||
*no = _G(_currentCommand)->_noun;
|
||
|
||
if (*no > 6) {
|
||
_G(_lastNoun) = *no;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void freeCommands() {
|
||
while (_G(_currentCommand) && _G(_currentCommand)->_previous)
|
||
_G(_currentCommand) = _G(_currentCommand)->_previous;
|
||
while (_G(_currentCommand)) {
|
||
Command *temp = _G(_currentCommand);
|
||
_G(_currentCommand) = _G(_currentCommand)->_next;
|
||
delete temp;
|
||
}
|
||
_G(_currentCommand) = nullptr;
|
||
freeStrings();
|
||
if (_G(_firstErrorMessage))
|
||
delete[] _G(_firstErrorMessage);
|
||
_G(_firstErrorMessage) = nullptr;
|
||
}
|
||
|
||
glui32 *toUnicode(const char *string) {
|
||
if (string == nullptr)
|
||
return nullptr;
|
||
glui32 unicode[2048];
|
||
int i;
|
||
int dest = 0;
|
||
for (i = 0; string[i] != 0 && i < 2047; i++) {
|
||
char c = string[i];
|
||
if (c == '\n')
|
||
c = 10;
|
||
glui32 unichar = (glui32)c;
|
||
if (_G(_game) && (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64)) {
|
||
const char d = string[i + 1];
|
||
if (c == 'u' && d == 'e') { // ü
|
||
if (!(i > 2 && string[i - 1] == 'e')) {
|
||
unichar = 0xfc;
|
||
i++;
|
||
}
|
||
} else if (c == 'o' && d == 'e') {
|
||
unichar = 0xf6; // ö
|
||
i++;
|
||
} else if (c == 'a' && d == 'e') {
|
||
unichar = 0xe4; // ä
|
||
i++;
|
||
} else if (c == 's' && d == 's') {
|
||
if (string[i + 2] != 'c' && string[i - 2] != 'W' && !(string[i - 1] == 'a' && string[i - 2] == 'l') && string[i + 2] != '-' && string[i - 2] != 'b') {
|
||
unichar = 0xdf; // ß
|
||
i++;
|
||
}
|
||
} else if (c == 'U' && d == 'E') {
|
||
unichar = 0xdc; // Ü
|
||
i++;
|
||
}
|
||
if (c == '\"') {
|
||
unichar = 0x2019; // ’
|
||
}
|
||
} else if (_G(_game) && CURRENT_GAME == GREMLINS_SPANISH) {
|
||
switch (c) {
|
||
case '\x83':
|
||
unichar = 0xbf; // ¿
|
||
break;
|
||
case '\x80':
|
||
unichar = 0xa1; // ¡
|
||
break;
|
||
case '\x82':
|
||
unichar = 0xfc; // ü
|
||
break;
|
||
case '{':
|
||
unichar = 0xe1; // á
|
||
break;
|
||
case '}':
|
||
unichar = 0xed; // í
|
||
break;
|
||
case '|':
|
||
unichar = 0xf3; // ó
|
||
break;
|
||
case '~':
|
||
unichar = 0xf1; // ñ
|
||
break;
|
||
case '\x84':
|
||
unichar = 0xe9; // é
|
||
break;
|
||
case '\x85':
|
||
unichar = 0xfa; // ú
|
||
break;
|
||
}
|
||
} else if (_G(_game) && CURRENT_GAME == TI994A) {
|
||
switch (c) {
|
||
case '@':
|
||
unicode[dest++] = 0xa9;
|
||
unichar = ' ';
|
||
break;
|
||
case '}':
|
||
unichar = 0xfc;
|
||
break;
|
||
case 12:
|
||
unichar = 0xf6;
|
||
break;
|
||
case '{':
|
||
unichar = 0xe4;
|
||
break;
|
||
}
|
||
}
|
||
unicode[dest++] = unichar;
|
||
}
|
||
unicode[dest] = 0;
|
||
glui32 *result = new glui32[(dest + 1) * 4];
|
||
memcpy(result, unicode, (dest + 1) * 4);
|
||
return result;
|
||
}
|
||
|
||
char *fromUnicode(glui32 *unicodeString, int origLength) {
|
||
int sourcepos = 0;
|
||
int destpos = 0;
|
||
|
||
char dest[MAX_WORDLENGTH];
|
||
glui32 unichar = unicodeString[sourcepos];
|
||
while (unichar != 0 && destpos < MAX_WORDLENGTH && sourcepos < origLength) {
|
||
switch (unichar) {
|
||
case '.':
|
||
if (origLength == 1) {
|
||
dest[destpos++] = 'a';
|
||
dest[destpos++] = 'n';
|
||
dest[destpos++] = 'd';
|
||
} else {
|
||
dest[destpos] = (char)unichar;
|
||
}
|
||
break;
|
||
case 0xf6: // ö
|
||
dest[destpos++] = 'o';
|
||
dest[destpos] = 'e';
|
||
break;
|
||
case 0xe4: // ä
|
||
dest[destpos++] = 'a';
|
||
dest[destpos] = 'e';
|
||
break;
|
||
case 0xfc: // ü
|
||
dest[destpos] = 'u';
|
||
if (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64) {
|
||
destpos++;
|
||
dest[destpos] = 'e';
|
||
}
|
||
break;
|
||
case 0xdf: // ß
|
||
dest[destpos++] = 's';
|
||
dest[destpos] = 's';
|
||
break;
|
||
case 0xed: // í
|
||
dest[destpos] = 'i';
|
||
break;
|
||
case 0xe1: // á
|
||
dest[destpos] = 'a';
|
||
break;
|
||
case 0xf3: // ó
|
||
dest[destpos] = 'o';
|
||
break;
|
||
case 0xf1: // ñ
|
||
dest[destpos] = 'n';
|
||
break;
|
||
case 0xe9: // é
|
||
dest[destpos] = 'e';
|
||
break;
|
||
default:
|
||
dest[destpos] = (char)unichar;
|
||
break;
|
||
}
|
||
sourcepos++;
|
||
destpos++;
|
||
unichar = unicodeString[sourcepos];
|
||
}
|
||
if (destpos == 0)
|
||
return nullptr;
|
||
char *result = new char[destpos + 1];
|
||
memcpy(result, dest, destpos);
|
||
|
||
result[destpos] = 0;
|
||
return result;
|
||
}
|
||
|
||
int recheckForExtraCommand() {
|
||
const char *verbWord = _G(_charWords)[_G(_currentCommand)->_verbWordIndex];
|
||
|
||
int extraVerb = whichWord(verbWord, _G(_extraCommands), _G(_gameHeader)->_wordLength);
|
||
if (!extraVerb) {
|
||
return 0;
|
||
}
|
||
int ExtraNoun = 0;
|
||
if (_G(_currentCommand)->_noun) {
|
||
const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
|
||
ExtraNoun = whichWord(nounWord, _G(_extraNouns), strlen(nounWord));
|
||
}
|
||
_G(_currentCommand)->_verb = _G(_extraCommandsKey)[extraVerb];
|
||
if (ExtraNoun)
|
||
_G(_currentCommand)->_noun = _G(_extraNounsKey)[ExtraNoun];
|
||
|
||
return g_scott->performExtraCommand(1);
|
||
}
|
||
|
||
int whichWord(const char *word, Common::StringArray list, int wordLength) {
|
||
int n = 1;
|
||
unsigned int ne = 1;
|
||
const char *tp;
|
||
while (ne < list.size()) {
|
||
tp = list[ne].c_str();
|
||
if (*tp == '*')
|
||
tp++;
|
||
else
|
||
n = ne;
|
||
if (scumm_strnicmp(word, tp, wordLength) == 0)
|
||
return (n);
|
||
ne++;
|
||
}
|
||
return (0);
|
||
}
|
||
|
||
} // End of namespace Scott
|
||
} // End of namespace Glk
|