[Scummvm-git-logs] scummvm master -> 5a93dc22750916bed7d752fcbad853b16baa5dba

sev- sev at scummvm.org
Sun Mar 14 17:52:39 UTC 2021


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:
dc5783c910 COMMON: Add String::forEachLine and convertBiDiStringByLines
5a93dc2275 AGI: Add Hebrew support


Commit: dc5783c910428629a9476856c7a9e257af11da05
    https://github.com/scummvm/scummvm/commit/dc5783c910428629a9476856c7a9e257af11da05
Author: Zvika Haramaty (haramaty.zvika at gmail.com)
Date: 2021-03-14T18:52:36+01:00

Commit Message:
COMMON: Add String::forEachLine and convertBiDiStringByLines

`convertBiDiStringByLines` calls the BiDi algo for each line in isolation,
and returns a joined result.
That's needed to support BiDi in AGI, and might be needed for other engines
in the future.

In order to do that, a new utility function was added:
`String::forEachLine` which gets a function as input, and its arg(s) (if it has any),
and calls the function on each line, and returns a new string which is all
concatenation of all the lines results (with '\n' added between them).

Changed paths:
    common/str.cpp
    common/str.h
    common/unicode-bidi.cpp
    common/unicode-bidi.h


diff --git a/common/str.cpp b/common/str.cpp
index e1171a9511..98bdac2a14 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -410,6 +410,27 @@ String String::substr(size_t pos, size_t len) const {
 		return String(_str + pos, MIN((size_t)_size - pos, len));
 }
 
+String String::forEachLine(String(*func)(const String, va_list args), ...) const {
+	String result = "";
+	size_t index = findFirstOf('\n', 0);
+	size_t prev_index = 0;
+	va_list args;
+	va_start(args, func);
+	while (index != -1) {
+		String textLine = substr(prev_index, index - prev_index);
+		textLine = (*func)(textLine, args);
+		result = result + textLine + '\n';
+		prev_index = index + 1;
+		index = findFirstOf('\n', index + 1);
+	}
+
+	String textLine = substr(prev_index);
+	textLine = (*func)(textLine, args);
+	result = result + textLine;
+	va_end(args);
+	return result;
+}
+
 #pragma mark -
 
 bool operator==(const char* y, const String &x) {
diff --git a/common/str.h b/common/str.h
index ff8a0b3dae..ca0a3e886f 100644
--- a/common/str.h
+++ b/common/str.h
@@ -242,6 +242,9 @@ public:
 	/** Return a substring of this string */
 	String substr(size_t pos = 0, size_t len = npos) const;
 
+	/** Calls func on each line of the string, and returns a joined string */
+	String forEachLine(String(*func)(const String, va_list args), ...) const;
+
 	/** Python-like method **/
 	U32String decode(CodePage page = kUtf8) const;
 
diff --git a/common/unicode-bidi.cpp b/common/unicode-bidi.cpp
index 44f8ca2cc1..b03b03d72a 100644
--- a/common/unicode-bidi.cpp
+++ b/common/unicode-bidi.cpp
@@ -38,6 +38,12 @@ UnicodeBiDiText::UnicodeBiDiText(const Common::String &str, const Common::CodePa
 	initWithU32String(str.decode(page));
 }
 
+UnicodeBiDiText::UnicodeBiDiText(const Common::String &str, const Common::CodePage page, uint *pbase_dir) : logical(str), _log_to_vis_index(NULL), _vis_to_log_index(NULL) {
+	_pbase_dir = *pbase_dir;
+	initWithU32String(str.decode(page));
+	*pbase_dir = _pbase_dir;
+}
+
 UnicodeBiDiText::~UnicodeBiDiText() {
 	delete[] _log_to_vis_index;
 	delete[] _vis_to_log_index;
@@ -64,13 +70,12 @@ void UnicodeBiDiText::initWithU32String(const U32String &input) {
 	FriBidiChar *visual_str = new FriBidiChar[buff_length * sizeof(FriBidiChar)];
 	_log_to_vis_index = new uint32[input_size];
 	_vis_to_log_index = new uint32[input_size];
-	FriBidiParType pbase_dir = FRIBIDI_PAR_ON;
 
 	if (!fribidi_log2vis(
 		/* input */
 		(const FriBidiChar *)input.c_str(),
 		input_size,
-		&pbase_dir,
+		&_pbase_dir,
 		/* output */
 		visual_str,
 		(FriBidiStrIndex *)_log_to_vis_index,	// position_L_to_V_list,
@@ -99,6 +104,17 @@ void UnicodeBiDiText::initWithU32String(const U32String &input) {
 
 }
 
+Common::String bidiByLineHelper(Common::String line, va_list args) {
+	Common::CodePage page = va_arg(args, Common::CodePage);
+	uint32 *pbase_dir = va_arg(args, uint32*);
+	return UnicodeBiDiText(line, page, pbase_dir).visual.encode(page);
+}
+
+String convertBiDiStringByLines(const String &input, const Common::CodePage page) {
+	uint32 pbase_dir = SCUMMVM_FRIBIDI_PAR_ON;
+	return input.forEachLine(bidiByLineHelper, page, &pbase_dir);
+}
+
 String convertBiDiString(const String &input, const Common::Language lang) {
 	if (lang != Common::HE_ISR)		//TODO: modify when we'll support other RTL languages, such as Arabic and Farsi
 		return input;
diff --git a/common/unicode-bidi.h b/common/unicode-bidi.h
index 6c7465489c..a24fad16ca 100644
--- a/common/unicode-bidi.h
+++ b/common/unicode-bidi.h
@@ -27,6 +27,21 @@
 #include "common/ustr.h"
 #include "common/language.h"
 
+
+// SCUMMVM_FRIBIDI_PAR_ON: automatically check the text's direction
+// SCUMMVM_FRIBIDI_PAR_LTR, SCUMMVM_FRIBIDI_PAR_RTL: enforce LTR or RTL direction
+// if not USE_FRIBIDI, these defines values don't matter
+#ifdef USE_FRIBIDI
+#define SCUMMVM_FRIBIDI_PAR_ON			FRIBIDI_PAR_ON
+#define SCUMMVM_FRIBIDI_PAR_LTR			FRIBIDI_PAR_LTR
+#define SCUMMVM_FRIBIDI_PAR_RTL			FRIBIDI_PAR_RTL
+#else
+#define SCUMMVM_FRIBIDI_PAR_ON			0
+#define SCUMMVM_FRIBIDI_PAR_LTR			0
+#define SCUMMVM_FRIBIDI_PAR_RTL			0
+#endif
+
+
 namespace Common {
 
 class UnicodeBiDiText {
@@ -34,12 +49,15 @@ private:
 	uint32 *_log_to_vis_index; // from fribidi conversion
 	uint32 *_vis_to_log_index; // from fribidi conversion
 	void initWithU32String(const Common::U32String &str);
+	Common::String bidiByLine(Common::String line, va_list args);
 public:
 	const Common::U32String logical; // original string, ordered logically
 	Common::U32String visual; // from fribidi conversion, ordered visually
+	uint32 _pbase_dir;
 
 	UnicodeBiDiText(const Common::U32String &str);
 	UnicodeBiDiText(const Common::String &str, const Common::CodePage page);
+	UnicodeBiDiText(const Common::String &str, const Common::CodePage page, uint *pbase_dir);
 	~UnicodeBiDiText();
 
 	/**
@@ -57,6 +75,9 @@ UnicodeBiDiText convertBiDiU32String(const U32String &input);
 String convertBiDiString(const String &input, const Common::Language lang);
 String convertBiDiString(const String &input, const Common::CodePage page);
 
+// calls convertBiDiString for each line in isolation
+String convertBiDiStringByLines(const String &input, const Common::CodePage page);
+
 } // End of namespace Common
 
 #endif


Commit: 5a93dc22750916bed7d752fcbad853b16baa5dba
    https://github.com/scummvm/scummvm/commit/5a93dc22750916bed7d752fcbad853b16baa5dba
Author: Zvika Haramaty (haramaty.zvika at gmail.com)
Date: 2021-03-14T18:52:36+01:00

Commit Message:
AGI: Add Hebrew support

Changed paths:
    engines/agi/agi.h
    engines/agi/detection_tables.h
    engines/agi/inv.cpp
    engines/agi/keyboard.cpp
    engines/agi/loader_v2.cpp
    engines/agi/menu.cpp
    engines/agi/metaengine.cpp
    engines/agi/saveload.cpp
    engines/agi/systemui.cpp
    engines/agi/text.cpp
    engines/agi/text.h
    engines/agi/words.cpp
    engines/agi/words.h


diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index f6d4b10ca4..504f52e5f8 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -734,6 +734,7 @@ public:
 	uint16 getVersion() const;
 	uint16 getGameType() const;
 	Common::Language getLanguage() const;
+	bool isLanguageRTL() const;
 	Common::Platform getPlatform() const;
 	const char *getGameMD5() const;
 	void initFeatures();
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index 068403aa0d..6b6f71195e 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -545,6 +545,9 @@ static const AGIGameDescription gameDescriptions[] = {
 	// not sure about disk format -- dsymonds
 	GAME("pq1", "2.0G 1987-12-03", "d194e5d88363095f55d5096b8e32fbbb", 0x2917, GID_PQ1),
 
+	// Police Quest 1 (PC) 2.0G 12/3/87; with Hebrew translation
+	GAME_LVFPN("pq1", "2.0G 1987-12-03", "PQ1.WAG", "59e1b2fb6d025968b8ed7388f107c7b5", -1, Common::HE_ISR, 0x2917, 0, GID_PQ1, Common::kPlatformDOS, GType_V2, GAMEOPTIONS_DEFAULT),
+
 	// Police Quest 1 (CoCo3 360k) [AGI 2.023]
 	GAME_PS("pq1", "", "28a077041f75aab78f66804800940085", 375, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
 
diff --git a/engines/agi/inv.cpp b/engines/agi/inv.cpp
index 834fa9badc..4df55527a1 100644
--- a/engines/agi/inv.cpp
+++ b/engines/agi/inv.cpp
@@ -65,9 +65,20 @@ void InventoryMgr::getPlayerInventory() {
 			inventoryEntry.name = _vm->objectName(objectNr);
 			inventoryEntry.row = curRow;
 			inventoryEntry.column = curColumn;
-			if (inventoryEntry.column > 1) {
-				// right side, adjust column accordingly
-				inventoryEntry.column -= strlen(inventoryEntry.name);
+			if (!_vm->isLanguageRTL()) {
+				if (inventoryEntry.column > 1) {
+					// right side, adjust column accordingly
+					inventoryEntry.column -= Common::strnlen(inventoryEntry.name, FONT_COLUMN_CHARACTERS);
+				}
+			} else {
+				// mirror the sides
+				if (inventoryEntry.column == 1) {
+					// right side, adjust column accordingly
+					inventoryEntry.column = FONT_COLUMN_CHARACTERS - 1 - Common::strnlen(inventoryEntry.name, FONT_COLUMN_CHARACTERS);
+				} else {
+					// left side, adjust column accordingly
+					inventoryEntry.column = 1;
+				}
 			}
 			_array.push_back(inventoryEntry);
 
@@ -187,10 +198,16 @@ void InventoryMgr::keyPress(uint16 newKey) {
 		changeActiveItem(+2);
 		break;
 	case AGI_KEY_LEFT:
-		changeActiveItem(-1);
+		if (!_vm->isLanguageRTL())
+			changeActiveItem(-1);
+		else
+			changeActiveItem(+1);
 		break;
 	case AGI_KEY_RIGHT:
-		changeActiveItem(+1);
+		if (!_vm->isLanguageRTL())
+			changeActiveItem(+1);
+		else
+			changeActiveItem(-1);
 		break;
 
 	default:
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 0f86438907..d1ad572c73 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -141,6 +141,11 @@ void AgiEngine::processScummVMEvents() {
 				}
 			}
 
+			if (_game._vm->getLanguage() == Common::HE_ISR && key >= 0x05d0 && key <= 0x05ea) {
+				// convert to WIN-1255
+				key = key - 0x05d0 + 0xe0;
+			}
+
 			if ((key) && (key <= 0xFF)) {
 				// No special key, directly accept it
 				// Is ISO-8859-1, we need lower 128 characters only, which is plain ASCII, so no mapping required
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index ddc8df3f2d..fed17ac11a 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -276,7 +276,10 @@ int AgiLoader_v2::loadObjects(const char *fname) {
 }
 
 int AgiLoader_v2::loadWords(const char *fname) {
-	return _vm->_words->loadDictionary(fname);
+	if (_vm->getLanguage() != Common::HE_ISR) 
+		return _vm->_words->loadDictionary(fname);
+	else
+		return _vm->_words->loadExtendedDictionary(fname);
 }
 
 } // End of namespace Agi
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
index 1d5366da5a..743de33045 100644
--- a/engines/agi/menu.cpp
+++ b/engines/agi/menu.cpp
@@ -41,7 +41,10 @@ GfxMenu::GfxMenu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture, TextMgr *text)
 	_delayedExecuteViaKeyboard = false;
 	_delayedExecuteViaMouse = false;
 
-	_setupMenuColumn = 1;
+	if (!_vm->isLanguageRTL())
+		_setupMenuColumn = 1;
+	else
+		_setupMenuColumn = FONT_COLUMN_CHARACTERS - 2;
 	_setupMenuItemColumn = 1;
 
 	_lastSelectedMenuNr = 0;
@@ -82,26 +85,33 @@ void GfxMenu::addMenu(const char *menuText) {
 
 	menuEntry->textLen = menuEntry->text.size();
 
-	// Cut menu name in case menu bar is full
-	// Happens in at least the fan game Get Outta Space Quest
-	// Original interpreter had graphical issues in this case
-	// TODO: this whole code needs to get reworked anyway to support different types of menu bars depending on platform
-	curColumnEnd += menuEntry->textLen;
-	while ((menuEntry->textLen) && (curColumnEnd > 40)) {
-		menuEntry->text.deleteLastChar();
-		menuEntry->textLen--;
-		curColumnEnd--;
+	if (!_vm->isLanguageRTL()) {
+		// Cut menu name in case menu bar is full
+		// Happens in at least the fan game Get Outta Space Quest
+		// Original interpreter had graphical issues in this case
+		// TODO: this whole code needs to get reworked anyway to support different types of menu bars depending on platform
+		curColumnEnd += menuEntry->textLen;
+		while ((menuEntry->textLen) && (curColumnEnd > 40)) {
+			menuEntry->text.deleteLastChar();
+			menuEntry->textLen--;
+			curColumnEnd--;
+		}
 	}
 
 	menuEntry->row = 0;
 	menuEntry->column = _setupMenuColumn;
+	if (_vm->isLanguageRTL())
+		menuEntry->column -= menuEntry->textLen;
 	menuEntry->itemCount = 0;
 	menuEntry->firstItemNr = _itemArray.size();
 	menuEntry->selectedItemNr = menuEntry->firstItemNr;
 	menuEntry->maxItemTextLen = 0;
 	_array.push_back(menuEntry);
 
-	_setupMenuColumn += menuEntry->textLen + 1;
+	if (!_vm->isLanguageRTL())
+		_setupMenuColumn += menuEntry->textLen + 1;
+	else
+		_setupMenuColumn -= menuEntry->textLen + 1;
 }
 
 void GfxMenu::addMenuItem(const char *menuItemText, uint16 controllerSlot) {
@@ -132,11 +142,17 @@ void GfxMenu::addMenuItem(const char *menuItemText, uint16 controllerSlot) {
 	}
 
 	if (curMenuEntry->itemCount == 0) {
-		// for first menu item of menu calculated column
-		if (menuItemEntry->textLen + curMenuEntry->column < (FONT_COLUMN_CHARACTERS - 1)) {
-			_setupMenuItemColumn = curMenuEntry->column;
+		if (!_vm->isLanguageRTL()) {
+			// for first menu item of menu calculated column
+			if (menuItemEntry->textLen + curMenuEntry->column < (FONT_COLUMN_CHARACTERS - 1)) {
+				_setupMenuItemColumn = curMenuEntry->column;
+			} else {
+				_setupMenuItemColumn = (FONT_COLUMN_CHARACTERS - 1) - menuItemEntry->textLen;
+			}
 		} else {
-			_setupMenuItemColumn = (FONT_COLUMN_CHARACTERS - 1) - menuItemEntry->textLen;
+			_setupMenuItemColumn = curMenuEntry->column + curMenuEntry->textLen - menuItemEntry->textLen;
+			if (_setupMenuItemColumn < 2)
+				_setupMenuItemColumn = 2;
 		}
 	}
 
@@ -520,10 +536,16 @@ void GfxMenu::keyPress(uint16 newKey) {
 		break;
 
 	case AGI_KEY_LEFT:
-		newMenuNr--;
+		if (!_vm->isLanguageRTL())
+			newMenuNr--;
+		else
+			newMenuNr++;
 		break;
 	case AGI_KEY_RIGHT:
-		newMenuNr++;
+		if (!_vm->isLanguageRTL())
+			newMenuNr++;
+		else
+			newMenuNr--;
 		break;
 	case AGI_KEY_HOME:
 		// select first menu
diff --git a/engines/agi/metaengine.cpp b/engines/agi/metaengine.cpp
index d91099f85c..a1d9b5c162 100644
--- a/engines/agi/metaengine.cpp
+++ b/engines/agi/metaengine.cpp
@@ -55,7 +55,16 @@ Common::Platform AgiBase::getPlatform() const {
 }
 
 Common::Language AgiBase::getLanguage() const {
-	return _gameDescription->desc.language;
+	if (_gameDescription->desc.language != Common::UNK_LANG)
+		return _gameDescription->desc.language;
+	else if (ConfMan.hasKey("language"))
+		return Common::parseLanguage(ConfMan.get("language"));
+	else
+		return Common::UNK_LANG;
+}
+
+bool AgiBase::isLanguageRTL() const {
+	return getLanguage() == Common::HE_ISR;
 }
 
 uint16 AgiBase::getVersion() const {
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index c781f00d7c..cca2c8b0e6 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -85,11 +85,20 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de
 
 	out->writeUint32BE(AGIflag);
 
+	const char *descriptionStringC;
+	Common::U32String hebDesc;
+	if (_game._vm->getLanguage() != Common::HE_ISR) {
+		descriptionStringC = descriptionString.c_str();
+	} else {
+		hebDesc = descriptionString.substr(0, SAVEDGAME_DESCRIPTION_LEN / 2 - 3).decode(Common::kWindows1255);
+		descriptionStringC = hebDesc.encode(Common::kUtf8).c_str();
+	}
+
 	// Write description of saved game, limited to SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
 	char description[SAVEDGAME_DESCRIPTION_LEN + 1];
 
 	memset(description, 0, sizeof(description));
-	strncpy(description, descriptionString.c_str(), SAVEDGAME_DESCRIPTION_LEN);
+	Common::strlcpy(description, descriptionStringC, SAVEDGAME_DESCRIPTION_LEN);
 	assert(SAVEDGAME_DESCRIPTION_LEN + 1 == 31); // safety
 	out->write(description, 31);
 
@@ -927,6 +936,10 @@ bool AgiEngine::getSavegameInformation(int16 slotId, Common::String &saveDescrip
 		saveDescription += saveGameDescription;
 		saveIsValid = true;
 
+		if (_game._vm->getLanguage() == Common::HE_ISR) {
+			saveDescription = saveDescription.decode(Common::kUtf8).encode(Common::kWindows1255);
+		}
+
 		delete in;
 		return true;
 	}
diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp
index f2304ea623..0eb54752b9 100644
--- a/engines/agi/systemui.cpp
+++ b/engines/agi/systemui.cpp
@@ -102,6 +102,26 @@ SystemUI::SystemUI(AgiEngine *vm, GfxMgr *gfx, TextMgr *text) {
 		_textRestoreGameError = "\x8E\xE8\xA8\xA1\xAA\xA0 \xA2 \xA7\xA0\xAF\xA8\xE1\xA0\xAD\xAD\xAE\xA9 \xA8\xA3\xE0\xA5.\nENTER - \xA2\xEB\xE5\xAE\xA4.";
 		_textRestoreGameVerify = "\x83\xAE\xE2\xAE\xA2 \xAA \xE1\xE7\xA8\xE2\xEB\xA2\xA0\xAD\xA8\xEE \xA8\xA3\xE0\xEB\x2C\n\xAE\xAF\xA8\xE1\xA0\xAD\xAD\xAE\xA9 \xAA\xA0\xAA.\n\n%s\n\n\xA8\xA7 \xE4\xA0\xA9\xAB\xA0:\n%s\n\n\x84\xAB\xEF \xAF\xE0\xAE\xA4\xAE\xAB\xA6\xA5\xAD\xA8\xEF \xAD\xA0\xA6\xAC\xA8\xE2\xA5 ENTER.\nESC - \xAE\xE2\xAC\xA5\xAD\xA0.";
 		break;
+	case Common::HE_ISR:
+		_textStatusScore = "\xf0\xe9\xf7\xe5\xe3: %v3 \xee\xfa\xe5\xea %v7";
+		_textStatusSoundOn = "\xf6\xec\xe9\xec: \xf4\xe5\xf2\xec";
+		_textStatusSoundOff = "\xf6\xec\xe9\xec: \xeb\xe1\xe5\xe9";
+		_textEnterCommand = "\xe4\xeb\xf0\xe9\xf1\xe5 \xf7\xec\xe8\n\n";
+		_textPause = "      \xe4\xee\xf9\xe7\xf7 \xee\xe5\xf9\xe4\xe4.\n\xec\xe7\xf6\xe5 \xf2\xec ENTER \xeb\xe3\xe9 \xec\xe4\xee\xf9\xe9\xea.";
+		_textRestart = "\xec\xe7\xf6\xe5 \xf2\xec ENTER \xeb\xe3\xe9 \xec\xe4\xfa\xe7\xe9\xec \xee\xe7\xe3\xf9\n\xe0\xfa \xe4\xee\xf9\xe7\xf7.\n\n\xec\xe7\xf6\xe5 \xf2\xec ESC \xeb\xe3\xe9 \xec\xe4\xee\xf9\xe9\xea\n\xe0\xfa \xe4\xee\xf9\xe7\xf7 \xe4\xf0\xe5\xeb\xe7\xe9.";
+		_textQuit = "\xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xe9\xf6\xe9\xe0\xe4.\n\xec\xe7\xf6\xe5 \xf2\xec ESC \xeb\xe3\xe9 \xec\xe4\xee\xf9\xe9\xea \xec\xf9\xe7\xf7.";
+		_textInventoryYouAreCarrying = "\xe0\xfa\xe4 \xf0\xe5\xf9\xe0:";
+		_textInventoryNothing = "\xec\xe0 \xeb\xec\xe5\xed";
+		_textInventorySelectItems = "\xec\xe7\xf6\xe5 \xf2\xec ENTER \xeb\xe3\xe9 \xec\xe1\xe7\xe5\xf8, ESC \xeb\xe3\xe9 \xec\xe1\xe8\xec";
+		_textInventoryReturnToGame = "\xec\xe7\xf6\xe5 \xf2\xec \xee\xf7\xf9 \xeb\xec\xf9\xe4\xe5 \xeb\xe3\xe9 \xec\xe7\xe6\xe5\xf8 \xec\xee\xf9\xe7\xf7";
+		_textSaveGameSelectSlot = "\xe4\xf9\xfa\xee\xf9\xe5 \xe1\xee\xf7\xf9\xe9 \xe4\xe7\xe9\xf6\xe9\xed \xf2\xec \xee\xf0\xfa \xec\xe1\xe7\xe5\xf8 \xee\xf9\xe1\xf6\xfa \xf9\xe1\xe4 \xfa\xf8\xf6\xe5 \xec\xf9\xee\xe5\xf8 \xe0\xfa \xe4\xee\xf9\xe7\xf7. \xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xf9\xee\xe9\xf8\xfa \xee\xf9\xe7\xf7 \xe1\xee\xf9\xe1\xf6\xfa, ESC \xec\xe1\xe9\xe8\xe5\xec.";
+		_textSaveGameEnterDescription = "\xe0\xe9\xea \xfa\xf8\xf6\xe5 \xec\xfa\xe0\xf8 \xe0\xfa \xe4\xee\xf9\xe7\xf7 \xe4\xf9\xee\xe5\xf8 \xe4\xe6\xe4?\n\n";
+		_textSaveGameVerify = "\xe1\xe7\xf8\xfa \xec\xf9\xee\xe5\xf8 \xe0\xfa \xe4\xee\xf9\xe7\xf7\n\xf2\xed \xe4\xfa\xe9\xe0\xe5\xf8:\n\n%s\n\n\xe1\xf7\xe5\xe1\xf5:\n%s\n\n\xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xe4\xee\xf9\xea.\n\xec\xe7\xf6\xe5 \xf2\xec ESC \xec\xe1\xe9\xe8\xe5\xec.";
+		_textRestoreGameNoSlots = "\xe0\xe9\xef \xee\xf9\xe7\xf7\xe9\xed\n\xec\xf9\xe7\xe6\xf8\n\n \xe1\xf1\xf4\xf8\xe9\xe9\xfa \xe4\xee\xf9\xe7\xf7\xe9\xed \xe4\xf9\xee\xe5\xf8\xe9\xed \xf9\xec ScummVM\n\n\xec\xe7\xf6\xe5 \xf2\xec ENTER \xeb\xe3\xe9 \xec\xe4\xee\xf9\xe9\xea.";
+		_textRestoreGameSelectSlot = "\xe4\xf9\xfa\xee\xf9\xe5 \xe1\xee\xf7\xf9\xe9 \xe4\xe7\xe9\xf6\xe9\xed \xf2\xec \xee\xf0\xfa \xec\xe1\xe7\xe5\xf8 \xe0\xfa \xe4\xee\xf9\xe7\xf7 \xf9\xfa\xf8\xf6\xe5 \xec\xf9\xe7\xe6\xf8. \xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xf9\xe7\xe6\xe5\xf8 \xe4\xee\xf9\xe7\xf7, ESC \xec\xe1\xe9\xe8\xe5\xec.";
+		_textRestoreGameError = "\xf9\xe2\xe9\xe0\xe4 \xe1\xf0\xe9\xf1\xe9\xe5\xef \xec\xf9\xe7\xe6\xe5\xf8 \xee\xf9\xe7\xf7.\n\xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xe9\xf6\xe9\xe0\xe4.";
+		_textRestoreGameVerify = "\xe1\xe7\xf8\xfa \xec\xf9\xe7\xe6\xf8 \xe0\xfa \xe4\xee\xf9\xe7\xf7\n\xf2\xed \xe4\xfa\xe9\xe0\xe5\xf8:\n\n%s\n\n\xee\xe4\xf7\xe5\xe1\xf5:\n%s\n\n\xec\xe7\xf6\xe5 \xf2\xec ENTER \xec\xe4\xee\xf9\xea.\n\xec\xe7\xf6\xe5 \xf2\xec ESC \xec\xe1\xe9\xe8\xe5\xec.";
+		break;
 	default:
 		break;
 	}
@@ -723,8 +743,13 @@ void SystemUI::drawSavedGameSlots() {
 	_text->charAttrib_Set(0, 15);
 
 	for (slotNr = 0; slotNr < slotsToDrawCount; slotNr++) {
-		_text->displayTextInsideWindow("-", 5 + slotNr, 1);
-		_text->displayTextInsideWindow(_savedGameArray[_savedGameUpmostSlotNr + slotNr].displayText, 5 + slotNr, 3);
+		if (!_vm->isLanguageRTL()) {
+			_text->displayTextInsideWindow("-", 5 + slotNr, 1);
+			_text->displayTextInsideWindow(_savedGameArray[_savedGameUpmostSlotNr + slotNr].displayText, 5 + slotNr, 3);
+		} else {
+			_text->displayTextInsideWindow(_savedGameArray[_savedGameUpmostSlotNr + slotNr].displayText, 5 + slotNr, 0);
+			_text->displayTextInsideWindow("-", 5 + slotNr, 32);
+		}
 	}
 	_text->charAttrib_Pop();
 }
@@ -734,10 +759,19 @@ void SystemUI::drawSavedGameSlotSelector(bool active) {
 
 	_text->charAttrib_Push();
 	_text->charAttrib_Set(0, 15);
+	int16 column;
+	const char *arrow;
+	if (!_vm->isLanguageRTL()) {
+		column = 0;
+		arrow = "\x1a";
+	} else {
+		column = 33;
+		arrow = "\x1b";
+	}
 	if (active) {
-		_text->displayTextInsideWindow("\x1a", windowRow, 0);
+		_text->displayTextInsideWindow(arrow, windowRow, column);
 	} else {
-		_text->displayTextInsideWindow(" ", windowRow, 0);
+		_text->displayTextInsideWindow(" ", windowRow, column);
 	}
 	_text->charAttrib_Pop();
 }
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index bfc4ff4519..5e332ab8da 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "common/config-manager.h"
+#include "common/unicode-bidi.h"
 #include "agi/agi.h"
 #include "agi/sprite.h"     // for commit_both()
 #include "agi/graphics.h"
@@ -274,7 +275,27 @@ void TextMgr::displayTextInsideWindow(const char *textPtr, int16 windowRow, int1
 	charPos_Pop();
 }
 
+Common::String rightAlign(Common::String line, va_list args) {
+	uint width = va_arg(args, uint);
+
+	while (line.size() < width)
+		line = " " + line;
+	return line;
+}
+
 void TextMgr::displayText(const char *textPtr, bool disabledLook) {
+	Common::String textString;
+	if (_vm->isLanguageRTL()) {
+		textString = textPtr;
+		if (_vm->getLanguage() == Common::HE_ISR)
+			textString = Common::convertBiDiStringByLines(textString, Common::kWindows1255);
+
+		if (textString.contains('\n')) 
+			textString = textString.forEachLine(rightAlign, (uint)_messageState.textSize_Width);
+
+		textPtr = textString.c_str();
+	}
+
 	const char *curTextPtr = textPtr;
 	byte  curCharacter = 0;
 
@@ -563,11 +584,17 @@ void TextMgr::statusDraw() {
 		clearLine(_statusRow, 15);
 
 		charAttrib_Set(0, 15);
-		charPos_Set(_statusRow, 1);
 		statusTextPtr = stringPrintf(_systemUI->getStatusTextScore());
+		if (!_vm->isLanguageRTL())
+			charPos_Set(_statusRow, 1);
+		else
+			charPos_Set(_statusRow, FONT_COLUMN_CHARACTERS - Common::strnlen(statusTextPtr, FONT_COLUMN_CHARACTERS) - 1);
 		displayText(statusTextPtr);
 
-		charPos_Set(_statusRow, 30);
+		if (!_vm->isLanguageRTL())
+			charPos_Set(_statusRow, 30);
+		else
+			charPos_Set(_statusRow, 1);
 		if (_vm->getFlag(VM_FLAG_SOUND_ON)) {
 			statusTextPtr = stringPrintf(_systemUI->getStatusTextSoundOn());
 		} else {
@@ -684,6 +711,7 @@ void TextMgr::promptKeyPress(uint16 newKey) {
 	// but as soon as invalid characters were used in graphics mode they weren't properly shown
 	switch (_vm->getLanguage()) {
 	case Common::RU_RUS:
+	case Common::HE_ISR:
 		if (newKey >= 0x20)
 			acceptableInput = true;
 		break;
@@ -721,6 +749,8 @@ void TextMgr::promptKeyPress(uint16 newKey) {
 			_promptCursorPos--;
 			_prompt[_promptCursorPos] = 0;
 			displayCharacter(newKey);
+			if (_vm->isLanguageRTL())
+				promptRedraw();
 
 			promptRememberForAutoComplete();
 		}
@@ -750,6 +780,8 @@ void TextMgr::promptKeyPress(uint16 newKey) {
 				_promptCursorPos++;
 				_prompt[_promptCursorPos] = 0;
 				displayCharacter(newKey);
+				if (_vm->isLanguageRTL())
+					promptRedraw();
 
 				promptRememberForAutoComplete();
 			}
@@ -810,9 +842,17 @@ void TextMgr::promptRedraw() {
 		textPtr = stringPrintf(textPtr);
 		textPtr = stringWordWrap(textPtr, 40);
 
-		displayText(textPtr);
-		displayText((char *)&_prompt);
-		inputEditOff();
+		if (!_vm->isLanguageRTL()) {
+			displayText(textPtr);
+			displayText((char *)&_prompt);
+			inputEditOff();
+		} else {
+			charPos_Set(_promptRow, FONT_COLUMN_CHARACTERS - 2 - Common::strnlen((const char *)_prompt, FONT_COLUMN_CHARACTERS));
+			inputEditOff();
+			displayText((char *)&_prompt);
+			displayText(textPtr);
+			charPos_Set(_promptRow, FONT_COLUMN_CHARACTERS - 1);
+		}
 	}
 }
 
@@ -889,9 +929,21 @@ void TextMgr::stringEdit(int16 stringMaxLen) {
 
 	// Caller can set the input string
 	_inputStringCursorPos = 0;
-	while (_inputStringCursorPos < inputStringLen) {
-		displayCharacter(_inputString[_inputStringCursorPos]);
-		_inputStringCursorPos++;
+	if (!_vm->isLanguageRTL()) {
+		while (_inputStringCursorPos < inputStringLen) {
+			displayCharacter(_inputString[_inputStringCursorPos]);
+			_inputStringCursorPos++;
+		}
+	} else {
+		while (_inputStringCursorPos < inputStringLen) 
+			_inputStringCursorPos++;
+		if (stringMaxLen == 30)
+			// called from askForSaveGameDescription
+			charPos_Set(_textPos.row, 34 - _inputStringCursorPos);
+		else
+			charPos_Set(_textPos.row, stringMaxLen + 2 - _inputStringCursorPos);
+		inputEditOff();
+		displayText((const char *)_inputString);
 	}
 
 	// should never happen unless there is a coding glitch
@@ -900,7 +952,8 @@ void TextMgr::stringEdit(int16 stringMaxLen) {
 	_inputStringMaxLen = stringMaxLen;
 	_inputStringEntered = false;
 
-	inputEditOff();
+	if (!_vm->isLanguageRTL())
+		inputEditOff();
 
 	do {
 		_vm->processAGIEvents();
@@ -932,6 +985,13 @@ void TextMgr::stringKeyPress(uint16 newKey) {
 			_inputStringCursorPos--;
 			_inputString[_inputStringCursorPos] = 0;
 			displayCharacter(newKey);
+			if (_vm->isLanguageRTL()) {
+				for (int i = 0; i < _inputStringCursorPos; i++)
+					displayCharacter(0x08);
+				displayCharacter(' ');
+				inputEditOff();
+				displayText((const char *)_inputString);
+			}
 
 			stringRememberForAutoComplete();
 		}
@@ -965,6 +1025,7 @@ void TextMgr::stringKeyPress(uint16 newKey) {
 			// but as soon as invalid characters were used in graphics mode they weren't properly shown
 			switch (_vm->getLanguage()) {
 			case Common::RU_RUS:
+			case Common::HE_ISR:
 				if (newKey >= 0x20)
 					acceptableInput = true;
 				break;
@@ -981,7 +1042,14 @@ void TextMgr::stringKeyPress(uint16 newKey) {
 					_inputString[_inputStringCursorPos] = newKey;
 					_inputStringCursorPos++;
 					_inputString[_inputStringCursorPos] = 0;
-					displayCharacter(newKey);
+					if (!_vm->isLanguageRTL()) {
+						displayCharacter(newKey);
+					} else {
+						for(int i = 0; i < _inputStringCursorPos ; i++)
+							displayCharacter(0x08);
+						inputEditOff();
+						displayText((const char *)_inputString);
+					}
 
 					stringRememberForAutoComplete();
 				}
diff --git a/engines/agi/text.h b/engines/agi/text.h
index f0aeab7762..5ad6b5afa7 100644
--- a/engines/agi/text.h
+++ b/engines/agi/text.h
@@ -212,6 +212,8 @@ public:
 	char *stringWordWrap(const char *originalText, int16 maxWidth, int16 *calculatedWidthPtr = nullptr, int16 *calculatedHeightPtr = nullptr);
 };
 
+Common::String rightAlign(Common::String line, va_list args);
+
 } // End of namespace Agi
 
 #endif /* AGI_TEXT_H */
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 2e34e1c410..92e6dcd138 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -123,6 +123,32 @@ int Words::loadDictionary(const char *fname) {
 	return errOK;
 }
 
+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;
+}
+
 void Words::unloadDictionary() {
 	for (int16 firstCharNr = 0; firstCharNr < 26; firstCharNr++) {
 		Common::Array<WordEntry *> &dictionary = _dictionaryWords[firstCharNr];
@@ -226,7 +252,9 @@ int16 Words::findWordInDictionary(const Common::String &userInputLowcased, uint1
 
 	foundWordLen = 0;
 
-	if ((firstChar >= 'a') && (firstChar <= 'z')) {
+	const byte lastCharInAbc = _vm->getLanguage() == Common::HE_ISR ? 0xfa : 'z';
+
+	if ((firstChar >= 'a') && (firstChar <= lastCharInAbc)) {
 		// word has to start with a letter
 		if (((userInputPos + 1) < userInputLen) && (userInputLowcased[userInputPos + 1] == ' ')) {
 			// current word is 1 char only?
diff --git a/engines/agi/words.h b/engines/agi/words.h
index 96dafae275..d067b6fade 100644
--- a/engines/agi/words.h
+++ b/engines/agi/words.h
@@ -42,7 +42,8 @@ private:
 	AgiEngine *_vm;
 
 	// Dictionary
-	Common::Array<WordEntry *> _dictionaryWords[26];
+	// 158 = 255 - 'a' ; that's allows us to support extended character set, and does no harm for regular English games
+	Common::Array<WordEntry *> _dictionaryWords[158];
 
 	WordEntry _egoWords[MAX_WORDS];
 	uint16  _egoWordCount;
@@ -54,6 +55,8 @@ public:
 
 	int  loadDictionary_v1(Common::File &f);
 	int  loadDictionary(const char *fname);
+	// used for fan made translations requiring extended char set
+	int  loadExtendedDictionary(const char *fname);
 	void unloadDictionary();
 
 	void clearEgoWords();




More information about the Scummvm-git-logs mailing list