aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillem Jan Palenstijn2010-10-03 20:58:50 +0000
committerWillem Jan Palenstijn2010-10-03 20:58:50 +0000
commit11d9f1ec54351bcaf8eed8a55a96a474370808d9 (patch)
treec9fceb5a1bf51ad88dd3c4ce798673d5d1959ab6
parent0596971decae5d64857cf01eb11cf5694043440d (diff)
downloadscummvm-rg350-11d9f1ec54351bcaf8eed8a55a96a474370808d9.tar.gz
scummvm-rg350-11d9f1ec54351bcaf8eed8a55a96a474370808d9.tar.bz2
scummvm-rg350-11d9f1ec54351bcaf8eed8a55a96a474370808d9.zip
SCI: Support alternative inputs from vocab 913
This allows the input of accented characters and Japanese using plain ascii in non-English games. svn-id: r53001
-rw-r--r--engines/sci/graphics/controls.cpp20
-rw-r--r--engines/sci/parser/vocabulary.cpp101
-rw-r--r--engines/sci/parser/vocabulary.h36
3 files changed, 154 insertions, 3 deletions
diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index 70ff70daab..e4e7bc0c92 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -159,6 +159,8 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
error("kEditControl called on object that doesnt have a text reference");
text = _segMan->getString(textReference);
+ uint16 oldCursorPos = cursorPos;
+
if (!eventObject.isNull()) {
textSize = text.size();
eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
@@ -223,6 +225,11 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
}
}
+ if (!textChanged && oldCursorPos != cursorPos) {
+ assert(!textAddChar);
+ textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos);
+ }
+
if (textChanged) {
GuiResourceId oldFontId = _text16->GetFontId();
GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
@@ -230,18 +237,27 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom)));
_text16->SetFont(fontId);
if (textAddChar) {
- // We check, if we are really able to add the new char
- uint16 textWidth = 0;
+
const char *textPtr = text.c_str();
+
+ // We check if we are really able to add the new char
+ uint16 textWidth = 0;
while (*textPtr)
textWidth += _text16->_font->getCharWidth((byte)*textPtr++);
textWidth += _text16->_font->getCharWidth(eventKey);
+
+ // Does it fit?
if (textWidth >= rect.width()) {
_text16->SetFont(oldFontId);
return;
}
+
text.insertChar(eventKey, cursorPos++);
+
+ // Note: the following checkAltInput call might make the text
+ // too wide to fit, but SSCI fails to check that too.
}
+ g_sci->getVocabulary()->checkAltInput(text, cursorPos);
texteditCursorErase();
_paint16->eraseRect(rect);
_text16->Box(text.c_str(), 0, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1);
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index a6de61167f..f9989b22a8 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -73,6 +73,8 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
_parserRules = NULL;
}
+ loadAltInputs();
+
parser_base = NULL_REG;
parser_event = NULL_REG;
parserIsValid = false;
@@ -81,6 +83,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
Vocabulary::~Vocabulary() {
freeRuleList(_parserRules);
freeSuffixes();
+ freeAltInputs();
}
void Vocabulary::reset() {
@@ -272,6 +275,104 @@ bool Vocabulary::loadBranches() {
return true;
}
+bool Vocabulary::loadAltInputs() {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 1);
+
+ if (!resource)
+ return true; // it's not a problem if this resource doesn't exist
+
+ const char *data = (const char*)resource->data;
+ const char *data_end = data + resource->size;
+
+ _altInputs.clear();
+ _altInputs.resize(256);
+
+ while (data < data_end && *data) {
+ AltInput t;
+ t._input = data;
+
+ unsigned int l = strlen(data);
+ t._inputLength = l;
+ data += l + 1;
+
+ t._replacement = data;
+ l = strlen(data);
+ data += l + 1;
+
+ if (data < data_end && strncmp(data, t._input, t._inputLength) == 0)
+ t._prefix = true;
+ else
+ t._prefix = false;
+
+ unsigned char firstChar = t._input[0];
+ _altInputs[firstChar].push_front(t);
+ }
+
+ return true;
+}
+
+void Vocabulary::freeAltInputs() {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 0);
+ if (resource)
+ _resMan->unlockResource(resource);
+
+ _altInputs.clear();
+}
+
+bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) {
+ if (_altInputs.empty())
+ return false;
+ if (SELECTOR(parseLang) == -1)
+ return false;
+ if (readSelectorValue(g_sci->getEngineState()->_segMan, g_sci->getGameObject(), SELECTOR(parseLang)) == 1)
+ return false;
+
+ bool ret = false;
+ unsigned int loopCount = 0;
+ bool changed;
+ do {
+ changed = false;
+
+ const char* t = text.c_str();
+ unsigned int tlen = text.size();
+
+ for (unsigned int p = 0; p < tlen && !changed; ++p) {
+ unsigned char s = t[p];
+ if (s >= _altInputs.size() || _altInputs[s].empty())
+ continue;
+ Common::List<AltInput>::iterator i;
+ for (i = _altInputs[s].begin(); i != _altInputs[s].end(); ++i) {
+ if (p + i->_inputLength > tlen)
+ continue;
+ if (i->_prefix && cursorPos > p && cursorPos <= p + i->_inputLength)
+ continue;
+ if (strncmp(i->_input, t+p, i->_inputLength) == 0) {
+ // replace
+ if (cursorPos > p + i->_inputLength) {
+ cursorPos += strlen(i->_replacement) - i->_inputLength;
+ } else if (cursorPos > p) {
+ cursorPos = p + strlen(i->_replacement);
+ }
+
+ for (unsigned int j = 0; j < i->_inputLength; ++j)
+ text.deleteChar(p);
+ const char *r = i->_replacement;
+ while (*r)
+ text.insertChar(*r++, p++);
+
+ assert(cursorPos <= text.size());
+
+ changed = true;
+ ret = true;
+ break;
+ }
+ }
+ }
+ } while (changed && loopCount < 10);
+
+ return ret;
+}
+
// we assume that *word points to an already lowercased word
void Vocabulary::lookupWord(ResultWordList& retval, const char *word, int word_len) {
retval.clear();
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index 1b37219bac..620d50c09d 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -49,7 +49,9 @@ enum {
VOCAB_RESOURCE_SCI1_MAIN_VOCAB = 900,
VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES = 901,
- VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902
+ VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902,
+
+ VOCAB_RESOURCE_ALT_INPUTS = 913
};
@@ -147,6 +149,15 @@ struct synonym_t {
typedef Common::List<synonym_t> SynonymList;
+
+struct AltInput {
+ const char *_input;
+ const char *_replacement;
+ unsigned int _inputLength;
+ bool _prefix;
+};
+
+
struct parse_tree_branch_t {
int id;
int data[10];
@@ -273,6 +284,14 @@ public:
int parseNodes(int *i, int *pos, int type, int nr, int argc, const char **argv);
+ /**
+ * Check text input against alternative inputs.
+ * @param text The text to process. It will be modified in-place
+ * @param cursorPos The cursor position
+ * @return true if anything changed
+ */
+ bool checkAltInput(Common::String& text, uint16& cursorPos);
+
private:
/**
* Loads all words from the main vocabulary.
@@ -305,6 +324,20 @@ private:
*/
void freeRuleList(ParseRuleList *rule_list);
+
+ /**
+ * Retrieves all alternative input combinations from vocab 913.
+ * @return true on success, false on error
+ */
+ bool loadAltInputs();
+
+ /**
+ * Frees all alternative input combinations.
+ */
+ void freeAltInputs();
+
+
+
ResourceManager *_resMan;
VocabularyVersions _vocabVersion;
@@ -319,6 +352,7 @@ private:
Common::Array<parse_tree_branch_t> _parserBranches;
WordMap _parserWords;
SynonymList _synonyms; /**< The list of synonyms */
+ Common::Array<Common::List<AltInput> > _altInputs;
public:
// Accessed by said()