[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