[Scummvm-git-logs] scummvm master -> 9f10fbcfc1e70f733b04b26e5812514fd02fea80

bluegr bluegr at gmail.com
Mon May 25 00:22:04 UTC 2020


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
6ab0cb78e0 SCI: Improve vm_hooks ; Support Hebrew SQ3
9f10fbcfc1 SCI: Hebrew SQ3 - fixed rejects


Commit: 6ab0cb78e0c14da5bdc8ae2df43155304e15def8
    https://github.com/scummvm/scummvm/commit/6ab0cb78e0c14da5bdc8ae2df43155304e15def8
Author: Zvika Haramaty (haramaty.zvika at gmail.com)
Date: 2020-05-25T03:21:59+03:00

Commit Message:
SCI: Improve vm_hooks ; Support Hebrew SQ3

- vm_hooks: fully support 'call*'
- make required changes to support Hebrew SQ3 (which is still a WIP
project)

Changed paths:
    engines/sci/detection_tables.h
    engines/sci/engine/script_patches.cpp
    engines/sci/engine/vm.cpp
    engines/sci/engine/vm_hooks.cpp
    engines/sci/engine/vm_hooks.h
    engines/sci/event.cpp
    engines/sci/graphics/controls16.cpp
    engines/sci/graphics/menu.cpp
    engines/sci/graphics/text16.cpp
    engines/sci/parser/vocabulary.cpp
    engines/sci/parser/vocabulary.h


diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 3388ecb970..2b333d47bc 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -4785,6 +4785,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
 		AD_LISTEND},
 		Common::EN_ANY, Common::kPlatformDOS, 0, GUIO_STD16_UNDITHER	},
 
+	// Space Quest 3 - Hebrew DOS (from the Space Quest Collection)
+	// Executable scanning reports "0.000.685", VERSION file reports "1.018"
+	// This translation is still a work in progress
+	{ "sq3", "", {
+		{"resource.map", 0, "55e91aeef1705bce2a9b79172682f36d", 5730},
+		{"resource.001", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 490247},
+		{"resource.002", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 715777},
+		{"resource.003", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 703370},
+		{"PATCHES/font.000", 0, "6fab182f1c071d1ed47be27776964baf", 3334},
+		AD_LISTEND},
+		Common::HE_ISR, Common::kPlatformDOS, 0, GUIO_STD16_UNDITHER },
+
+
 	// Space Quest 3 - English DOS 6 x 360k Floppy (from misterhands, bug report Trac #10677 and goodoldgeorge, bug report Trac #10636)
 	// Executable scanning reports "0.000.685", VERSION file reports "1.018"
 	{"sq3", "", {
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 302c0a4634..2ab4e7cade 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -15842,6 +15842,42 @@ static const SciScriptPatcherEntry qfg4Signatures[] = {
 
 #endif
 
+// Space Quest 3 has some strings hard coded in the scripts file
+// We need to patch them for the Hebrew translation
+
+// Replace "Enter input" prompt with Hebrew
+static const uint16 sq3HebrewEnterInputSignature[] = {
+	SIG_MAGICDWORD,
+	0x45, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0,
+	SIG_END
+};
+
+static const uint16 sq3HebrewEnterInputPatch[] = {
+	0xe4, 0xf7, 0xf9, 0x20, 0xf4, 0xf7, 0xe5, 0xe3, 0xe4, 0x3a, 0,
+	PATCH_END
+};
+
+// Replace "Space Quest ]I[" in status bar with Hebrew
+static const uint16 sq3HebrewStatusBarNameSignature[] = {
+	SIG_MAGICDWORD,
+	0x53, 0x70, 0x61, 0x63, 0x65, 0x20, 0x51, 0x75, 0x65, 0x73, 0x74, 0x20, 0x0b,		// "Space Quest " + special ]I[ char
+	SIG_END
+};
+
+static const uint16 sq3HebrewStatusBarNamePatch[] = {
+	0xee, 0xf1, 0xf2, 0x20, 0xe1, 0xe7, 0xec, 0xec, 0x20, 0x0b, 0x20, 0x20, 0x20,		// 'Space Quest' in Hebrew: 'Masa Bahalal ' + special ]I[ char
+	PATCH_END
+};
+
+//          script, description,                                      signature                                      patch
+static const SciScriptPatcherEntry sq3Signatures[] = {
+	{  false,   0, "Hebrew: Replace name in status bar",    1, sq3HebrewStatusBarNameSignature,                     sq3HebrewStatusBarNamePatch },
+	{  false, 996, "Hebrew: Replace 'Enter input' prompt",  1, sq3HebrewEnterInputSignature,                        sq3HebrewEnterInputPatch },
+	SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+
+
 // ===========================================================================
 //  script 298 of sq4/floppy has an issue. object "nest" uses another property
 //   which isn't included in property count. We return 0 in that case, so that
@@ -19140,6 +19176,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
 	case GID_SQ1:
 		signatureTable = sq1vgaSignatures;
 		break;
+	case GID_SQ3:
+		signatureTable = sq3Signatures;
+		break;
 	case GID_SQ4:
 		signatureTable = sq4Signatures;
 		break;
@@ -19216,6 +19255,12 @@ void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
 					enablePatch(signatureTable, "Floppy: fix peer bats, upper door (2/2)");
 				}
 				break;
+			case GID_SQ3:
+				if (g_sci->getLanguage() == Common::HE_ISR) {
+					enablePatch(signatureTable, "Hebrew: Replace name in status bar");
+					enablePatch(signatureTable, "Hebrew: Replace 'Enter input' prompt");
+				}
+				break;
 			case GID_SQ4:
 				// Enable the dress-purchase flag fixes for English Amiga only.
 				//  One of these patches is applied to scripts that are the same as those
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index b7259d6140..99ba2ae177 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -664,7 +664,7 @@ void run_vm(EngineState *s) {
 
 		// Get opcode
 		byte extOpcode;
-		if (!vmHooks.isActive())
+		if (!vmHooks.isActive(s))
 			s->xs->addr.pc.incOffset(readPMachineInstruction(scr->getBuf(s->xs->addr.pc.getOffset()), extOpcode, opparams));
 		else {
 			int offset = readPMachineInstruction(vmHooks.data(), extOpcode, opparams);
@@ -804,7 +804,7 @@ void run_vm(EngineState *s) {
 
 		case op_bt: // 0x17 (23)
 			// Branch relative if true
-			if (!vmHooks.isActive()) {
+			if (!vmHooks.isActive(s)) {
 				if (s->r_acc.getOffset() || s->r_acc.getSegment())
 					s->xs->addr.pc.incOffset(opparams[0]);
 
@@ -819,7 +819,7 @@ void run_vm(EngineState *s) {
 
 		case op_bnt: // 0x18 (24)
 			// Branch relative if not true
-			if (!vmHooks.isActive()) {
+			if (!vmHooks.isActive(s)) {
 				if (!(s->r_acc.getOffset() || s->r_acc.getSegment()))
 					s->xs->addr.pc.incOffset(opparams[0]);
 
@@ -833,7 +833,7 @@ void run_vm(EngineState *s) {
 			break;
 
 		case op_jmp: // 0x19 (25)
-			if (!vmHooks.isActive()) {
+			if (!vmHooks.isActive(s)) {
 				s->xs->addr.pc.incOffset(opparams[0]);
 
 				if (s->xs->addr.pc.getOffset() >= local_script->getScriptSize())
diff --git a/engines/sci/engine/vm_hooks.cpp b/engines/sci/engine/vm_hooks.cpp
index c4e2b46524..63608f4553 100644
--- a/engines/sci/engine/vm_hooks.cpp
+++ b/engines/sci/engine/vm_hooks.cpp
@@ -22,6 +22,7 @@
 
 #include "common/hashmap.h"
 #include "common/array.h"
+#include "common/language.h"
 #include "sci/engine/vm_hooks.h"
 #include "sci/engine/vm.h"
 #include "sci/engine/state.h"
@@ -35,7 +36,8 @@ namespace Sci {
  * This mechanism allows inserting new instructions, and not only replace existing code.
  * Note that when using hooks, the regular PC is frozen, and doesn't advance.
  * Therefore, 'jmp', 'bt' and 'bnt' are used to locally move around inside the patch.
- * call* opcodes can be used - but they should be last executed opcode, in order to successfully transfer control.
+ *
+ * (In the past there was a warning regarding 'call's - but they should work now)
  *
  ******************************************************************************************************************/
 
@@ -68,6 +70,60 @@ static const byte qfg1_die_after_running_on_ice[] = {
 	0x47, 0x00, 0x01, 0x10         // calle proc0_1
 };
 
+
+// SCI0 Hebrew translations need to modify and relocate the "Enter input:" prompt
+// currently SQ3 is the only Hebrew SCI0 game, but this patch (or similar) should work for the future games as well
+
+static const byte sci0_hebrew_input_prompt[] = {
+	// in (procedure (Print ...
+	// ...
+	// 			(#edit
+	// 				(++ paramCnt)
+	// adding:
+	//				(hDText moveTo: 104 4)
+	//				(= hDText (DText new:))
+	//				(hDText
+	//					text: ''
+	//					moveTo: 4 4
+	//					font: gDefaultFont
+	//					setSize:
+	//				)
+	//				(hDialog add: hDText)
+	0x38, 0x92, 0,			// pushi    #moveTo
+	0x39, 2,				// pushi    2
+	0x38, 210, 0,			// pushi    220		;x
+	0x39, 4, 				// pushi    4		;y
+	0x85, 1,				// lat      temp1
+	0x4a, 8,				// send     8
+	0x38, 0x59, 0,			// pushi    #new
+	0x39, 0, 				// pushi    0
+	0x51, 0xd,				// class    DText
+	0x4a, 4,				// send     4
+	0xa5, 1,				// sat      temp1
+	0x39, 0x1a,				// pushi    #text
+	0x39, 1,				// pushi    1
+	0x75, 25,				// lofss    ''	; that location has '\0'
+	0x38, 146, 0,			// pushi    146
+	0x39, 2,				// pushi    2
+	0x39, 4,				// pushi    4		;x
+	0x39, 14,				// pushi    12		;y
+	0x39, 33,				// pushi    33
+	0x39, 1,				// pushi    1
+	0x89, 0x16,				// lsg      global22
+	0x38, 144, 0,			// pushi    144
+	0x39, 0,				// pushi    0
+	0x85, 1,				// lat      temp1
+	0x4a, 24,				// send     24
+	0x38, 0x64, 0,			// pushi    #add
+	0x39, 1, 				// pushi    1
+	0x8d, 1,				// lst      temp1
+	0x85, 0,				// lat      temp0
+	0x4a, 6,				// send     6
+	0x85, 5					// lat      temp5
+};
+
+
+
 /** Write here all games hooks
  *  From this we'll build _hooksMap, which contains only relevant hooks to current game
  *  The match is performed according to PC, script number, opcode (only opcode name, as seen in ScummVM debugger),
@@ -76,9 +132,11 @@ static const byte qfg1_die_after_running_on_ice[] = {
  *  - external function ID  (and then selector is "")
  *		= in that case, if objName == "" it will be ignored, otherwise, it will be also used to match
  */
+
 static const GeneralHookEntry allGamesHooks[] = {
-	// GID, script, PC.offset, objName,  selector, externID, opcode,  hook array
-	{GID_QFG1, {58, 0x144d}, {"egoRuns", "changeState", -1 , "push0", HOOKARRAY(qfg1_die_after_running_on_ice)}}
+	// GID, script, lang,        PC.offset, objName,  selector,  externID, opcode,  hook array
+	{GID_QFG1, Common::UNK_LANG, {58,  0x144d}, {"egoRuns", "changeState", -1 , "push0", HOOKARRAY(qfg1_die_after_running_on_ice)}},
+	{GID_SQ3,  Common::HE_ISR,   {255, 0x1103}, {"User",    "",            -1 , "pushi", HOOKARRAY(sci0_hebrew_input_prompt)}}
 };
 
 
@@ -86,10 +144,12 @@ VmHooks::VmHooks() {
 	// build _hooksMap
 	for (uint i = 0; i < ARRAYSIZE(allGamesHooks); i++) {
 		if (allGamesHooks[i].gameId == g_sci->getGameId())
-			_hooksMap.setVal(allGamesHooks[i].key, allGamesHooks[i].entry);
+			if (allGamesHooks[i].language == g_sci->getLanguage() || allGamesHooks[i].language == Common::UNK_LANG)
+				_hooksMap.setVal(allGamesHooks[i].key, allGamesHooks[i].entry);
 	}
 
 	_lastPc = NULL_REG;
+	_just_finished = false;
 	_location = 0;
 }
 
@@ -120,12 +180,16 @@ bool hook_exec_match(Sci::EngineState *s, HookEntry entry) {
 		s->xs->debugExportId == entry.exportId && strcmp(entry.opcodeName, opcodeNames[opcode]) == 0;
 }
 
-
 void VmHooks::vm_hook_before_exec(Sci::EngineState *s) {
+	if (_just_finished) {
+		_just_finished = false;
+		_lastPc = NULL_REG;
+		return;
+	}
 	Script *scr = s->_segMan->getScript(s->xs->addr.pc.getSegment());
 	int scriptNumber = scr->getScriptNumber();
 	HookHashKey key = { scriptNumber, s->xs->addr.pc.getOffset() };
-	if (_lastPc != s->xs->addr.pc && _hooksMap.contains(key)) {
+	if (_hookScriptData.empty() && _lastPc != s->xs->addr.pc && _hooksMap.contains(key)) {
 		_lastPc = s->xs->addr.pc;
 		HookEntry entry = _hooksMap[key];
 		if (hook_exec_match(s, entry)) {
@@ -143,8 +207,9 @@ byte *VmHooks::data() {
 	return _hookScriptData.data() + _location;
 }
 
-bool VmHooks::isActive() {
-	return !_hookScriptData.empty();
+bool VmHooks::isActive(Sci::EngineState *s) {
+	// if PC has changed, then we're temporary inactive - went to some other call, or send, etc.
+	return !_hookScriptData.empty() && _lastPc == s->xs->addr.pc;
 }
 
 void VmHooks::advance(int offset) {
@@ -155,6 +220,7 @@ void VmHooks::advance(int offset) {
 		error("VmHooks: requested to change offset after end of patch");
 	else if ((uint)newLocation == _hookScriptData.size()) {
 		_hookScriptData.clear();
+		_just_finished = true;
 		_location = 0;
 	} else
 		_location = newLocation;
diff --git a/engines/sci/engine/vm_hooks.h b/engines/sci/engine/vm_hooks.h
index 5c5e699d53..274a0d8816 100644
--- a/engines/sci/engine/vm_hooks.h
+++ b/engines/sci/engine/vm_hooks.h
@@ -61,6 +61,7 @@ struct HookEntry {
 /** Used for allGamesHooks - from it we build the specific _hooksMap */
 struct GeneralHookEntry {
 	SciGameId gameId;
+	Common::Language language;			// language to be patched. UNK_LANG means to patch all languages
 	HookHashKey key;
 	HookEntry entry;
 };
@@ -80,7 +81,7 @@ public:
 
 	byte *data();
 
-	bool isActive();
+	bool isActive(Sci::EngineState *s);
 
 	void advance(int offset);
 
@@ -90,9 +91,12 @@ private:
 
 	Common::Array<byte> _hookScriptData;
 
-	/** Used to avoid double patching in row */
+	/** Used to avoid double patching in row, and to support `call`ing */
 	reg_t _lastPc;
 
+	/** Used to avoid double patching in row */
+	bool _just_finished;
+
 	/** Location inside patch */
 	int _location;
 };
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 65f563ea6f..cfbfdb3585 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -375,6 +375,10 @@ SciEvent EventManager::getScummVMEvent() {
 					input.character = 0x80 + i;
 					break;
 				}
+		} else if (g_sci->getLanguage() == Common::HE_ISR) {
+			if (input.character >= 0x05d0 && input.character <= 0x05ea)
+				// convert to WIN-1255
+				input.character = input.character - 0x05d0 + 0xe0;
 		}
 	}
 
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index 08906c03a0..ce50d59ba1 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -113,10 +113,16 @@ void GfxControls16::texteditCursorDraw(Common::Rect rect, const char *text, uint
 		for (i = 0; i < curPos; i++) {
 			textWidth += _text16->_font->getCharWidth((unsigned char)text[i]);
 		}
-		_texteditCursorRect.left = rect.left + textWidth;
+		if (!g_sci->isLanguageRTL())
+			_texteditCursorRect.left = rect.left + textWidth;
+		else
+			_texteditCursorRect.right = rect.right - textWidth;
 		_texteditCursorRect.top = rect.top;
 		_texteditCursorRect.bottom = _texteditCursorRect.top + _text16->_font->getHeight();
-		_texteditCursorRect.right = _texteditCursorRect.left + (text[curPos] == 0 ? 1 : _text16->_font->getCharWidth((unsigned char)text[curPos]));
+		if (!g_sci->isLanguageRTL())
+			_texteditCursorRect.right = _texteditCursorRect.left + (text[curPos] == 0 ? 1 : _text16->_font->getCharWidth((unsigned char)text[curPos]));
+		else
+			_texteditCursorRect.left = _texteditCursorRect.right - (text[curPos] == 0 ? 1 : _text16->_font->getCharWidth((unsigned char)text[curPos]));
 		_paint16->invertRect(_texteditCursorRect);
 		_paint16->bitsShow(_texteditCursorRect);
 		_texteditCursorVisible = true;
@@ -184,13 +190,25 @@ void GfxControls16::kernelTexteditChange(reg_t controlObject, reg_t eventObject)
 				cursorPos = textSize; textChanged = true;
 				break;
 			case kSciKeyLeft:
-				if (cursorPos > 0) {
-					cursorPos--; textChanged = true;
+				if (!g_sci->isLanguageRTL()) {
+					if (cursorPos > 0) {
+						cursorPos--; textChanged = true;
+					}
+				} else {
+					if (cursorPos + 1 <= textSize) {
+						cursorPos++; textChanged = true;
+					}
 				}
 				break;
 			case kSciKeyRight:
-				if (cursorPos + 1 <= textSize) {
-					cursorPos++; textChanged = true;
+				if (!g_sci->isLanguageRTL()) {
+					if (cursorPos + 1 <= textSize) {
+						cursorPos++; textChanged = true;
+					}
+				} else {
+					if (cursorPos > 0) {
+						cursorPos--; textChanged = true;
+					}
 				}
 				break;
 			case kSciKeyEtx:
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index b1ebbc71e5..d85f87e790 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -355,12 +355,24 @@ void GfxMenu::drawBar() {
 	_paint16->fillRect(_ports->_menuBarRect, 1, _screen->getColorWhite());
 	_paint16->fillRect(_ports->_menuLine, 1, 0);
 	_ports->penColor(0);
-	_ports->moveTo(8, 1);
+	if (!g_sci->isLanguageRTL())
+		_ports->moveTo(8, 1);
+	else
+		_ports->moveTo(_screen->getWidth() - 8, 1);
 
 	listIterator = _list.begin();
 	while (listIterator != listEnd) {
 		listEntry = *listIterator;
+		int16 textWidth;
+		int16 textHeight;
+		if (g_sci->isLanguageRTL()) {
+			_text16->StringWidth(listEntry->textSplit.c_str(), _text16->GetFontId(), textWidth, textHeight);
+			_ports->_curPort->curLeft -= textWidth;
+		}
+		int16 origCurLeft = _ports->_curPort->curLeft;
 		_text16->DrawString(listEntry->textSplit.c_str());
+		if (g_sci->isLanguageRTL())
+			_ports->_curPort->curLeft = origCurLeft;
 
 		listIterator++;
 	}
@@ -584,19 +596,35 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 	_menuRect.top = _ports->_menuBarRect.bottom;
 	menuTextRect.top = _ports->_menuBarRect.top;
 	menuTextRect.bottom = _ports->_menuBarRect.bottom;
-	menuTextRect.left = menuTextRect.right = 7;
+	if (!g_sci->isLanguageRTL())
+		menuTextRect.left = menuTextRect.right = 7;
+	else
+		menuTextRect.left = menuTextRect.right = _ports->_menuBarRect.right - 7;
 	listIterator = _list.begin();
 	while (listIterator != listEnd) {
 		listEntry = *listIterator;
 		listNr++;
-		menuTextRect.left = menuTextRect.right;
-		menuTextRect.right += listEntry->textWidth;
-		if (listNr == newMenuId)
-			_menuRect.left = menuTextRect.left;
+		if (!g_sci->isLanguageRTL()) {
+			menuTextRect.left = menuTextRect.right;
+			menuTextRect.right += listEntry->textWidth;
+			if (listNr == newMenuId)
+				_menuRect.left = menuTextRect.left;
+		} else {
+			menuTextRect.right = menuTextRect.left;
+			menuTextRect.left -= listEntry->textWidth;
+			if (listNr == newMenuId)
+				_menuRect.right = menuTextRect.right;
+		}
 		if ((listNr == newMenuId) || (listNr == oldMenuId)) {
-			menuTextRect.translate(1, 0);
-			_paint16->invertRect(menuTextRect);
-			menuTextRect.translate(-1, 0);
+			if (!g_sci->isLanguageRTL()) {
+				menuTextRect.translate(1, 0);
+				_paint16->invertRect(menuTextRect);
+				menuTextRect.translate(-1, 0);
+			} else {
+				menuTextRect.translate(-1, 0);
+				_paint16->invertRect(menuTextRect);
+				menuTextRect.translate(1, 0);
+			}
 		}
 
 		listIterator++;
@@ -614,16 +642,26 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 		}
 		listItemIterator++;
 	}
-	_menuRect.right = _menuRect.left + 16 + 4 + 2;
-	_menuRect.right += maxTextWidth + maxTextRightAlignedWidth;
-	if (!maxTextRightAlignedWidth)
-		_menuRect.right -= 5;
+	if (!g_sci->isLanguageRTL()) {
+		_menuRect.right = _menuRect.left + 16 + 4 + 2;
+		_menuRect.right += maxTextWidth + maxTextRightAlignedWidth;
+		if (!maxTextRightAlignedWidth)
+			_menuRect.right -= 5;
+	} else {
+		_menuRect.left = _menuRect.right - (16 + 4 + 2);
+		_menuRect.left -= (maxTextWidth + maxTextRightAlignedWidth);
+		if (!maxTextRightAlignedWidth)
+			_menuRect.left += 5;
+	}
 
 	// If part of menu window is outside the screen, move it into the screen
 	// (this happens in multilingual sq3 and lsl3).
 	if (_menuRect.right > _screen->getWidth()) {
 		_menuRect.translate(-(_menuRect.right - _screen->getWidth()), 0);
 	}
+	if (_menuRect.left < 0) {
+		warning("GfxMenu::drawMenu: _menuRect.left < 0");
+	}
 
 	// Save background
 	_menuSaveHandle = _paint16->bitsSave(_menuRect, GFX_SCREEN_MASK_VISUAL);
@@ -633,7 +671,10 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 	_menuRect.left++; _menuRect.right--; _menuRect.bottom--;
 	_paint16->fillRect(_menuRect, GFX_SCREEN_MASK_VISUAL, _screen->getColorWhite());
 
-	_menuRect.left += 8;
+	if (!g_sci->isLanguageRTL())
+		_menuRect.left += 8;
+	else
+		_menuRect.right -= 8;
 	topPos = _menuRect.top + 1;
 	listItemIterator = _itemList.begin();
 	while (listItemIterator != listItemEnd) {
@@ -641,10 +682,17 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 		if (listItemEntry->menuId == newMenuId) {
 			if (!listItemEntry->separatorLine) {
 				_ports->textGreyedOutput(!listItemEntry->enabled);
-				_ports->moveTo(_menuRect.left, topPos);
-				_text16->DrawString(listItemEntry->textSplit.c_str());
-				_ports->moveTo(_menuRect.right - listItemEntry->textRightAlignedWidth - 5, topPos);
-				_text16->DrawString(listItemEntry->textRightAligned.c_str());
+				if (!g_sci->isLanguageRTL()) {
+					_ports->moveTo(_menuRect.left, topPos);
+					_text16->DrawString(listItemEntry->textSplit.c_str());
+					_ports->moveTo(_menuRect.right - listItemEntry->textRightAlignedWidth - 5, topPos);
+					_text16->DrawString(listItemEntry->textRightAligned.c_str());
+				} else {
+					_ports->moveTo(_menuRect.left + 5, topPos);
+					_text16->DrawString(listItemEntry->textRightAligned.c_str());
+					_ports->moveTo(_menuRect.right - listItemEntry->textWidth, topPos);
+					_text16->DrawString(listItemEntry->textSplit.c_str());
+				}
 			} else {
 				// We dont 100% follow sierra here, we draw the line from left to right. Looks better
 				// BTW. SCI1.1 seems to put 2 pixels and then skip one, we don't do this at all (lsl6)
@@ -664,8 +712,16 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 	// Draw the black line again
 	_paint16->fillRect(_ports->_menuLine, 1, 0);
 
-	_menuRect.left -= 8;
-	_menuRect.left--; _menuRect.right++; _menuRect.bottom++;
+	if (!g_sci->isLanguageRTL()) {
+		_menuRect.left -= 8;
+		_menuRect.left--;
+		_menuRect.right++;
+	} else {
+		_menuRect.right += 8;
+		_menuRect.right--;
+		_menuRect.left++;
+	}
+	_menuRect.bottom++;
 	_paint16->bitsShow(_menuRect);
 }
 
@@ -701,15 +757,26 @@ uint16 GfxMenu::mouseFindMenuSelection(Common::Point mousePosition) {
 	GuiMenuEntry *listEntry;
 	GuiMenuList::iterator listIterator;
 	GuiMenuList::iterator listEnd = _list.end();
-	uint16 curXstart = 8;
+	uint16 curXstart;
+	if (!g_sci->isLanguageRTL())
+		curXstart = 8;
+	else
+		curXstart = _screen->getWidth() - 8;
 
 	listIterator = _list.begin();
 	while (listIterator != listEnd) {
 		listEntry = *listIterator;
-		if (mousePosition.x >= curXstart && mousePosition.x < curXstart + listEntry->textWidth) {
-			return listEntry->id;
+		if (!g_sci->isLanguageRTL()) {
+			if (mousePosition.x >= curXstart && mousePosition.x < curXstart + listEntry->textWidth) {
+				return listEntry->id;
+			}
+			curXstart += listEntry->textWidth;
+		} else {
+			if (mousePosition.x <= curXstart && mousePosition.x > curXstart - listEntry->textWidth) {
+				return listEntry->id;
+			}
+			curXstart -= listEntry->textWidth;
 		}
-		curXstart += listEntry->textWidth;
 		listIterator++;
 	}
 	return 0;
@@ -787,10 +854,22 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
 					}
 					break;
 				case kSciKeyLeft:
-					newMenuId--; newItemId = 1;
+					if (!g_sci->isLanguageRTL()) {
+						newMenuId--;
+						newItemId = 1;
+					} else {
+						newMenuId++;
+						newItemId = 1;
+					}
 					break;
 				case kSciKeyRight:
-					newMenuId++; newItemId = 1;
+					if (!g_sci->isLanguageRTL()) {
+						newMenuId++;
+						newItemId = 1;
+					} else {
+						newMenuId--;
+						newItemId = 1;
+					}
 					break;
 				case kSciKeyUp:
 					newItemId--;
@@ -952,7 +1031,14 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack
 
 	_paint16->fillRect(_ports->_menuBarRect, 1, colorBack);
 	_ports->penColor(colorPen);
-	_ports->moveTo(0, 1);
+	if (!g_sci->isLanguageRTL()) {
+		_ports->moveTo(0, 1);
+	} else {
+		int16 textWidth;
+		int16 textHeight;
+		_text16->StringWidth(text, _text16->GetFontId(), textWidth, textHeight);
+		_ports->moveTo(_screen->getWidth() - textWidth, 1);
+	}
 	_text16->DrawStatus(text);
 	_paint16->bitsShow(_ports->_menuBarRect);
 	// Also draw the line under the status bar. Normally, this is never drawn,
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 8aba5587f2..eccbf49806 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -22,6 +22,7 @@
 
 #include "common/util.h"
 #include "common/stack.h"
+#include "common/unicode-bidi.h"
 #include "graphics/primitives.h"
 
 #include "sci/sci.h"
@@ -559,22 +560,56 @@ void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const
 			break;
 		Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true);
 		maxTextWidth = MAX<int16>(maxTextWidth, textWidth);
-		switch (alignment) {
-		case SCI_TEXT16_ALIGNMENT_RIGHT:
-			offset = rect.width() - textWidth;
-			break;
-		case SCI_TEXT16_ALIGNMENT_CENTER:
-			offset = (rect.width() - textWidth) / 2;
-			break;
-		case SCI_TEXT16_ALIGNMENT_LEFT:
-			offset = 0;
-			break;
+		if (!g_sci->isLanguageRTL()) {
+			switch (alignment) {
+			case SCI_TEXT16_ALIGNMENT_RIGHT:
+				offset = rect.width() - textWidth;
+				break;
+			case SCI_TEXT16_ALIGNMENT_CENTER:
+				offset = (rect.width() - textWidth) / 2;
+				break;
+			case SCI_TEXT16_ALIGNMENT_LEFT:
+				offset = 0;
+				break;
 
-		default:
-			warning("Invalid alignment %d used in TextBox()", alignment);
+			default:
+				warning("Invalid alignment %d used in TextBox()", alignment);
+			}
+		} else {
+			// language direction is from Right to Left
+			switch (alignment) {
+			case SCI_TEXT16_ALIGNMENT_LEFT:
+				offset = rect.width() - textWidth;
+				break;
+			case SCI_TEXT16_ALIGNMENT_CENTER:
+				offset = (rect.width() - textWidth) / 2;
+				break;
+			case SCI_TEXT16_ALIGNMENT_RIGHT:
+				offset = 0;
+				break;
+
+			default:
+				warning("Invalid alignment %d used in TextBox()", alignment);
+			}
+
+			// in the fonts, the characters have some spacing to the left, and no space to the right
+			// therefore, when we start drawing from the right, they "start from the border"
+			// e.g., in SQ3 Hebrew user's input prompt
+			// we can't make the Hebrew letters spacing in the right (in the font), because then mixed English-Hebrew text
+			// might have 2 letters stick together
+			// therefore, we shift here one pixel to the left, for the spacing
+			offset--;
 		}
 		_ports->moveTo(rect.left + offset, rect.top + hline);
 
+		Common::String textString;
+		if (g_sci->isLanguageRTL()) {
+			const char *curTextLineOrig = curTextLine;
+			Common::String textLogical = Common::String(curTextLineOrig, (uint32)charCount);
+			textString = Common::convertBiDiString(textLogical, g_sci->getLanguage());		//TODO: maybe move to Draw()?
+			curTextLine = textString.c_str();
+		}
+
 		if (show) {
 			Show(curTextLine, 0, charCount, fontId, previousPenColor);
 		} else {
@@ -618,8 +653,15 @@ void GfxText16::DrawString(const Common::String &text) {
 
 // we need to have a separate status drawing code
 //  In KQ4 the IV char is actually 0xA, which would otherwise get considered as linebreak and not printed
-void GfxText16::DrawStatus(const Common::String &str) {
+void GfxText16::DrawStatus(const Common::String &strOrig) {
 	uint16 curChar, charWidth;
+
+	Common::String str;
+	if (!g_sci->isLanguageRTL())
+		str = strOrig;
+	else
+		str = Common::convertBiDiString(strOrig, g_sci->getLanguage());
+
 	const byte *text = (const byte *)str.c_str();
 	uint16 textLen = str.size();
 	Common::Rect rect;
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 6699ed25b8..2c8739e272 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -116,7 +116,8 @@ bool Vocabulary::loadParserWords() {
 		}
 		// If all of them were empty, we are definitely seeing SCI01 vocab in disguise (e.g. pq2 japanese)
 		if (alphabetNr == 26) {
-			warning("SCI0: Found SCI01 vocabulary in disguise");
+			if (g_sci->getLanguage() != Common::HE_ISR)
+				warning("SCI0: Found SCI01 vocabulary in disguise");
 			resourceType = kVocabularySCI1;
 		}
 	}
@@ -250,10 +251,38 @@ bool Vocabulary::loadSuffixes() {
 
 		_parserSuffixes.push_back(suffix);
 	}
+	appendSuffixes();
 
 	return true;
 }
 
+void Vocabulary::appendSuffixes() {
+	if (g_sci->getLanguage() == Common::HE_ISR) {
+		for (int i = 0; i < 2; i++) {
+			int cls;
+			if (i == 0)
+				cls = VOCAB_CLASS_PREPOSITION << 4;
+			else
+				cls = VOCAB_CLASS_NOUN << 4;
+
+			suffix_t suffix1 = { cls, cls, 1, 0, "\xea", "" };					// get rid of Kaf Sofit
+			_parserSuffixes.push_back(suffix1);
+
+			suffix_t suffix2 = { cls, cls, 2, 0, "\xe9\xed", "" };				// get rid of Yud, Mem Sofit
+			_parserSuffixes.push_back(suffix2);
+
+			suffix_t suffix3 = { cls, cls, 2, 0, "\xe5\xfa", "" };				// get rid of Vav, Taf
+			_parserSuffixes.push_back(suffix3);
+
+			suffix_t suffix4 = { cls, cls, 3, 2, "\xe9\xe5\xfa", "\xe9\xfa" };	// Yud, Vav, Taf -> Yud, Taf
+			_parserSuffixes.push_back(suffix4);
+
+			suffix_t suffix5 = { cls, cls, 3, 2, "\xe0\xe5\xfa", "\xe0\xe4" };	// Alef, Vav, Taf -> Alef, He
+			_parserSuffixes.push_back(suffix5);
+		}
+	}
+}
+
 void Vocabulary::freeSuffixes() {
 	Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), false);
 	if (resource)
@@ -482,6 +511,42 @@ void Vocabulary::lookupWord(ResultWordList& retval, const char *word, int word_l
 	}
 }
 
+void Vocabulary::lookupWordPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len) {
+	// currently, this is needed only for Hebrew translation
+	if (g_sci->getLanguage() != Common::HE_ISR)
+		return;
+
+	if (--word_len <= 0)
+		return;
+
+	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xe1, "1hebrew1prefix1bet"))			// "Bet"
+		return;
+	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xe4, "the"))							// "He Hayedia"
+		return;
+	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xec, "1hebrew1prefix1lamed"))			// "Lamed"
+		return;
+	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xee, "1hebrew1prefix1mem"))			// "Mem"
+		return;
+}
+
+bool Vocabulary::lookupSpecificPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len, unsigned char prefix, const char *meaning) {
+	if (!_parserWords.contains(meaning)) {
+		warning("Vocabulary::lookupSpecificPrefix: _parserWords doesn't contains '%s'", meaning);
+		return false;
+	}
+	if ((unsigned char)word[0] == prefix) {
+		ResultWordList word_list;
+		lookupWord(word_list, word + 1, word_len);
+		if (!word_list.empty())
+			if (word_list.front()._class == VOCAB_CLASS_NOUN << 4 || word_list.front()._class == VOCAB_CLASS_PREPOSITION << 4) {
+				parent_retval.push_back(_parserWords[meaning]);
+				retval = word_list;
+				return true;
+			}
+	}
+	return false;
+}
+
 void Vocabulary::debugDecipherSaidBlock(const SciSpan<const byte> &data) {
 	bool first = true;
 	uint16 nextItem;
@@ -585,10 +650,14 @@ bool Vocabulary::tokenizeString(ResultWordListList &retval, const char *sentence
 				lookupWord(lookup_result, currentWord, wordLen);
 
 				if (lookup_result.empty()) { // Not found?
-					*error = (char *)calloc(wordLen + 1, 1);
-					strncpy(*error, currentWord, wordLen); // Set the offending word
-					retval.clear();
-					return false; // And return with error
+					lookupWordPrefix(retval, lookup_result, currentWord, wordLen);
+
+					if (lookup_result.empty()) { // Still not found? {
+						*error = (char *)calloc(wordLen + 1, 1);
+						strncpy(*error, currentWord, wordLen); // Set the offending word
+						retval.clear();
+						return false; // And return with error
+					}
 				}
 
 				// Copy into list
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index 763c1fd189..bdfbe5bdca 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -208,6 +208,29 @@ public:
 	 */
 	void lookupWord(ResultWordList &retval, const char *word, int word_len);
 
+	/**
+	 * Looks up a single word in the words list, taking into account suffixes, and updating parent_retval if finding matching prefix
+	 * Note: there is no equivalent in Sierra SCI, added to support specific languages translations
+	 * For other languages, does nothing
+	 * @param parent_retval		parent's function list of matches
+	 * @param retval			the list of matches
+	 * @param word				pointer to the word to look up
+	 * @param word_len			length of the word to look up
+	 */
+	void lookupWordPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len);
+
+	/**
+	 * Helper function for lookupWordPrefix, checking specific prefix for match
+	 * @param parent_retval		lookupWordPrefix's parent's function list of matches
+	 * @param retval			lookupWordPrefix's list of matches
+	 * @param word				pointer to the word to look up
+	 * @param word_len			length of the word to look up
+	 * @param prefix			the prefix to look for in the word
+	 * @param meaning			the meaning of that prefix
+	 * @return true on prefix match, false on prefix not matching
+	 */
+	bool lookupSpecificPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len, unsigned char prefix, const char *meaning);
+
 
 	/**
 	 * Tokenizes a string and compiles it into word_ts.
@@ -324,6 +347,11 @@ private:
 	 */
 	bool loadSuffixes();
 
+	/**
+	 * Appends required suffixes for specific languages
+	 */
+	void appendSuffixes();
+
 	/**
 	 * Frees all suffixes in the given list.
 	 * @param suffixes: The suffixes to free


Commit: 9f10fbcfc1e70f733b04b26e5812514fd02fea80
    https://github.com/scummvm/scummvm/commit/9f10fbcfc1e70f733b04b26e5812514fd02fea80
Author: Zvika (haramaty.zvika at gmail.com)
Date: 2020-05-25T03:21:59+03:00

Commit Message:
SCI: Hebrew SQ3 - fixed rejects

Changed paths:
    engines/sci/engine/vm_hooks.cpp
    engines/sci/engine/vm_hooks.h
    engines/sci/graphics/menu.cpp
    engines/sci/graphics/text16.cpp
    engines/sci/parser/vocabulary.cpp
    engines/sci/parser/vocabulary.h


diff --git a/engines/sci/engine/vm_hooks.cpp b/engines/sci/engine/vm_hooks.cpp
index 63608f4553..7a6476c719 100644
--- a/engines/sci/engine/vm_hooks.cpp
+++ b/engines/sci/engine/vm_hooks.cpp
@@ -37,8 +37,6 @@ namespace Sci {
  * Note that when using hooks, the regular PC is frozen, and doesn't advance.
  * Therefore, 'jmp', 'bt' and 'bnt' are used to locally move around inside the patch.
  *
- * (In the past there was a warning regarding 'call's - but they should work now)
- *
  ******************************************************************************************************************/
 
 
diff --git a/engines/sci/engine/vm_hooks.h b/engines/sci/engine/vm_hooks.h
index 274a0d8816..a9b367c7e2 100644
--- a/engines/sci/engine/vm_hooks.h
+++ b/engines/sci/engine/vm_hooks.h
@@ -94,7 +94,7 @@ private:
 	/** Used to avoid double patching in row, and to support `call`ing */
 	reg_t _lastPc;
 
-	/** Used to avoid double patching in row */
+	/** Raised after patch has ended, to avoid confusion with situation of returning from a `call` to the patch, and continue execution of original code */
 	bool _just_finished;
 
 	/** Location inside patch */
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index d85f87e790..eb87f81c25 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -616,15 +616,10 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
 				_menuRect.right = menuTextRect.right;
 		}
 		if ((listNr == newMenuId) || (listNr == oldMenuId)) {
-			if (!g_sci->isLanguageRTL()) {
-				menuTextRect.translate(1, 0);
-				_paint16->invertRect(menuTextRect);
-				menuTextRect.translate(-1, 0);
-			} else {
-				menuTextRect.translate(-1, 0);
-				_paint16->invertRect(menuTextRect);
-				menuTextRect.translate(1, 0);
-			}
+			int multiplier = !g_sci->isLanguageRTL() ? 1 : -1;
+			menuTextRect.translate(1 * multiplier, 0);
+			_paint16->invertRect(menuTextRect);
+			menuTextRect.translate(-1 * multiplier, 0);
 		}
 
 		listIterator++;
@@ -834,6 +829,8 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
 	_paint16->bitsShow(_ports->_menuRect);
 	_paint16->bitsShow(_menuRect);
 
+	int multiplier = !g_sci->isLanguageRTL() ? 1 : -1;
+
 	while (true) {
 		curEvent = _event->getSciEvent(kSciEventAny);
 
@@ -854,22 +851,12 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
 					}
 					break;
 				case kSciKeyLeft:
-					if (!g_sci->isLanguageRTL()) {
-						newMenuId--;
-						newItemId = 1;
-					} else {
-						newMenuId++;
-						newItemId = 1;
-					}
+					newMenuId -= 1 * multiplier;
+					newItemId = 1;
 					break;
 				case kSciKeyRight:
-					if (!g_sci->isLanguageRTL()) {
-						newMenuId++;
-						newItemId = 1;
-					} else {
-						newMenuId--;
-						newItemId = 1;
-					}
+					newMenuId += 1 * multiplier;
+					newItemId = 1;
 					break;
 				case kSciKeyUp:
 					newItemId--;
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index eccbf49806..1e88588be1 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -560,46 +560,37 @@ void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const
 			break;
 		Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true);
 		maxTextWidth = MAX<int16>(maxTextWidth, textWidth);
-		if (!g_sci->isLanguageRTL()) {
-			switch (alignment) {
-			case SCI_TEXT16_ALIGNMENT_RIGHT:
+		switch (alignment) {
+		case SCI_TEXT16_ALIGNMENT_RIGHT:
+			if (!g_sci->isLanguageRTL())
 				offset = rect.width() - textWidth;
-				break;
-			case SCI_TEXT16_ALIGNMENT_CENTER:
-				offset = (rect.width() - textWidth) / 2;
-				break;
-			case SCI_TEXT16_ALIGNMENT_LEFT:
+			else
 				offset = 0;
-				break;
-
-			default:
-				warning("Invalid alignment %d used in TextBox()", alignment);
-			}
-		} else {
-			// language direction is from Right to Left
-			switch (alignment) {
-			case SCI_TEXT16_ALIGNMENT_LEFT:
-				offset = rect.width() - textWidth;
-				break;
-			case SCI_TEXT16_ALIGNMENT_CENTER:
-				offset = (rect.width() - textWidth) / 2;
-				break;
-			case SCI_TEXT16_ALIGNMENT_RIGHT:
+			break;
+		case SCI_TEXT16_ALIGNMENT_CENTER:
+			offset = (rect.width() - textWidth) / 2;
+			break;
+		case SCI_TEXT16_ALIGNMENT_LEFT:
+			if (!g_sci->isLanguageRTL())
 				offset = 0;
-				break;
+			else
+				offset = rect.width() - textWidth;
+			break;
+
+		default:
+			warning("Invalid alignment %d used in TextBox()", alignment);
+		}
 
-			default:
-				warning("Invalid alignment %d used in TextBox()", alignment);
-			}
 
-			// in the fonts, the characters have some spacing to the left, and no space to the right
+		if (g_sci->isLanguageRTL())
+			// In the game fonts, characters have spacing on the left, and no spacing on the right,
 			// therefore, when we start drawing from the right, they "start from the border"
-			// e.g., in SQ3 Hebrew user's input prompt
-			// we can't make the Hebrew letters spacing in the right (in the font), because then mixed English-Hebrew text
-			// might have 2 letters stick together
-			// therefore, we shift here one pixel to the left, for the spacing
+			// e.g., in SQ3 Hebrew user's input prompt.
+			// We can't add spacing on the right of the Hebrew letters, because then characters in mixed
+			// English-Hebrew text might be stuck together.
+			// Therefore, we shift one pixel to the left, for proper spacing
 			offset--;
-		}
+
 		_ports->moveTo(rect.left + offset, rect.top + hline);
 
 		Common::String textString;
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 2c8739e272..7ff06dfd6f 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -265,20 +265,16 @@ void Vocabulary::appendSuffixes() {
 			else
 				cls = VOCAB_CLASS_NOUN << 4;
 
-			suffix_t suffix1 = { cls, cls, 1, 0, "\xea", "" };					// get rid of Kaf Sofit
-			_parserSuffixes.push_back(suffix1);
-
-			suffix_t suffix2 = { cls, cls, 2, 0, "\xe9\xed", "" };				// get rid of Yud, Mem Sofit
-			_parserSuffixes.push_back(suffix2);
-
-			suffix_t suffix3 = { cls, cls, 2, 0, "\xe5\xfa", "" };				// get rid of Vav, Taf
-			_parserSuffixes.push_back(suffix3);
-
-			suffix_t suffix4 = { cls, cls, 3, 2, "\xe9\xe5\xfa", "\xe9\xfa" };	// Yud, Vav, Taf -> Yud, Taf
-			_parserSuffixes.push_back(suffix4);
-
-			suffix_t suffix5 = { cls, cls, 3, 2, "\xe0\xe5\xfa", "\xe0\xe4" };	// Alef, Vav, Taf -> Alef, He
-			_parserSuffixes.push_back(suffix5);
+			suffix_t suffixes[] = {
+				{cls, cls, 1, 0, "\xea", ""},					// get rid of Kaf Sofit
+				{cls, cls, 2, 0, "\xe9\xed", ""},				// get rid of Yud, Mem Sofit
+				{cls, cls, 2, 0, "\xe5\xfa", ""},				// get rid of Vav, Taf
+				{cls, cls, 3, 2, "\xe9\xe5\xfa", "\xe9\xfa"},	// Yud, Vav, Taf -> Yud, Taf
+				{cls, cls, 3, 2, "\xe0\xe5\xfa", "\xe0\xe4"}	// Alef, Vav, Taf -> Alef, He
+			};
+
+			for (int j = 0; j < ARRAYSIZE(suffixes); j++)
+				_parserSuffixes.push_back(suffixes[j]);
 		}
 	}
 }
@@ -519,14 +515,16 @@ void Vocabulary::lookupWordPrefix(ResultWordListList &parent_retval, ResultWordL
 	if (--word_len <= 0)
 		return;
 
-	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xe1, "1hebrew1prefix1bet"))			// "Bet"
-		return;
-	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xe4, "the"))							// "He Hayedia"
-		return;
-	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xec, "1hebrew1prefix1lamed"))			// "Lamed"
-		return;
-	if (lookupSpecificPrefix(parent_retval, retval, word, word_len, 0xee, "1hebrew1prefix1mem"))			// "Mem"
-		return;
+	PrefixMeaning prefixes[] = {
+		{0xe1, "1hebrew1prefix1bet"},           // "Bet"
+		{0xe4, "the"},                          // "He Hayedia"
+		{0xec, "1hebrew1prefix1lamed"},         // "Lamed"
+		{0xee, "1hebrew1prefix1mem"}            // "Mem"
+	};
+
+	for (int i = 0; i < ARRAYSIZE(prefixes); i++)
+		if (lookupSpecificPrefix(parent_retval, retval, word, word_len, prefixes[i].prefix, prefixes[i].meaning))
+			return;
 }
 
 bool Vocabulary::lookupSpecificPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len, unsigned char prefix, const char *meaning) {
@@ -652,7 +650,7 @@ bool Vocabulary::tokenizeString(ResultWordListList &retval, const char *sentence
 				if (lookup_result.empty()) { // Not found?
 					lookupWordPrefix(retval, lookup_result, currentWord, wordLen);
 
-					if (lookup_result.empty()) { // Still not found? {
+					if (lookup_result.empty()) { // Still not found?
 						*error = (char *)calloc(wordLen + 1, 1);
 						strncpy(*error, currentWord, wordLen); // Set the offending word
 						retval.clear();
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index bdfbe5bdca..f4d13d698b 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -209,24 +209,24 @@ public:
 	void lookupWord(ResultWordList &retval, const char *word, int word_len);
 
 	/**
-	 * Looks up a single word in the words list, taking into account suffixes, and updating parent_retval if finding matching prefix
+	 * Looks up a single word in the words list, taking into account suffixes, and updating parent_retval if a matching prefix is found
 	 * Note: there is no equivalent in Sierra SCI, added to support specific languages translations
-	 * For other languages, does nothing
-	 * @param parent_retval		parent's function list of matches
-	 * @param retval			the list of matches
-	 * @param word				pointer to the word to look up
-	 * @param word_len			length of the word to look up
+	 * For other languages, it does nothing
+	 * @param parent_retval     parent's function list of matches
+	 * @param retval            the list of matches
+	 * @param word              pointer to the word to look up
+	 * @param word_len          length of the word to look up
 	 */
 	void lookupWordPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len);
 
 	/**
 	 * Helper function for lookupWordPrefix, checking specific prefix for match
-	 * @param parent_retval		lookupWordPrefix's parent's function list of matches
-	 * @param retval			lookupWordPrefix's list of matches
-	 * @param word				pointer to the word to look up
-	 * @param word_len			length of the word to look up
-	 * @param prefix			the prefix to look for in the word
-	 * @param meaning			the meaning of that prefix
+	 * @param parent_retval     lookupWordPrefix's parent's function list of matches
+	 * @param retval            lookupWordPrefix's list of matches
+	 * @param word              pointer to the word to look up
+	 * @param word_len          length of the word to look up
+	 * @param prefix            the prefix to look for in the word
+	 * @param meaning           the meaning of that prefix
 	 * @return true on prefix match, false on prefix not matching
 	 */
 	bool lookupSpecificPrefix(ResultWordListList &parent_retval, ResultWordList &retval, const char *word, int word_len, unsigned char prefix, const char *meaning);
@@ -399,6 +399,11 @@ private:
 	SynonymList _synonyms; /**< The list of synonyms */
 	Common::Array<Common::List<AltInput> > _altInputs;
 
+	struct PrefixMeaning {
+		unsigned char prefix;
+		const char *meaning;
+	};
+
 	int _pronounReference;
 
 public:




More information about the Scummvm-git-logs mailing list