SWORD2: Fix support for Korean translation

This commit is contained in:
Youngjun, Choi 2023-12-07 17:33:54 +09:00 committed by Andrea Boscarino
parent 19466d352e
commit 2ea2e6d317
6 changed files with 114 additions and 9 deletions

View File

@ -81,6 +81,21 @@ static const ADGameDescription gameDescriptions[] = {
GUIO0()
},
{ // Korean fan translation
"sword2",
"",
AD_ENTRY6s("general.clu", "31db8564f9187538f24d9fda0677f666", 7059728,
"text.clu", "9b344d976ca8d19a1cf5aa4413397f6b", 304968,
"speech1.clu", "a403904a0e825356107d228f8f74092e", 176260048,
"docks.clu", "b39246fbb5b955a29f9a207c69bfc318", 20262263,
"bs2k.fnt", NULL, 1222000,
"korean.clu", NULL, -1),
Common::KO_KOR,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
{ // GOG.com release version 2.0.0.6
"sword2",
"GOG",

View File

@ -69,6 +69,9 @@ namespace Sword2 {
#define DUD 64 // the first "chequered flag" (dud) symbol in
// our character set is in the '@' position
#define KOREAN_CHAR_WIDTH 20
#define KOREAN_CHAR_HEIGHT 26
namespace {
Common::String readLine(Common::ReadStream &stream) {
Common::String ret = stream.readString('\n');
@ -204,6 +207,9 @@ uint16 FontRenderer::analyzeSentence(const byte *sentence, uint16 maxWidth, uint
if (isChinese && (ch & 0x80)) {
w = kChineseWidth + _charSpacing;
l = 2;
} else if (isKoreanChar(ch, sentence[pos+1], fontRes)) {
w += wcharWidth(ch, sentence[pos+1], fontRes) + _charSpacing;
l = 2;
} else {
w = charWidth(ch, fontRes) + _charSpacing;
l = 1;
@ -434,12 +440,21 @@ byte *FontRenderer::buildTextSprite(const byte *sentence, uint32 fontRes, uint8
continue;
}
byte *charPtr = findChar(ch, charSet);
byte *charPtr = NULL;
if (isKoreanChar(ch, *currTxtLine, fontRes)) {
charPtr = findWChar(ch, *currTxtLine++, charSet);
j++;
frame_head.width = KOREAN_CHAR_WIDTH;
frame_head.read(charPtr);
copyWChar(charPtr, spritePtr, spriteWidth, pen);
} else {
charPtr = findChar(ch, charSet);
assert(frame_head.height == char_height);
copyChar(charPtr, spritePtr, spriteWidth, pen);
frame_head.read(charPtr);
assert(frame_head.height == char_height);
copyChar(charPtr, spritePtr, spriteWidth, pen);
}
// We must remember to free memory for generated character in psx,
// as it is extracted differently than pc version (copyed from a
@ -490,6 +505,20 @@ uint16 FontRenderer::charWidth(byte ch, uint32 fontRes) {
return frame_head.width;
}
/**
* @param hi the KSX1001 code upper byte of the character
* @param lo the KSX1001 code lower byte of the character
* @param fontRes the font resource id
* @return the width of the character
*/
uint16 FontRenderer::wcharWidth(byte hi, byte lo, uint32 fontRes) {
if (isKoreanChar(hi, lo, fontRes)) {
return KOREAN_CHAR_WIDTH;
}
return charWidth(hi, fontRes) + charWidth(lo, fontRes);
}
/**
* @param fontRes the font resource id
* @return the height of a character sprite
@ -599,6 +628,17 @@ byte *FontRenderer::findChar(byte ch, byte *charSet) {
}
}
byte *FontRenderer::findWChar(byte hi, byte lo, byte *charSet) {
uint16 frameWidth = KOREAN_CHAR_WIDTH;
uint16 frameHeight = KOREAN_CHAR_HEIGHT;
FrameHeader frame_head;
byte *charPtr = findChar(0xFF, charSet);
frame_head.read(charPtr);
return charPtr + frame_head.size() + frame_head.width * frame_head.height + ((hi - 0xB0) * 94 + (lo - 0xA1)) * frameWidth * frameHeight;
}
/**
* Copies a character sprite to the sprite buffer.
* @param charPtr pointer to the character sprite
@ -617,6 +657,11 @@ void FontRenderer::copyChar(const byte *charPtr, byte *spritePtr, uint16 spriteW
copyCharRaw(charPtr + FrameHeader::size(), frame.width, frame.height, spritePtr, spriteWidth, pen);
}
void FontRenderer::copyWChar(const byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen) {
copyCharRaw(charPtr, KOREAN_CHAR_WIDTH, KOREAN_CHAR_HEIGHT, spritePtr, spriteWidth, pen);
}
void FontRenderer::copyCharRaw(const byte *source, uint16 charWidth, uint16 charHeight, byte *spritePtr, uint16 spriteWidth, uint8 pen) {
byte *rowPtr = spritePtr;
@ -658,6 +703,14 @@ void FontRenderer::copyCharRaw(const byte *source, uint16 charWidth, uint16 char
}
}
bool FontRenderer::isKoreanChar(byte hi, byte lo, uint32 fontRes) {
if (!_vm->_isKorTrs || fontRes != ENGLISH_SPEECH_FONT_ID)
return false;
if (hi >= 0xB0 && hi <= 0xC8 && lo >= 0xA1 && lo <= 0xFE)
return true;
return false;
}
// Distance to keep speech text from edges of screen
#define TEXT_MARGIN 12

View File

@ -103,10 +103,14 @@ private:
uint16 analyzeSentence(const byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line, bool isChinese);
byte *buildTextSprite(const byte *sentence, uint32 fontRes, uint8 pen, LineInfo *line, uint16 noOfLines, bool isChinese);
uint16 charWidth(byte ch, uint32 fontRes);
uint16 wcharWidth(byte hi, byte lo, uint32 fontRes);
uint16 charHeight(uint32 fontRes);
byte *findChar(byte ch, byte *charSet);
byte *findWChar(byte hi, byte lo, byte *charSet);
void copyChar(const byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen);
void copyWChar(const byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen);
void copyCharRaw(const byte *source, uint16 charWidth, uint16 charHeight, byte *spritePtr, uint16 spriteWidth, uint8 pen);
bool isKoreanChar(const byte hi, const byte lo, const uint32 fontRes);
public:
FontRenderer(Sword2Engine *vm) : _vm(vm) {

View File

@ -230,6 +230,9 @@ bool ResourceManager::init() {
return false;
}
// Use korean.clu instead of TEXT.CLU
if (_vm->_isKorTrs && !scumm_stricmp(_resFiles[i].fileName, "TEXT.CLU"))
strcpy_s(_resFiles[i].fileName, 20, "korean.clu");
_resFiles[i].cd = cdInf[j].cd;
}
}
@ -308,12 +311,38 @@ byte *ResourceManager::openResource(uint32 res, bool dump) {
debug(6, "res len %d", len);
// Ok, we know the length so try and allocate the memory.
_resList[res].ptr = _vm->_memory->memAlloc(len, res);
_resList[res].size = len;
_resList[res].refCount = 0;
if (res == ENGLISH_SPEECH_FONT_ID && _vm->_isKorTrs) {
// Load Korean Font
uint32 korFontSize = 0;
Common::File *korFontFile = NULL;
file->read(_resList[res].ptr, len);
korFontFile = new Common::File();
korFontFile->open("bs2k.fnt");
if (korFontFile->isOpen()) {
korFontSize = korFontFile->size();
}
// Ok, we know the length so try and allocate the memory.
_resList[res].ptr = _vm->_memory->memAlloc(len + korFontSize, res);
_resList[res].size = len + korFontSize;
_resList[res].refCount = 0;
file->read(_resList[res].ptr, len);
if (korFontSize > 0) {
korFontFile->read(_resList[res].ptr + len, korFontSize);
korFontFile->close();
}
len += korFontSize;
} else {
// Ok, we know the length so try and allocate the memory.
_resList[res].ptr = _vm->_memory->memAlloc(len, res);
_resList[res].size = len;
_resList[res].refCount = 0;
file->read(_resList[res].ptr, len);
}
debug(3, "Loaded resource '%s' (%d) from '%s' on CD %d (%d)", fetchName(_resList[res].ptr), res, _resFiles[cluFileNum].fileName, getCD(), _resFiles[cluFileNum].cd);

View File

@ -84,6 +84,8 @@ Sword2Engine::Sword2Engine(OSystem *syst, const ADGameDescription *gameDesc) : E
_gameSpeed = 1;
_gmmLoadSlot = -1; // Used to manage GMM Loading
_isKorTrs = gameDesc->language == Common::KO_KOR;
}
Sword2Engine::~Sword2Engine() {

View File

@ -198,6 +198,8 @@ public:
int32 _gameCycle;
bool _isKorTrs;
#if RIGHT_CLICK_CLEARS_LUGGAGE
bool heldIsInInventory();
#endif