2007-05-30 21:56:52 +00:00
|
|
|
|
/* ScummVM - Graphic Adventure Engine
|
2006-05-23 23:43:52 +00:00
|
|
|
|
*
|
2007-05-30 21:56:52 +00:00
|
|
|
|
* 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.
|
2006-05-23 23:43:52 +00:00
|
|
|
|
*
|
2021-12-26 18:47:58 +01:00
|
|
|
|
* 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.
|
2014-02-18 02:34:17 +01:00
|
|
|
|
*
|
2006-05-23 23:43:52 +00:00
|
|
|
|
* 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.
|
2014-02-18 02:34:17 +01:00
|
|
|
|
*
|
2006-05-23 23:43:52 +00:00
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-12-26 18:47:58 +01:00
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2006-05-23 23:43:52 +00:00
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "agi/agi.h"
|
2016-01-29 13:13:40 +01:00
|
|
|
|
#include "agi/words.h"
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2011-04-24 11:34:27 +03:00
|
|
|
|
#include "common/textconsole.h"
|
|
|
|
|
|
2006-05-23 23:43:52 +00:00
|
|
|
|
namespace Agi {
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
Words::Words(AgiEngine *vm) {
|
|
|
|
|
_vm = vm;
|
|
|
|
|
|
|
|
|
|
clearEgoWords();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Words::~Words() {
|
|
|
|
|
clearEgoWords();
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
int Words::loadDictionary_v1(Common::File &f) {
|
2011-06-16 12:34:40 +03:00
|
|
|
|
char str[64];
|
|
|
|
|
int k;
|
2011-06-16 18:46:06 +03:00
|
|
|
|
|
|
|
|
|
debug(0, "Loading dictionary");
|
2012-09-26 04:17:31 +02:00
|
|
|
|
|
2011-06-16 12:34:40 +03:00
|
|
|
|
// Loop through alphabet, as words in the dictionary file are sorted by
|
|
|
|
|
// first character
|
|
|
|
|
f.seek(f.pos() + 26 * 2, SEEK_SET);
|
|
|
|
|
do {
|
|
|
|
|
// Read next word
|
|
|
|
|
for (k = 0; k < (int)sizeof(str) - 1; k++) {
|
|
|
|
|
str[k] = f.readByte();
|
|
|
|
|
if (str[k] == 0 || (uint8)str[k] == 0xFF)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// And store it in our internal dictionary
|
|
|
|
|
if (k > 0) {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
WordEntry *newWord = new WordEntry;
|
|
|
|
|
byte firstCharNr = str[0] - 'a';
|
|
|
|
|
|
|
|
|
|
newWord->word = Common::String(str, k + 1); // myStrndup(str, k + 1);
|
|
|
|
|
newWord->id = f.readUint16LE();
|
2016-02-02 18:43:36 +01:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
_dictionaryWords[firstCharNr].push_back(newWord);
|
|
|
|
|
debug(3, "'%s' (%d)", newWord->word.c_str(), newWord->id);
|
2011-06-16 12:34:40 +03:00
|
|
|
|
}
|
2016-02-02 18:43:36 +01:00
|
|
|
|
} while ((uint8)str[0] != 0xFF);
|
2011-06-16 12:34:40 +03:00
|
|
|
|
|
|
|
|
|
return errOK;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
int Words::loadDictionary(const char *fname) {
|
2006-05-23 23:43:52 +00:00
|
|
|
|
Common::File fp;
|
|
|
|
|
|
2006-05-24 19:51:37 +00:00
|
|
|
|
if (!fp.open(fname)) {
|
2010-10-26 22:33:49 +00:00
|
|
|
|
warning("loadWords: can't open %s", fname);
|
2009-06-06 17:39:13 +00:00
|
|
|
|
return errOK; // err_BadFileOpen
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
2010-10-26 22:33:49 +00:00
|
|
|
|
debug(0, "Loading dictionary: %s", fname);
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2011-06-16 11:07:05 +03:00
|
|
|
|
// Loop through alphabet, as words in the dictionary file are sorted by
|
|
|
|
|
// first character
|
2024-02-29 16:20:11 +02:00
|
|
|
|
char str[64] = { 0 };
|
|
|
|
|
char c;
|
2011-06-16 11:07:05 +03:00
|
|
|
|
for (int i = 0; i < 26; i++) {
|
|
|
|
|
fp.seek(i * 2, SEEK_SET);
|
|
|
|
|
int offset = fp.readUint16BE();
|
2011-06-16 18:46:06 +03:00
|
|
|
|
if (offset == 0)
|
|
|
|
|
continue;
|
2011-06-16 11:07:05 +03:00
|
|
|
|
fp.seek(offset, SEEK_SET);
|
|
|
|
|
int k = fp.readByte();
|
|
|
|
|
while (!fp.eos() && !fp.err()) {
|
|
|
|
|
// Read next word
|
|
|
|
|
do {
|
|
|
|
|
c = fp.readByte();
|
|
|
|
|
str[k++] = (c ^ 0x7F) & 0x7F;
|
|
|
|
|
} while (!(c & 0x80) && k < (int)sizeof(str) - 1);
|
|
|
|
|
str[k] = 0;
|
|
|
|
|
|
2013-10-01 19:58:51 +02:00
|
|
|
|
// WORKAROUND:
|
|
|
|
|
// The SQ0 fan game stores words starting with numbers (like '7up')
|
|
|
|
|
// in its dictionary under the 'a' entry. We skip these.
|
2021-02-28 01:28:23 -08:00
|
|
|
|
// See bug #6415
|
2013-10-01 19:58:51 +02:00
|
|
|
|
if (str[0] == 'a' + i) {
|
|
|
|
|
// And store it in our internal dictionary
|
2016-01-29 13:13:40 +01:00
|
|
|
|
WordEntry *newWord = new WordEntry;
|
|
|
|
|
newWord->word = Common::String(str, k);
|
|
|
|
|
newWord->id = fp.readUint16BE();
|
|
|
|
|
_dictionaryWords[i].push_back(newWord);
|
2024-02-29 16:20:11 +02:00
|
|
|
|
} else {
|
|
|
|
|
fp.readUint16BE();
|
2013-10-01 19:58:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
k = fp.readByte();
|
2011-06-16 11:07:05 +03:00
|
|
|
|
|
|
|
|
|
// Are there more words with an already known prefix?
|
2013-10-01 19:58:51 +02:00
|
|
|
|
// WORKAROUND: We only break after already seeing words with the
|
|
|
|
|
// right prefix, for the SQ0 words starting with digits filed under
|
2021-02-28 01:28:23 -08:00
|
|
|
|
// 'a'. See above comment and bug #6415.
|
2013-10-01 19:58:51 +02:00
|
|
|
|
if (k == 0 && str[0] >= 'a' + i)
|
2011-06-16 11:07:05 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 12:40:51 +00:00
|
|
|
|
return errOK;
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-02 13:42:54 +02:00
|
|
|
|
int Words::loadExtendedDictionary(const char *sierraFname) {
|
|
|
|
|
Common::String fnameStr = Common::String(sierraFname) + ".extended";
|
|
|
|
|
const char *fname = fnameStr.c_str();
|
|
|
|
|
|
|
|
|
|
Common::File fp;
|
|
|
|
|
|
|
|
|
|
if (!fp.open(fname)) {
|
|
|
|
|
warning("loadWords: can't open %s", fname);
|
|
|
|
|
return errOK; // err_BadFileOpen
|
|
|
|
|
}
|
|
|
|
|
debug(0, "Loading dictionary: %s", fname);
|
|
|
|
|
|
|
|
|
|
// skip the header
|
|
|
|
|
fp.readString('\n');
|
|
|
|
|
|
|
|
|
|
while (!fp.eos() && !fp.err()) {
|
|
|
|
|
WordEntry *newWord = new WordEntry;
|
|
|
|
|
newWord->word = fp.readString();
|
|
|
|
|
newWord->id = atoi(fp.readString('\n').c_str());
|
|
|
|
|
if(!newWord->word.empty())
|
|
|
|
|
_dictionaryWords[(byte)newWord->word[0] - 'a'].push_back(newWord);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errOK;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
void Words::unloadDictionary() {
|
|
|
|
|
for (int16 firstCharNr = 0; firstCharNr < 26; firstCharNr++) {
|
|
|
|
|
Common::Array<WordEntry *> &dictionary = _dictionaryWords[firstCharNr];
|
|
|
|
|
int16 dictionarySize = dictionary.size();
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
for (int16 dictionaryWordNr = 0; dictionaryWordNr < dictionarySize; dictionaryWordNr++) {
|
|
|
|
|
delete dictionary[dictionaryWordNr];
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
_dictionaryWords[firstCharNr].clear();
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
void Words::clearEgoWords() {
|
|
|
|
|
for (int16 wordNr = 0; wordNr < MAX_WORDS; wordNr++) {
|
|
|
|
|
_egoWords[wordNr].id = 0;
|
|
|
|
|
_egoWords[wordNr].word.clear();
|
|
|
|
|
}
|
|
|
|
|
_egoWordCount = 0;
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
static bool isCharSeparator(const char curChar) {
|
|
|
|
|
switch (curChar) {
|
|
|
|
|
case ' ':
|
|
|
|
|
case ',':
|
|
|
|
|
case '.':
|
|
|
|
|
case '?':
|
|
|
|
|
case '!':
|
|
|
|
|
case '(':
|
|
|
|
|
case ')':
|
|
|
|
|
case ';':
|
|
|
|
|
case ':':
|
|
|
|
|
case '[':
|
|
|
|
|
case ']':
|
|
|
|
|
case '{':
|
|
|
|
|
case '}':
|
|
|
|
|
return true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
static bool isCharInvalid(const char curChar) {
|
|
|
|
|
switch (curChar) {
|
|
|
|
|
case 0x27: // '
|
|
|
|
|
case 0x60: // `
|
|
|
|
|
case '-':
|
|
|
|
|
case '\\':
|
|
|
|
|
case '"':
|
|
|
|
|
return true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
void Words::cleanUpInput(const char *rawUserInput, Common::String &cleanInput) {
|
|
|
|
|
byte curChar = 0;
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
cleanInput.clear();
|
2009-06-06 17:39:13 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
curChar = *rawUserInput;
|
|
|
|
|
while (curChar) {
|
|
|
|
|
// skip separators / invalid characters
|
|
|
|
|
if (isCharSeparator(curChar) || isCharInvalid(curChar)) {
|
|
|
|
|
rawUserInput++;
|
|
|
|
|
curChar = *rawUserInput;
|
|
|
|
|
} else {
|
|
|
|
|
do {
|
|
|
|
|
if (!isCharInvalid(curChar)) {
|
|
|
|
|
// not invalid char, add it to the cleaned up input
|
|
|
|
|
cleanInput += curChar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rawUserInput++;
|
|
|
|
|
curChar = *rawUserInput;
|
|
|
|
|
|
|
|
|
|
if (isCharSeparator(curChar)) {
|
|
|
|
|
cleanInput += ' ';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (curChar);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cleanInput.hasSuffix(" ")) {
|
|
|
|
|
// ends with a space? remove it
|
|
|
|
|
cleanInput.deleteLastChar();
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-06 17:39:13 +00:00
|
|
|
|
|
2016-01-30 03:13:17 +01:00
|
|
|
|
int16 Words::findWordInDictionary(const Common::String &userInputLowcased, uint16 userInputLen, uint16 userInputPos, uint16 &foundWordLen) {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
uint16 userInputLeft = userInputLen - userInputPos;
|
|
|
|
|
uint16 wordStartPos = userInputPos;
|
|
|
|
|
int16 wordId = DICTIONARY_RESULT_UNKNOWN;
|
2016-01-30 03:13:17 +01:00
|
|
|
|
byte firstChar = userInputLowcased[userInputPos];
|
2016-01-29 13:13:40 +01:00
|
|
|
|
|
|
|
|
|
foundWordLen = 0;
|
|
|
|
|
|
2022-09-14 10:06:34 -07:00
|
|
|
|
const byte lastCharInAbc = _vm->getFeatures() & GF_EXTCHAR ? 0xff : 'z';
|
2021-03-02 13:42:54 +02:00
|
|
|
|
|
|
|
|
|
if ((firstChar >= 'a') && (firstChar <= lastCharInAbc)) {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
// word has to start with a letter
|
2016-01-30 03:13:17 +01:00
|
|
|
|
if (((userInputPos + 1) < userInputLen) && (userInputLowcased[userInputPos + 1] == ' ')) {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
// current word is 1 char only?
|
|
|
|
|
if ((firstChar == 'a') || (firstChar == 'i')) {
|
|
|
|
|
// and it's "a" or "i"? -> then set current type to ignore
|
|
|
|
|
wordId = DICTIONARY_RESULT_IGNORE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-06 17:39:13 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
Common::Array<WordEntry *> &dictionary = _dictionaryWords[firstChar - 'a'];
|
|
|
|
|
int16 dictionarySize = dictionary.size();
|
|
|
|
|
|
|
|
|
|
for (int16 dictionaryWordNr = 0; dictionaryWordNr < dictionarySize; dictionaryWordNr++) {
|
|
|
|
|
WordEntry *dictionaryEntry = dictionary[dictionaryWordNr];
|
|
|
|
|
uint16 dictionaryWordLen = dictionaryEntry->word.size();
|
|
|
|
|
|
|
|
|
|
if (dictionaryWordLen <= userInputLeft) {
|
|
|
|
|
// dictionary word is longer or same length as the remaining user input
|
|
|
|
|
uint16 curCompareLeft = dictionaryWordLen;
|
|
|
|
|
uint16 dictionaryWordPos = 0;
|
|
|
|
|
|
|
|
|
|
userInputPos = wordStartPos;
|
|
|
|
|
while (curCompareLeft) {
|
2024-03-10 17:16:09 -06:00
|
|
|
|
byte curUserInputChar = userInputLowcased[userInputPos];
|
|
|
|
|
byte curDictionaryChar = dictionaryEntry->word[dictionaryWordPos];
|
2016-01-29 13:13:40 +01:00
|
|
|
|
|
|
|
|
|
if (curUserInputChar != curDictionaryChar)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
userInputPos++;
|
|
|
|
|
dictionaryWordPos++;
|
|
|
|
|
curCompareLeft--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!curCompareLeft) {
|
2016-01-30 03:13:17 +01:00
|
|
|
|
// check, if there is also nothing more of user input left or if a space the follow-up char?
|
|
|
|
|
if ((userInputPos >= userInputLen) || (userInputLowcased[userInputPos] == ' ')) {
|
|
|
|
|
// so fully matched, remember match
|
|
|
|
|
wordId = dictionaryEntry->id;
|
|
|
|
|
foundWordLen = dictionaryWordLen;
|
|
|
|
|
|
|
|
|
|
// perfect match? -> exit loop
|
|
|
|
|
if (userInputLeft == foundWordLen) {
|
|
|
|
|
// perfect match -> break
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-01-29 13:13:40 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
2016-01-29 13:13:40 +01:00
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
if (foundWordLen == 0) {
|
|
|
|
|
userInputPos = wordStartPos;
|
|
|
|
|
while (userInputPos < userInputLen) {
|
2016-01-30 03:13:17 +01:00
|
|
|
|
if (userInputLowcased[userInputPos] == ' ') {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
userInputPos++;
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
2016-01-29 13:13:40 +01:00
|
|
|
|
foundWordLen = userInputPos - wordStartPos;
|
|
|
|
|
}
|
|
|
|
|
return wordId;
|
|
|
|
|
}
|
2006-05-23 23:43:52 +00:00
|
|
|
|
|
2016-02-28 15:50:25 +01:00
|
|
|
|
void Words::parseUsingDictionary(const char *rawUserInput) {
|
2016-01-29 13:13:40 +01:00
|
|
|
|
Common::String userInput;
|
2016-01-30 03:13:17 +01:00
|
|
|
|
Common::String userInputLowcased;
|
2016-01-29 13:13:40 +01:00
|
|
|
|
const char *userInputPtr = nullptr;
|
|
|
|
|
uint16 userInputLen;
|
|
|
|
|
uint16 userInputPos = 0;
|
|
|
|
|
uint16 foundWordLen = 0;
|
|
|
|
|
uint16 wordCount = 0;
|
|
|
|
|
|
|
|
|
|
assert(rawUserInput);
|
|
|
|
|
debugC(2, kDebugLevelScripts, "parse: userinput = \"%s\"", rawUserInput);
|
|
|
|
|
|
|
|
|
|
// Reset result
|
|
|
|
|
clearEgoWords();
|
|
|
|
|
|
|
|
|
|
// clean up user input
|
|
|
|
|
cleanUpInput(rawUserInput, userInput);
|
|
|
|
|
|
2016-01-30 03:13:17 +01:00
|
|
|
|
// Sierra compared independent of upper case and lower case
|
|
|
|
|
userInputLowcased = userInput;
|
|
|
|
|
userInputLowcased.toLowercase();
|
|
|
|
|
|
2021-07-31 14:54:11 +02:00
|
|
|
|
if (_vm->getLanguage() == Common::RU_RUS) {
|
|
|
|
|
const char *conv =
|
|
|
|
|
// АБВГДЕЖЗИЙКЛМНОП
|
|
|
|
|
"abvgdewziiklmnop" // 80
|
|
|
|
|
// РСТУФХЦЧШЩЪЫЬЭЮЯ
|
|
|
|
|
"rstufxcyhhjijeuq" // 90
|
|
|
|
|
// абвгдежзийклмноп
|
|
|
|
|
"abvgdewziiklmnop" // a0
|
|
|
|
|
" " // b0
|
|
|
|
|
" " // c0
|
|
|
|
|
" " // d0
|
|
|
|
|
// рстуфхцчшщъыьэюя
|
|
|
|
|
"rstufxcyhhjijeuq" // e0
|
|
|
|
|
// Ее
|
|
|
|
|
"ee ";// f0
|
|
|
|
|
|
|
|
|
|
Common::String tr;
|
|
|
|
|
for (uint i = 0; i < userInputLowcased.size(); i++) {
|
|
|
|
|
if ((byte)userInputLowcased[i] >= 0x80) {
|
|
|
|
|
tr += conv[(byte)userInputLowcased[i] - 0x80];
|
|
|
|
|
} else {
|
|
|
|
|
tr += (byte)userInputLowcased[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
userInputLowcased = tr;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
userInputLen = userInput.size();
|
|
|
|
|
userInputPtr = userInput.c_str();
|
|
|
|
|
|
2020-06-17 11:32:41 +03:00
|
|
|
|
// WORKAROUND: For Apple II support speed changes
|
|
|
|
|
// some of the games hadn't this feature
|
|
|
|
|
// some (like PQ1) had it, but we override the speed that the game request
|
|
|
|
|
// with `timeDelayOverwrite`
|
|
|
|
|
// this mechanism works for all the games, and therefore, doesn't bother to search in the dictionary
|
|
|
|
|
if (_vm->getPlatform() == Common::kPlatformApple2GS) {
|
|
|
|
|
if (userInput.equals("fastest")) {
|
|
|
|
|
_vm->_game.setAppleIIgsSpeedLevel(0);
|
|
|
|
|
return;
|
|
|
|
|
} else if (userInput.equals("fast")) {
|
|
|
|
|
_vm->_game.setAppleIIgsSpeedLevel(1);
|
|
|
|
|
return;
|
|
|
|
|
} else if (userInput.equals("normal")) {
|
|
|
|
|
_vm->_game.setAppleIIgsSpeedLevel(2);
|
|
|
|
|
return;
|
|
|
|
|
} else if (userInput.equals("slow")) {
|
|
|
|
|
_vm->_game.setAppleIIgsSpeedLevel(3);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
while (userInputPos < userInputLen) {
|
|
|
|
|
// Skip trailing space
|
|
|
|
|
if (userInput[userInputPos] == ' ')
|
|
|
|
|
userInputPos++;
|
|
|
|
|
|
2024-03-10 17:16:09 -06:00
|
|
|
|
uint16 foundWordPos = userInputPos;
|
|
|
|
|
int16 foundWordId = findWordInDictionary(userInputLowcased, userInputLen, userInputPos, foundWordLen);
|
2016-01-29 13:13:40 +01:00
|
|
|
|
|
|
|
|
|
if (foundWordId != DICTIONARY_RESULT_IGNORE) {
|
|
|
|
|
// word not supposed to get ignored
|
|
|
|
|
// add it now
|
|
|
|
|
if (foundWordId != DICTIONARY_RESULT_UNKNOWN) {
|
|
|
|
|
// known word
|
|
|
|
|
_egoWords[wordCount].id = foundWordId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_egoWords[wordCount].word = Common::String(userInputPtr + foundWordPos, foundWordLen);
|
|
|
|
|
debugC(2, kDebugLevelScripts, "found word %s (id %d)", _egoWords[wordCount].word.c_str(), _egoWords[wordCount].id);
|
|
|
|
|
wordCount++;
|
|
|
|
|
|
|
|
|
|
if (foundWordId == DICTIONARY_RESULT_UNKNOWN) {
|
|
|
|
|
// unknown word
|
|
|
|
|
_vm->setVar(VM_VAR_WORD_NOT_FOUND, wordCount);
|
|
|
|
|
break; // and exit now
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-29 13:13:40 +01:00
|
|
|
|
|
|
|
|
|
userInputPos += foundWordLen;
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-29 13:13:40 +01:00
|
|
|
|
_egoWordCount = wordCount;
|
|
|
|
|
|
|
|
|
|
debugC(4, kDebugLevelScripts, "ego word count = %d", _egoWordCount);
|
|
|
|
|
if (_egoWordCount > 0) {
|
2016-01-31 17:56:53 +01:00
|
|
|
|
_vm->setFlag(VM_FLAG_ENTERED_CLI, true);
|
2016-01-29 13:13:40 +01:00
|
|
|
|
} else {
|
2016-01-31 17:56:53 +01:00
|
|
|
|
_vm->setFlag(VM_FLAG_ENTERED_CLI, false);
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
2016-01-31 17:56:53 +01:00
|
|
|
|
_vm->setFlag(VM_FLAG_SAID_ACCEPTED_INPUT, false);
|
2016-01-29 13:13:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16 Words::getEgoWordCount() {
|
|
|
|
|
return _egoWordCount;
|
|
|
|
|
}
|
|
|
|
|
const char *Words::getEgoWord(int16 wordNr) {
|
|
|
|
|
assert(wordNr >= 0 && wordNr < MAX_WORDS);
|
|
|
|
|
return _egoWords[wordNr].word.c_str();
|
|
|
|
|
}
|
|
|
|
|
uint16 Words::getEgoWordId(int16 wordNr) {
|
|
|
|
|
assert(wordNr >= 0 && wordNr < MAX_WORDS);
|
|
|
|
|
return _egoWords[wordNr].id;
|
2006-05-23 23:43:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-06 17:39:13 +00:00
|
|
|
|
} // End of namespace Agi
|