From 11d9f1ec54351bcaf8eed8a55a96a474370808d9 Mon Sep 17 00:00:00 2001 From: Willem Jan Palenstijn Date: Sun, 3 Oct 2010 20:58:50 +0000 Subject: 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 --- engines/sci/graphics/controls.cpp | 20 +++++++- engines/sci/parser/vocabulary.cpp | 101 ++++++++++++++++++++++++++++++++++++++ engines/sci/parser/vocabulary.h | 36 +++++++++++++- 3 files changed, 154 insertions(+), 3 deletions(-) (limited to 'engines/sci') 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::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 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 _parserBranches; WordMap _parserWords; SynonymList _synonyms; /**< The list of synonyms */ + Common::Array > _altInputs; public: // Accessed by said() -- cgit v1.2.3