[Scummvm-git-logs] scummvm master -> b00b6fc7602ce700cf1cd5c192926f245155f264

sev- noreply at scummvm.org
Mon Nov 28 23:45:23 UTC 2022


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

Summary:
01d6629b9f SKY: Read Chinese strings from EXE
16a7aec946 SKY: Support Chinese word-wrapping
676a765068 SKY: Add Chinese renderer
b00b6fc760 NEWS: Add an entry for Chinese BASS support


Commit: 01d6629b9fa67aeba9537ce9a753d3d33c44daa6
    https://github.com/scummvm/scummvm/commit/01d6629b9fa67aeba9537ce9a753d3d33c44daa6
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-11-29T00:45:18+01:00

Commit Message:
SKY: Read Chinese strings from EXE

Changed paths:
    engines/sky/control.cpp
    engines/sky/sky.cpp
    engines/sky/sky.h
    engines/sky/skydefs.h
    engines/sky/text.cpp


diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp
index 9ab0a4a9c3b..ac3f6bbb797 100644
--- a/engines/sky/control.cpp
+++ b/engines/sky/control.cpp
@@ -1621,6 +1621,9 @@ void Control::showGameQuitMsg() {
 	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
 		_skyText->displayText(_quitTexts[8 * 2 + 0], textBuf1, true, 320, 255);
 		_skyText->displayText(_quitTexts[8 * 2 + 1], textBuf2, true, 320, 255);
+	} else if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL) { // Not translated in original
+		_skyText->displayText(_quitTexts[0], textBuf1, true, 320, 255);
+		_skyText->displayText(_quitTexts[1], textBuf2, true, 320, 255);
 	} else {
 		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 0], textBuf1, true, 320, 255);
 		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 1], textBuf2, true, 320, 255);
diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
index f79440963d6..1fe092cbe6b 100644
--- a/engines/sky/sky.cpp
+++ b/engines/sky/sky.cpp
@@ -24,6 +24,8 @@
 #include "common/config-manager.h"
 #include "common/system.h"
 #include "common/textconsole.h"
+#include "common/file.h"
+#include "common/md5.h"
 
 #include "sky/control.h"
 #include "sky/debug.h"
@@ -70,6 +72,8 @@ namespace Sky {
 void *SkyEngine::_itemList[300];
 SystemVars *SkyEngine::_systemVars = nullptr;
 const char *SkyEngine::shortcutsKeymapId = "sky-shortcuts";
+uint32 SkyEngine::_chineseTraditionalOffsets[8];
+char *SkyEngine::_chineseTraditionalBlock;
 
 SkyEngine::SkyEngine(OSystem *syst)
 	: Engine(syst), _fastMode(0), _debugger(0) {
@@ -84,6 +88,9 @@ SkyEngine::SkyEngine(OSystem *syst)
 	_systemVars->pastIntro      = false;
 	_systemVars->paused         = false;
 
+	memset (_chineseTraditionalOffsets, 0, sizeof(_chineseTraditionalOffsets));
+	_chineseTraditionalBlock = nullptr;
+
 	_action     = kSkyActionNone;
 	_skyLogic   = nullptr;
 	_skySound   = nullptr;
@@ -114,6 +121,8 @@ SkyEngine::~SkyEngine() {
 			free(_itemList[i]);
 
 	delete _systemVars;
+	delete [] _chineseTraditionalBlock;
+	_chineseTraditionalBlock = nullptr;
 }
 
 void SkyEngine::syncSoundSettings() {
@@ -273,6 +282,62 @@ Common::Error SkyEngine::go() {
 	return Common::kNoError;
 }
 
+static const struct {
+	// Identification
+	const char *md5; // File MD5
+	uint length; // File length
+	// Main section
+	// Offset from the beginning of file to virtual address 0
+	uint32 virtualBase;
+	// Offset to index of string sections
+	uint stringSectionIndexOffset;
+	// Offset to the font
+	uint fontOffset;
+	// Next one isn't strictly necessarry but makes logic simpler
+	// by allowing to read string block into memory as whole
+	// without any parsing. Just has to cover the block containing
+	// the strings. Reading more (up to whole file) is OK.
+	// End of strings block.
+	uint stringBlockEnd;
+} chineseExes[] = {
+	{
+		// Identification
+		"7bc128ba9bfaecb9bb4ef328b756057a", 575538,
+		// Main
+		0x5191, 0x6427e, 0x54afc,
+		// Value to simplify code
+		0x7eee1
+	}
+};
+
+bool SkyEngine::loadChineseTraditional() {
+	Common::File skyExe;
+	if (!skyExe.open("sky.exe"))
+		return false;
+	uint length = skyExe.size();
+	Common::String md5 = Common::computeStreamMD5AsString(skyExe, length);
+
+	for (uint i = 0; i < ARRAYSIZE(chineseExes); i++) {
+		if (md5 == chineseExes[i].md5 && length == chineseExes[i].length) {
+			skyExe.seek(chineseExes[i].stringSectionIndexOffset);
+			for (uint j = 0; j < 8; j++)
+				_chineseTraditionalOffsets[j] = skyExe.readUint32LE() + chineseExes[i].virtualBase;
+			uint stringBlockOffset = _chineseTraditionalOffsets[0];
+			for (uint j = 1; j < 8; j++)
+				stringBlockOffset = MIN(_chineseTraditionalOffsets[j], stringBlockOffset);
+			for (uint j = 0; j < 8; j++)
+				_chineseTraditionalOffsets[j] -= stringBlockOffset;
+			uint stringBlockLen = chineseExes[i].stringBlockEnd - stringBlockOffset;
+			_chineseTraditionalBlock = new char[stringBlockLen];
+			skyExe.seek(stringBlockOffset);
+			skyExe.read(_chineseTraditionalBlock, stringBlockLen);
+			return true;
+		}
+	}
+
+	return false;
+}
+
 Common::Error SkyEngine::init() {
 	initGraphics(320, 200);
 
@@ -355,12 +420,21 @@ Common::Error SkyEngine::init() {
 	case Common::EN_GRB:
 		_systemVars->language = SKY_ENGLISH;
 		break;
+	case Common::ZH_TWN:
+		_systemVars->language = SKY_CHINESE_TRADITIONAL;
+		break;
+
 	default:
 		_systemVars->language = SKY_ENGLISH;
 		break;
 	}
 
-	if (!_skyDisk->fileExists(60600 + SkyEngine::_systemVars->language * 8)) {
+	if (_systemVars->language == SKY_CHINESE_TRADITIONAL && !loadChineseTraditional()) {
+		_systemVars->language = SKY_ENGLISH;
+	}
+
+	if (_systemVars->language != SKY_CHINESE_TRADITIONAL &&
+	    !_skyDisk->fileExists(60600 + SkyEngine::_systemVars->language * 8)) {
 		warning("The language you selected does not exist in your BASS version");
 		if (_skyDisk->fileExists(60600))
 			SkyEngine::_systemVars->language = SKY_ENGLISH; // default to GB english if it exists..
diff --git a/engines/sky/sky.h b/engines/sky/sky.h
index 6bac9d52ef3..27f7d0d611f 100644
--- a/engines/sky/sky.h
+++ b/engines/sky/sky.h
@@ -109,10 +109,13 @@ public:
 	static void *_itemList[300];
 	static SystemVars *_systemVars;
 	static const char *shortcutsKeymapId;
+	static uint32 _chineseTraditionalOffsets[8];
+	static char *_chineseTraditionalBlock;
 
 protected:
 	// Engine APIs
 	Common::Error init();
+	bool loadChineseTraditional();
 	Common::Error go();
 	Common::Error run() override {
 		Common::Error err;
diff --git a/engines/sky/skydefs.h b/engines/sky/skydefs.h
index 69ee47bb9d7..fc2459112da 100644
--- a/engines/sky/skydefs.h
+++ b/engines/sky/skydefs.h
@@ -45,6 +45,8 @@ namespace Sky {
 #define SKY_PORTUGUESE	6
 #define SKY_SPANISH		7
 #define SKY_RUSSIAN		8
+// Special treatment
+#define SKY_CHINESE_TRADITIONAL	0x7f
 
 #define ST_COLLISION_BIT	5
 
diff --git a/engines/sky/text.cpp b/engines/sky/text.cpp
index f71f3a9bbdb..2279497d2c0 100644
--- a/engines/sky/text.cpp
+++ b/engines/sky/text.cpp
@@ -121,6 +121,24 @@ void Text::getText(uint32 textNr) { //load text #"textNr" into textBuffer
 
 	uint32 sectionNo = (textNr & 0x0F000) >> 12;
 
+	if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL) {
+		uint32 sectionOffset = SkyEngine::_chineseTraditionalOffsets[sectionNo];
+		const char *ptr = SkyEngine::_chineseTraditionalBlock + sectionOffset;
+		uint nrInBlock = textNr & 0xFFF;
+		if (sectionNo != 7)
+			nrInBlock--;
+		for (uint32 i = 0; i < nrInBlock; i++) {
+			while (*ptr)
+				ptr++;
+			ptr++;
+		}
+		char *dest = (char *)_textBuffer;
+		while (*ptr)
+			*dest++ = *ptr++;
+		*dest = 0;
+		return;
+	}
+
 	if (SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] == NULL) { //check if already loaded
 		debug(5, "Loading Text item(s) for Section %d", (sectionNo >> 2));
 
@@ -453,6 +471,8 @@ void Text::initHuffTree() {
 }
 
 bool Text::patchMessage(uint32 textNum) {
+	if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL)
+		return false;
 	uint16 patchIdx = _patchLangIdx[SkyEngine::_systemVars->language];
 	uint16 patchNum = _patchLangNum[SkyEngine::_systemVars->language];
 	for (uint16 cnt = 0; cnt < patchNum; cnt++) {


Commit: 16a7aec9461b5fe7af4ded0ec50f07d062fb509d
    https://github.com/scummvm/scummvm/commit/16a7aec9461b5fe7af4ded0ec50f07d062fb509d
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-11-29T00:45:18+01:00

Commit Message:
SKY: Support Chinese word-wrapping

Changed paths:
    engines/sky/control.cpp
    engines/sky/control.h
    engines/sky/text.cpp
    engines/sky/text.h


diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp
index ac3f6bbb797..cd555b899e9 100644
--- a/engines/sky/control.cpp
+++ b/engines/sky/control.cpp
@@ -176,7 +176,7 @@ void ControlStatus::setToText(const char *newText) {
 		_statusText->flushForRedraw();
 		free(_textData);
 	}
-	DisplayedText disText = _skyText->displayText(tmpLine, NULL, true, STATUS_WIDTH, 255);
+	DisplayedText disText = _skyText->displayText(tmpLine, sizeof(tmpLine), NULL, true, STATUS_WIDTH, 255);
 	_textData = (DataFileHeader *)disText.textData;
 	_statusText->setSprite(_textData);
 	_statusText->drawToScreen(WITH_MASK);
@@ -373,7 +373,7 @@ void Control::buttonControl(ConResource *pButton) {
 		if (pButton->_text) {
 			DisplayedText textRes;
 			if (pButton->_text == 0xFFFF) // text for autosave button
-				textRes = _skyText->displayText(autoSave, NULL, false, PAN_LINE_WIDTH, 255);
+				textRes = _skyText->displayText(autoSave, sizeof(autoSave), NULL, false, PAN_LINE_WIDTH, 255);
 			else
 				textRes = _skyText->displayText(pButton->_text, NULL, false, PAN_LINE_WIDTH, 255);
 			_textSprite = (DataFileHeader *)textRes.textData;
@@ -625,14 +625,14 @@ uint16 Control::handleClick(ConResource *pButton) {
 		return QUIT_PANEL;
 	case RESTART:
 		animClick(pButton);
-		if (getYesNo(restart)) {
+		if (getYesNo(restart, sizeof(restart))) {
 			restartGame();
 			return GAME_RESTORED;
 		} else
 			return 0;
 	case QUIT_TO_DOS:
 		animClick(pButton);
-		if (getYesNo(quitDos))
+		if (getYesNo(quitDos, sizeof(quitDos)))
 			Engine::quitGame();
 		return 0;
 	default:
@@ -640,7 +640,7 @@ uint16 Control::handleClick(ConResource *pButton) {
 	}
 }
 
-bool Control::getYesNo(char *text) {
+bool Control::getYesNo(char *text, uint bufSize) {
 	bool retVal = false;
 	bool quitPanel = false;
 	uint8 mouseType = MOUSE_NORMAL;
@@ -650,7 +650,7 @@ bool Control::getYesNo(char *text) {
 
 	_yesNo->drawToScreen(WITH_MASK);
 	if (text) {
-		DisplayedText dlgLtm = _skyText->displayText(text, NULL, true, _yesNo->_spriteData->s_width - 8, 37);
+		DisplayedText dlgLtm = _skyText->displayText(text, bufSize, NULL, true, _yesNo->_spriteData->s_width - 8, 37);
 		dlgTextDat = (DataFileHeader *)dlgLtm.textData;
 		textY = MPNL_Y + 44 + (28 - dlgTextDat->s_height) / 2;
 	} else
@@ -1067,7 +1067,7 @@ void Control::setUpGameSprites(const Common::StringArray &saveGameNames, DataFil
 	char cursorChar[2] = "-";
 	DisplayedText textSpr;
 	if (!nameSprites[MAX_ON_SCREEN]) {
-		textSpr = _skyText->displayText(cursorChar, NULL, false, 15, 0);
+		textSpr = _skyText->displayText(cursorChar, sizeof(cursorChar), NULL, false, 15, 0);
 		nameSprites[MAX_ON_SCREEN] = (DataFileHeader *)textSpr.textData;
 	}
 	for (uint16 cnt = 0; cnt < MAX_ON_SCREEN; cnt++) {
@@ -1075,10 +1075,10 @@ void Control::setUpGameSprites(const Common::StringArray &saveGameNames, DataFil
 
 		if (firstNum + cnt == selectedGame) {
 			Common::sprintf_s(nameBuf, "%3d: %s", firstNum + cnt + 1, dirtyString.c_str());
-			textSpr = _skyText->displayText(nameBuf, NULL, false, PAN_LINE_WIDTH, 0);
+			textSpr = _skyText->displayText(nameBuf, sizeof(nameBuf), NULL, false, PAN_LINE_WIDTH, 0);
 		} else {
 			Common::sprintf_s(nameBuf, "%3d: %s", firstNum + cnt + 1, saveGameNames[firstNum + cnt].c_str());
-			textSpr = _skyText->displayText(nameBuf, NULL, false, PAN_LINE_WIDTH, 37);
+			textSpr = _skyText->displayText(nameBuf, sizeof(nameBuf), NULL, false, PAN_LINE_WIDTH, 37);
 		}
 		nameSprites[cnt] = (DataFileHeader *)textSpr.textData;
 		if (firstNum + cnt == selectedGame) {
@@ -1619,14 +1619,14 @@ void Control::showGameQuitMsg() {
 	screenData = _skyScreen->giveCurrent();
 
 	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
-		_skyText->displayText(_quitTexts[8 * 2 + 0], textBuf1, true, 320, 255);
-		_skyText->displayText(_quitTexts[8 * 2 + 1], textBuf2, true, 320, 255);
+		_skyText->displayText(_quitTexts[8 * 2 + 0], sizeof(_quitTexts[8 * 2 + 0]), textBuf1, true, 320, 255);
+		_skyText->displayText(_quitTexts[8 * 2 + 1], sizeof(_quitTexts[8 * 2 + 1]), textBuf2, true, 320, 255);
 	} else if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL) { // Not translated in original
-		_skyText->displayText(_quitTexts[0], textBuf1, true, 320, 255);
-		_skyText->displayText(_quitTexts[1], textBuf2, true, 320, 255);
+		_skyText->displayText(_quitTexts[0], sizeof(_quitTexts[0]), textBuf1, true, 320, 255);
+		_skyText->displayText(_quitTexts[1], sizeof(_quitTexts[1]), textBuf2, true, 320, 255);
 	} else {
-		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 0], textBuf1, true, 320, 255);
-		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 1], textBuf2, true, 320, 255);
+		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 0], sizeof(_quitTexts[SkyEngine::_systemVars->language * 2 + 0]), textBuf1, true, 320, 255);
+		_skyText->displayText(_quitTexts[SkyEngine::_systemVars->language * 2 + 1], sizeof(_quitTexts[SkyEngine::_systemVars->language * 2 + 1]), textBuf2, true, 320, 255);
 	}
 	uint8 *curLine1 = textBuf1 + sizeof(DataFileHeader);
 	uint8 *curLine2 = textBuf2 + sizeof(DataFileHeader);
diff --git a/engines/sky/control.h b/engines/sky/control.h
index da2508eeefd..4d17f020b63 100644
--- a/engines/sky/control.h
+++ b/engines/sky/control.h
@@ -213,7 +213,7 @@ private:
 	void delay(unsigned int amount);
 
 	void animClick(ConResource *pButton);
-	bool getYesNo(char *text);
+	bool getYesNo(char *text, uint bufSize);
 	void buttonControl(ConResource *pButton);
 	uint16 handleClick(ConResource *pButton);
 	uint16 doMusicSlide();
diff --git a/engines/sky/text.cpp b/engines/sky/text.cpp
index 2279497d2c0..ef1427fc73b 100644
--- a/engines/sky/text.cpp
+++ b/engines/sky/text.cpp
@@ -250,10 +250,11 @@ char Text::getTextChar(uint8 **data, uint32 *bitPos) {
 DisplayedText Text::displayText(uint32 textNum, uint8 *dest, bool center, uint16 pixelWidth, uint8 color) {
 	//Render text into buffer *dest
 	getText(textNum);
-	return displayText(_textBuffer, dest, center, pixelWidth, color);
+	return displayText(_textBuffer, sizeof(_textBuffer), dest, center, pixelWidth, color);
 }
 
-DisplayedText Text::displayText(char *textPtr, uint8 *dest, bool center, uint16 pixelWidth, uint8 color) {
+// TODO: Don't use caller-supplied buffer for editing operations
+DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, bool center, uint16 pixelWidth, uint8 color) {
 	//Render text pointed to by *textPtr in buffer *dest
 	uint32 centerTable[10];
 	uint16 lineWidth = 0;
@@ -278,23 +279,44 @@ DisplayedText Text::displayText(char *textPtr, uint8 *dest, bool center, uint16
 	char *curPos = textPtr;
 	char *lastSpace = textPtr;
 	uint8 textChar = (uint8)*curPos++;
+	bool isBig5 = SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL;
 
 	while (textChar >= 0x20) {
-		if ((_curCharSet == 1) && (textChar >= 0x80))
-			textChar = 0x20;
+		bool isDoubleChar = false;
+		int oldLineWidth = lineWidth;
+		if (isBig5 && (textChar & 0x80)) {
+			isDoubleChar = true;
+			curPos++;
+			lineWidth += SkyEngine::kChineseTraditionalWidth;
+		} else {
+			if ((_curCharSet == 1) && (textChar >= 0x80))
+				textChar = 0x20;
+
+			textChar -= 0x20;
+			if (textChar == 0) {
+				lastSpace = curPos; //keep track of last space
+				centerTable[numLines] = lineWidth;
+			}
 
-		textChar -= 0x20;
-		if (textChar == 0) {
-			lastSpace = curPos; //keep track of last space
-			centerTable[numLines] = lineWidth;
+			lineWidth += _characterSet[textChar];	//add character width
+			lineWidth += (uint16)_dtCharSpacing;	//include character spacing
 		}
 
-		lineWidth += _characterSet[textChar];	//add character width
-		lineWidth += (uint16)_dtCharSpacing;	//include character spacing
-
 		if (pixelWidth <= lineWidth) {
-			if (*(lastSpace-1) == 10)
-				error("line width exceeded");
+			// If no space is found just break here. This is common in e.g. Chinese
+			// that doesn't use spaces.
+			if (lastSpace == textPtr || *(lastSpace-1) == 10) {
+				curPos -= isDoubleChar ? 2 : 1;
+				if (curPos < textPtr)
+					curPos = textPtr;
+				if (strlen(textPtr) + 2 >= bufLen)
+					error("Ran out of buffer size when word-wrapping");
+				// Add a place for linebreak
+				memmove(curPos + 1, curPos, textPtr + bufLen - curPos - 2);
+				textPtr[bufLen - 1] = 0;
+				lastSpace = curPos + 1;
+				centerTable[numLines] = oldLineWidth;
+			}
 
 			*(lastSpace-1) = 10;
 			lineWidth = 0;
@@ -399,7 +421,7 @@ void Text::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, ui
 
 DisplayedText Text::lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool center) {
 	getText(textNum);
-	DisplayedText textInfo = displayText(_textBuffer, NULL, center, width, color);
+	DisplayedText textInfo = displayText(_textBuffer, sizeof(_textBuffer), NULL, center, width, color);
 
 	uint32 compactNum = FIRST_TEXT_COMPACT;
 	Compact *cpt = _skyCompact->fetchCpt(compactNum);
diff --git a/engines/sky/text.h b/engines/sky/text.h
index 80156dc81cc..0273d4e127e 100644
--- a/engines/sky/text.h
+++ b/engines/sky/text.h
@@ -49,7 +49,7 @@ public:
 	Text(Disk *skyDisk, SkyCompact *skyCompact);
 	~Text();
 	struct DisplayedText displayText(uint32 textNum, uint8 *dest, bool center, uint16 pixelWidth, uint8 color);
-	struct DisplayedText displayText(char *textPtr, uint8 *dest, bool center, uint16 pixelWidth, uint8 color);
+	struct DisplayedText displayText(char *textPtr, uint bufLen, uint8 *dest, bool center, uint16 pixelWidth, uint8 color);
 	struct DisplayedText lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool center);
 	void fnSetFont(uint32 fontNr);
 	void fnTextModule(uint32 textInfoId, uint32 textNo);


Commit: 676a765068c1bbb609f72d10430b73e7f3d1445b
    https://github.com/scummvm/scummvm/commit/676a765068c1bbb609f72d10430b73e7f3d1445b
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-11-29T00:45:18+01:00

Commit Message:
SKY: Add Chinese renderer

Changed paths:
    engines/sky/sky.cpp
    engines/sky/sky.h
    engines/sky/text.cpp
    engines/sky/text.h


diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
index 1fe092cbe6b..7512a64b776 100644
--- a/engines/sky/sky.cpp
+++ b/engines/sky/sky.cpp
@@ -74,6 +74,8 @@ SystemVars *SkyEngine::_systemVars = nullptr;
 const char *SkyEngine::shortcutsKeymapId = "sky-shortcuts";
 uint32 SkyEngine::_chineseTraditionalOffsets[8];
 char *SkyEngine::_chineseTraditionalBlock;
+Common::Array<SkyEngine::ChineseTraditionalGlyph> SkyEngine::_chineseTraditionalFont;
+Common::Array<int> SkyEngine::_chineseTraditionalIndex;
 
 SkyEngine::SkyEngine(OSystem *syst)
 	: Engine(syst), _fastMode(0), _debugger(0) {
@@ -123,6 +125,9 @@ SkyEngine::~SkyEngine() {
 	delete _systemVars;
 	delete [] _chineseTraditionalBlock;
 	_chineseTraditionalBlock = nullptr;
+
+	_chineseTraditionalFont.clear();
+	_chineseTraditionalIndex.clear();
 }
 
 void SkyEngine::syncSoundSettings() {
@@ -282,6 +287,39 @@ Common::Error SkyEngine::go() {
 	return Common::kNoError;
 }
 
+void SkyEngine::ChineseTraditionalGlyph::makeOutline() {
+	outline[0][0] = 0;
+	outline[0][1] = 0;
+	// OR into outline the original bitmap moved by 1 pixel
+	// 1 pixel down
+	for (int y = 0; y < SkyEngine::kChineseTraditionalHeight - 1; y++) {
+		outline[y+1][0] = bitmap[y][0];
+		outline[y+1][1] = bitmap[y][1];
+	}
+	// 1 pixel up
+	for (int y = 0; y < SkyEngine::kChineseTraditionalHeight - 1; y++) {
+		outline[y][0] |= bitmap[y+1][0];
+		outline[y][1] |= bitmap[y+1][1];
+	}
+	for (int y = 0; y < SkyEngine::kChineseTraditionalHeight; y++) {
+		// 1 pixel right
+		outline[y][0] |= bitmap[y][0] >> 1;
+		outline[y][1] |= bitmap[y][0] << 7;
+		outline[y][1] |= bitmap[y][1] >> 1;
+
+		// 1 pixel left
+		outline[y][0] |= bitmap[y][0] << 1;
+		outline[y][0] |= bitmap[y][1] >> 7;
+		outline[y][1] |= bitmap[y][1] << 1;
+	}
+
+	// Then AND-out the original bitmap
+	for (int y = 0; y < SkyEngine::kChineseTraditionalHeight; y++) {
+		outline[y][0] &= ~bitmap[y][0];
+		outline[y][1] &= ~bitmap[y][1];
+	}
+}
+
 static const struct {
 	// Identification
 	const char *md5; // File MD5
@@ -331,6 +369,23 @@ bool SkyEngine::loadChineseTraditional() {
 			_chineseTraditionalBlock = new char[stringBlockLen];
 			skyExe.seek(stringBlockOffset);
 			skyExe.read(_chineseTraditionalBlock, stringBlockLen);
+
+			skyExe.seek(chineseExes[i].fontOffset);
+			_chineseTraditionalIndex = Common::move(Common::Array<int>(0x8000, -1));
+			// So far the only version had 1981 glyphs. Optimize a little bit for this number
+			// but don't rely on it in any way
+			_chineseTraditionalFont.reserve(1981);
+			while(1) {
+				// Big-endian because it's not really a u16 but a big5 sequence.
+				uint16 ch = skyExe.readUint16BE();
+				ChineseTraditionalGlyph glyph;
+				if (ch == 0xffff)
+					break;
+				skyExe.read(&glyph.bitmap, sizeof(glyph.bitmap));
+				glyph.makeOutline();
+				_chineseTraditionalIndex[ch & 0x7fff] = _chineseTraditionalFont.size();
+				_chineseTraditionalFont.push_back(glyph);
+			}
 			return true;
 		}
 	}
diff --git a/engines/sky/sky.h b/engines/sky/sky.h
index 27f7d0d611f..2606dadf98d 100644
--- a/engines/sky/sky.h
+++ b/engines/sky/sky.h
@@ -23,6 +23,7 @@
 #define SKY_SKY_H
 
 
+#include "common/array.h"
 #include "common/error.h"
 #include "common/keyboard.h"
 #include "engines/engine.h"
@@ -111,6 +112,16 @@ public:
 	static const char *shortcutsKeymapId;
 	static uint32 _chineseTraditionalOffsets[8];
 	static char *_chineseTraditionalBlock;
+	static const int kChineseTraditionalWidth = 16;
+	static const int kChineseTraditionalHeight = 15;
+	struct ChineseTraditionalGlyph {
+		byte bitmap[kChineseTraditionalHeight][kChineseTraditionalWidth / 8];
+		byte outline[kChineseTraditionalHeight][kChineseTraditionalWidth / 8];
+
+		void makeOutline();
+	};
+	static Common::Array<ChineseTraditionalGlyph> _chineseTraditionalFont;
+	static Common::Array<int> _chineseTraditionalIndex;
 
 protected:
 	// Engine APIs
diff --git a/engines/sky/text.cpp b/engines/sky/text.cpp
index ef1427fc73b..a1fe2ed8df6 100644
--- a/engines/sky/text.cpp
+++ b/engines/sky/text.cpp
@@ -335,7 +335,8 @@ DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, bool
 	if (numLines > MAX_NO_LINES)
 		error("Maximum no. of lines exceeded");
 
-	uint32 dtLineSize = pixelWidth * _charHeight;
+	int charHeight = isBig5 ? MAX<int>(_charHeight, SkyEngine::kChineseTraditionalHeight) : _charHeight;
+	uint32 dtLineSize = pixelWidth * charHeight;
 	uint32 numBytes = (dtLineSize * numLines) + sizeof(DataFileHeader) + 4;
 
 	if (!dest)
@@ -346,8 +347,8 @@ DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, bool
 
 	//make the header
 	((DataFileHeader *)dest)->s_width = pixelWidth;
-	((DataFileHeader *)dest)->s_height = (uint16)(_charHeight * numLines);
-	((DataFileHeader *)dest)->s_sp_size = (uint16)(pixelWidth * _charHeight * numLines);
+	((DataFileHeader *)dest)->s_height = (uint16)(charHeight * numLines);
+	((DataFileHeader *)dest)->s_sp_size = (uint16)(pixelWidth * charHeight * numLines);
 	((DataFileHeader *)dest)->s_offset_x = 0;
 	((DataFileHeader *)dest)->s_offset_y = 0;
 
@@ -367,7 +368,12 @@ DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, bool
 
 		textChar = (uint8)*curPos++;
 		while (textChar >= 0x20) {
-			makeGameCharacter(textChar - 0x20, _characterSet, curDest, color, pixelWidth);
+			if (isBig5 && (textChar & 0x80)) {
+				uint8 trail = *curPos++;
+				uint16 fullCh = (textChar << 8) | trail;
+				makeChineseGameCharacter(fullCh, _characterSet, curDest, color, pixelWidth);
+			} else
+				makeGameCharacter(textChar - 0x20, _characterSet, curDest, color, pixelWidth);
 			textChar = *curPos++;
 		}
 
@@ -382,6 +388,29 @@ DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, bool
 	return ret;
 }
 
+void Text::makeChineseGameCharacter(uint16 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch) {
+	int glyphIdx = SkyEngine::_chineseTraditionalIndex[textChar & 0x7fff];
+	if (glyphIdx < 0) {
+		makeGameCharacter('?' - 0x20, charSetPtr, dest, color, bufPitch);
+		return;
+	}
+
+	const SkyEngine::ChineseTraditionalGlyph& glyph = SkyEngine::_chineseTraditionalFont[glyphIdx];
+
+	for (int y = 0; y < SkyEngine::kChineseTraditionalHeight; y++) {
+		uint8 *cur = dest + y * bufPitch;
+
+		for (int byte = 0; byte < 2; byte++)
+			for (int bit = 0; bit < 8; bit++, cur++)
+				if ((glyph.bitmap[y][byte] << bit) & 0x80)
+					*cur = color;
+				else if ((glyph.outline[y][byte] << bit) & 0x80)
+					*cur = 240;
+	}
+	//update position
+	dest += SkyEngine::kChineseTraditionalWidth;
+}
+
 void Text::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch) {
 	bool maskBit, dataBit;
 	uint8 charWidth = (uint8)((*(charSetPtr + textChar)) + 1 - _dtCharSpacing);
diff --git a/engines/sky/text.h b/engines/sky/text.h
index 0273d4e127e..42f12f3a242 100644
--- a/engines/sky/text.h
+++ b/engines/sky/text.h
@@ -66,6 +66,7 @@ private:
 	char getTextChar(uint8 **data, uint32 *bitPos);
 	bool getTextBit(uint8 **data, uint32 *bitPos);
 	void makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&data, uint8 color, uint16 bufPitch);
+	void makeChineseGameCharacter(uint16 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch);
 
 	bool patchMessage(uint32 textNum);
 


Commit: b00b6fc7602ce700cf1cd5c192926f245155f264
    https://github.com/scummvm/scummvm/commit/b00b6fc7602ce700cf1cd5c192926f245155f264
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-11-29T00:45:18+01:00

Commit Message:
NEWS: Add an entry for Chinese BASS support

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 720627abf33..5bb4682527f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -86,6 +86,9 @@ For a more comprehensive changelog of the latest experimental code, see:
  Sherlock:
    - Added support for Chinese Rose Tattoo.
 
+ Sky:
+   - Added support for Chinese Beneath a steel sky
+
  Toon:
    - Made game menus behave like in the original.
 




More information about the Scummvm-git-logs mailing list