commit 20dec023d080f7ae9f7af245b85e00949c928bb5
Author: Willem Jan Palenstijn <wjp@usecode.org>
Date:   Sun Oct 3 16:50:16 2010 +0200

    SCI: Support alternative inputs from vocab 913

diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index 70ff70d..7a107f5 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -230,17 +230,53 @@ 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();
+
+			// Check alternative input: for example, in German games,
+			// entering o" as two separate characters will produce an o-umlaut.
+			const AltInput *altInput = g_sci->getVocabulary()->checkAltInput(textPtr, eventKey, cursorPos);
+
+			// We check if we are really able to add the new char
+			uint16 textWidth = 0;
+
+			// Compute textwidth of text before cursor, excluding any
+			// characters we would remove for the altInput.
+			unsigned int deleteChars = 0;
+			if (altInput)
+				deleteChars = altInput->_inputLength-1;
+			for (unsigned int p = 0; p < cursorPos - deleteChars; ++p) {
+				textWidth += _text16->_font->getCharWidth((byte)textPtr[p]);
+			}
+			textPtr += cursorPos;
+			// Add textwidth of text after cursor
 			while (*textPtr)
 				textWidth += _text16->_font->getCharWidth((byte)*textPtr++);
-			textWidth += _text16->_font->getCharWidth(eventKey);
+			// Add textwidth of new text
+			if (!altInput) {
+				textWidth += _text16->_font->getCharWidth(eventKey);
+			} else {
+				const char *p = altInput->_replacement;
+				while (*p)
+					textWidth += _text16->_font->getCharWidth((byte)*p++);
+			}
+
+			// Does it fit?
 			if (textWidth >= rect.width()) {
 				_text16->SetFont(oldFontId);
 				return;
 			}
-			text.insertChar(eventKey, cursorPos++);
+
+			// Now actually insert key or replacement text
+			if (!altInput) {
+				text.insertChar(eventKey, cursorPos++);
+			} else {
+				for (unsigned int i = 0; i < deleteChars; ++i)
+					text.deleteChar(--cursorPos);
+				const char *p = altInput->_replacement;
+				while (*p)
+					text.insertChar(*p++, cursorPos++);
+			}
 		}
 		texteditCursorErase();
 		_paint16->eraseRect(rect);
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index a6de611..840ef5b 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,63 @@ 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;
+
+	_altInputs.clear();
+	_altInputs.resize(256);
+
+	while (*data) {
+		AltInput t;
+		t._input = data;
+
+		unsigned int l = strlen(data);
+		t._inputLength = l;
+		unsigned char lastChar = t._input[l-1];
+		data += l + 1;
+
+		t._replacement = data;
+		l = strlen(data);
+		data += l + 1;
+
+		_altInputs[lastChar].push_back(t);
+	}
+
+	return true;
+}
+
+void Vocabulary::freeAltInputs() {
+	Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 0);
+	if (resource)
+		_resMan->unlockResource(resource);
+
+	_altInputs.clear();
+}
+
+const AltInput* Vocabulary::checkAltInput(const char* text, unsigned char input, unsigned int pos) {
+	if (_altInputs.empty())
+		return 0;
+	if (SELECTOR(parseLang) == -1)
+		return 0;
+	if (readSelectorValue(g_sci->getEngineState()->_segMan, g_sci->getGameObject(), SELECTOR(parseLang)) == 1)
+		return 0;
+	if (_altInputs.size() <= input || _altInputs[input].empty())
+		return 0;
+
+	Common::List<AltInput>::const_iterator i;
+	for (i = _altInputs[input].begin(); i != _altInputs[input].end(); ++i) {
+		if (pos+1 >= i->_inputLength && (i->_inputLength == 1 || strncmp(text + pos - i->_inputLength + 1, i->_input, i->_inputLength-1) == 0))
+			return &(*i);
+	}
+
+	return 0;
+}
+
 // 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 1b37219..153e511 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;
+};
+
+
+
 struct parse_tree_branch_t {
 	int id;
 	int data[10];
@@ -273,6 +284,12 @@ public:
 
 	int parseNodes(int *i, int *pos, int type, int nr, int argc, const char **argv);
 
+	/**
+	 * Check text input against alternative inputs.
+	 * This function takes the currently selected input language into account.
+	 */
+	const AltInput* checkAltInput(const char *text, unsigned char input, unsigned int pos);
+
 private:
 	/**
 	 * Loads all words from the main vocabulary.
@@ -305,6 +322,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 +350,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()
