From b60febe8a81120a21c2346bb8bc4255038b695e8 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 23 Oct 2004 23:08:53 +0000 Subject: Patch #1048283 (Improved CJK / FM-TOWNS support & some documention) svn-id: r15671 --- scumm/charset.cpp | 213 ++++++++++++++++++++++++++++++--------------- scumm/charset.h | 23 ++++- scumm/nut_renderer.cpp | 18 +--- scumm/smush/smush_font.cpp | 17 +++- scumm/string.cpp | 52 +++++++++-- 5 files changed, 227 insertions(+), 96 deletions(-) (limited to 'scumm') diff --git a/scumm/charset.cpp b/scumm/charset.cpp index 257e7b332d..76128abc7f 100644 --- a/scumm/charset.cpp +++ b/scumm/charset.cpp @@ -26,20 +26,37 @@ namespace Scumm { void ScummEngine::loadCJKFont() { + File fp; _useCJKMode = false; - if ((_gameId == GID_DIG || _gameId == GID_CMI) && (_language == Common::KO_KOR || _language == Common::JA_JPN || _language == Common::ZH_TWN)) { - File fp; + if (_language == Common::JA_JPN && _version <= 5) { // FM-TOWNS v3 / v5 Kanji + int numChar = 256 * 32; + _2byteWidth = 16; + _2byteHeight = 16; + // use FM-TOWNS font rom, since game files don't have kanji font resources + if (fp.open("fmt_fnt.rom", File::kFileReadMode)) { + _useCJKMode = true; + debug(2, "Loading FM-TOWNS Kanji rom"); + _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar]; + fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar); + fp.close(); + } + } else if (_language == Common::KO_KOR || _language == Common::JA_JPN || _language == Common::ZH_TWN) { + int numChar = 0; const char *fontFile = NULL; + switch (_language) { case Common::KO_KOR: fontFile = "korean.fnt"; + numChar = 2350; break; case Common::JA_JPN: fontFile = (_gameId == GID_DIG) ? "kanji16.fnt" : "japanese.fnt"; + numChar = 1024; //FIXME break; case Common::ZH_TWN: if (_gameId == GID_CMI) { fontFile = "chinese.fnt"; + numChar = 1; //FIXME } break; default: @@ -52,49 +69,23 @@ void ScummEngine::loadCJKFont() { _2byteWidth = fp.readByte(); _2byteHeight = fp.readByte(); - int numChar = 0; - switch (_language) { - case Common::KO_KOR: - numChar = 2350; - break; - case Common::JA_JPN: - numChar = (_gameId == GID_DIG) ? 1024 : 2048; //FIXME - break; - case Common::ZH_TWN: - numChar = 1; //FIXME - break; - default: - break; - } - _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar]; - fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar); - fp.close(); - } - } else if (_language == Common::JA_JPN && _version == 5) { // FM-TOWNS Kanji - File fp; - int numChar = 256 * 32; - _2byteWidth = 16; - _2byteHeight = 16; - // use FM-TOWNS font rom, since game files don't have kanji font resources - if (fp.open("fmt_fnt.rom")) { - _useCJKMode = true; - debug(2, "Loading FM-TOWNS Kanji rom"); _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar]; fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar); fp.close(); + } else { + warning("Couldn't load any font"); } } } -static int SJIStoFMTChunk(int f, int s) //convert sjis code to fmt font offset -{ +static int SJIStoFMTChunk(int f, int s) { //converts sjis code to fmt font offset enum { KANA = 0, KANJI = 1, EKANJI = 2 }; - int base = s - (s % 32) - 1; - int c = 0, p = 0, chunk_f = 0, chunk = 0, cr, kanjiType = KANA; + int base = s - ((s + 1) % 32); + int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA; if (f >= 0x81 && f <= 0x84) kanjiType = KANA; if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI; @@ -115,10 +106,15 @@ static int SJIStoFMTChunk(int f, int s) //convert sjis code to fmt font offset chunk_f = c + 2 * p; } + // Base corrections if (base == 0x7f && s == 0x7f) - base -= 0x20; //correction - if ((base == 0x7f && s == 0x9e) || (base == 0x9f && s == 0xbe) || (base == 0xbf && s == 0xde)) - base += 0x20; //correction + base -= 0x20; + if (base == 0x9f && s == 0xbe) + base += 0x20; + if (base == 0xbf && s == 0xde) + base += 0x20; + //if (base == 0x7f && s == 0x9e) + // base += 0x20; switch (base) { case 0x3f: @@ -158,9 +154,11 @@ static int SJIStoFMTChunk(int f, int s) //convert sjis code to fmt font offset else if (kanjiType == EKANJI) chunk = 144; break; default: + warning("Invaild Char! f %x s %x base %x c %d p %d", f, s, base, c, p); return 0; } + debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr); return ((chunk_f + chunk) * 32 + (s - base)) + cr; } @@ -238,10 +236,24 @@ void CharsetRendererV3::setCurID(byte id) { _fontPtr += _numChars; } +int CharsetRendererCommon::getFontHeight() { + if(_vm->_useCJKMode) + return MAX(_vm->_2byteHeight + 1, (int)_fontPtr[1]); + else + return _fontPtr[1]; +} + +int CharsetRendererV3::getFontHeight() { + if(_vm->_useCJKMode) + return MAX(_vm->_2byteHeight + 1, 8); + else + return 8; +} + // do spacing for variable width old-style font int CharsetRendererClassic::getCharWidth(byte chr) { if (chr >= 0x80 && _vm->_useCJKMode) - return 6; + return _vm->_2byteWidth / 2; int spacing = 0; int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); @@ -276,6 +288,10 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) { if (chr == 0xD) break; if (chr == 254 || chr == 255) { + //process in LE + if(chr == 254 && checkKSCode(text[pos], chr) && _vm->_useCJKMode) { + goto loc_avoid_ks_fe; + } chr = text[pos++]; if (chr == 3) // 'WAIT' break; @@ -299,7 +315,13 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) { continue; } } - width += getCharWidth(chr); +loc_avoid_ks_fe: + if ((chr & 0x80) && _vm->_useCJKMode) { + pos++; + width += _vm->_2byteWidth; + } else { + width += getCharWidth(chr); + } } setCurID(oldID); @@ -333,6 +355,10 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { } else if (chr == '@') continue; if (chr == 254 || chr == 255) { + //process in LE + if(chr == 254 && checkKSCode(str[pos], chr) && _vm->_useCJKMode) { + goto loc_avoid_ks_fe; + } chr = str[pos++]; if (chr == 3) // 'Wait' break; @@ -366,7 +392,13 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { if (chr == ' ') lastspace = pos - 1; - curw += getCharWidth(chr); +loc_avoid_ks_fe: + if ((chr & 0x80) && _vm->_useCJKMode) { + pos++; + curw += _vm->_2byteWidth; + } else { + curw += getCharWidth(chr); + } if (lastspace == -1) continue; if (curw > maxwidth) { @@ -1105,6 +1137,8 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language) } int CharsetRendererV3::getCharWidth(byte chr) { + if (chr & 0x80 && _vm->_useCJKMode) + return _vm->_2byteWidth / 2; int spacing = 0; spacing = *(_widthTable + chr); @@ -1129,10 +1163,10 @@ void CharsetRendererV3::setColor(byte color) void CharsetRendererV3::printChar(int chr) { // Indy3 / Zak256 / Loom + int width, height, origWidth, origHeight; VirtScreen *vs; - byte *char_ptr, *dest_ptr; - int width, height; - int drawTop; + byte *charPtr, *dst; + int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; checkRange(_vm->_numCharsets - 1, 0, _curId, "Printing with bad charset %d"); @@ -1142,6 +1176,25 @@ void CharsetRendererV3::printChar(int chr) { if (chr == '@') return; + if (is2byte) { + charPtr = _vm->get2byteCharPtr(chr); + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + } else { + charPtr = _fontPtr + chr * 8; +// width = height = 8; + width = getCharWidth(chr); + height = 8; + } + + origWidth = width; + origHeight = height; + + if (_dropShadow) { + width++; + height++; + } + if (_firstChar) { _str.left = _left; _str.top = _top; @@ -1150,14 +1203,7 @@ void CharsetRendererV3::printChar(int chr) { _firstChar = false; } - width = height = 8; - if (_dropShadow) { - width++; - height++; - } - - drawTop = _top - vs->topline; - char_ptr = _fontPtr + chr * 8; + int drawTop = _top - vs->topline; _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height); @@ -1166,17 +1212,17 @@ void CharsetRendererV3::printChar(int chr) { _textScreenID = vs->number; } if (_ignoreCharsetMask || !vs->hasTwoBuffers) { - dest_ptr = vs->getPixels(_left, drawTop); - drawBits1(*vs, dest_ptr, char_ptr, drawTop, 8, 8); + dst = vs->getPixels(_left, drawTop); + drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight); } else { - dest_ptr = (byte *)_vm->gdi._textSurface.pixels + _top * _vm->gdi._textSurface.pitch + _left; - drawBits1(_vm->gdi._textSurface, dest_ptr, char_ptr, drawTop, 8, 8); + dst = (byte *)_vm->gdi._textSurface.pixels + _top * _vm->gdi._textSurface.pitch + _left; + drawBits1(_vm->gdi._textSurface, dst, charPtr, drawTop, origWidth, origHeight); } if (_str.left > _left) _str.left = _left; - _left += getCharWidth(chr); + _left += origWidth; if (_str.right < _left) { _str.right = _left; @@ -1189,10 +1235,21 @@ void CharsetRendererV3::printChar(int chr) { } void CharsetRendererV3::drawChar(int chr, const Graphics::Surface &s, int x, int y) { - byte *char_ptr, *dest_ptr; - char_ptr = _fontPtr + chr * 8; - dest_ptr = (byte *)s.pixels + y * s.pitch + x; - drawBits1(s, dest_ptr, char_ptr, y, 8, 8); + byte *charPtr, *dst; + int width, height; + int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; + if (is2byte) { + charPtr = _vm->get2byteCharPtr(chr); + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + } else { + charPtr = _fontPtr + chr * 8; +// width = height = 8; + width = getCharWidth(chr); + height = 8; + } + dst = (byte *)s.pixels + y * s.pitch + x; + drawBits1(s, dst, charPtr, y, width, height); } @@ -1216,6 +1273,7 @@ void CharsetRendererClassic::printChar(int chr) { int type = *_fontPtr; if (is2byte) { _dropShadow = true; + _shadowColor = (_vm->_features & GF_FMTOWNS) ? 8 : 0; charPtr = _vm->get2byteCharPtr(chr); width = _vm->_2byteWidth; height = _vm->_2byteHeight; @@ -1404,20 +1462,35 @@ void CharsetRendererClassic::printChar(int chr) { void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x, int y) { const byte *charPtr; byte *dst; + int width, height; + int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; - uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x10000); - if (!charOffs) - return; - charPtr = _fontPtr + charOffs; - - int width = charPtr[0]; - int height = charPtr[1]; + if (is2byte) { + _dropShadow = true; + _shadowColor = (_vm->_features & GF_FMTOWNS) ? 8 : 0; + charPtr = _vm->get2byteCharPtr(chr); + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + } else { + uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); + assert(charOffs < 0x10000); + if (!charOffs) + return; + charPtr = _fontPtr + charOffs; + + width = charPtr[0]; + height = charPtr[1]; - charPtr += 4; // Skip over char header + charPtr += 4; // Skip over char header + } dst = (byte *)s.pixels + y * s.pitch + x; - drawBitsN(s, dst, charPtr, *_fontPtr, y, width, height); + + if (is2byte) { + drawBits1(s, dst, charPtr, y, width, height); + } else { + drawBitsN(s, dst, charPtr, *_fontPtr, y, width, height); + } } void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) { @@ -1540,7 +1613,7 @@ void CharsetRendererNut::printChar(int chr) { int height = _current->getCharHeight(chr); if (chr >= 256 && _vm->_useCJKMode) - width = 16; + width = _vm->_2byteWidth; shadow.right = _left + width + 2; shadow.bottom = _top + height + 2; diff --git a/scumm/charset.h b/scumm/charset.h index 66c506e73c..0e86f83470 100644 --- a/scumm/charset.h +++ b/scumm/charset.h @@ -31,6 +31,25 @@ class ScummEngine; class NutRenderer; struct VirtScreen; +static inline bool checkKSCode(byte hi, byte lo) { + //hi : xx + //lo : yy + if ((0xA1 > lo) || (0xFE < lo)) { + return false; + } + if ((hi >= 0xB0) && (hi <= 0xC8)) { + return true; + } + return false; +} + +static inline bool checkSJISCode(byte c) { + if ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea /* && c <= 0xff */)) + return false; + return true; +} + + class CharsetRenderer { public: @@ -94,7 +113,7 @@ public: void setCurID(byte id); - int getFontHeight() { return _fontPtr[1]; } + int getFontHeight(); }; class CharsetRendererClassic : public CharsetRendererCommon { @@ -122,7 +141,7 @@ public: void drawChar(int chr, const Graphics::Surface &s, int x, int y); void setCurID(byte id); void setColor(byte color); - int getFontHeight() { return 8; } + int getFontHeight(); int getCharWidth(byte chr); }; diff --git a/scumm/nut_renderer.cpp b/scumm/nut_renderer.cpp index 859578bb14..1b8c737248 100644 --- a/scumm/nut_renderer.cpp +++ b/scumm/nut_renderer.cpp @@ -204,13 +204,8 @@ int NutRenderer::getCharWidth(byte c) const { return 0; } - if (c >= 0x80 && _vm->_useCJKMode) { - if (_vm->_gameId == GID_CMI) - return 8; - if (_vm->_gameId == GID_DIG) - return 6; - return 0; - } + if (c >= 0x80 && _vm->_useCJKMode) + return _vm->_2byteWidth / 2; if (c >= _numChars) error("invalid character in NutRenderer::getCharWidth : %d (%d)", c, _numChars); @@ -225,13 +220,8 @@ int NutRenderer::getCharHeight(byte c) const { return 0; } - if (c >= 0x80 && _vm->_useCJKMode) { - if (_vm->_gameId == GID_CMI) - return 16; - if (_vm->_gameId == GID_DIG) - return 10; - return 0; - } + if (c >= 0x80 && _vm->_useCJKMode) + return _vm->_2byteHeight; if (c >= _numChars) error("invalid character in NutRenderer::getCharHeight : %d (%d)", c, _numChars); diff --git a/scumm/smush/smush_font.cpp b/scumm/smush/smush_font.cpp index 188dfd6773..b830a78e82 100644 --- a/scumm/smush/smush_font.cpp +++ b/scumm/smush/smush_font.cpp @@ -43,7 +43,11 @@ int SmushFont::getStringWidth(const char *str) { int width = 0; while (*str) { - width += getCharWidth(*str++); + if(*str & 0x80 && g_scumm->_useCJKMode) { + width += g_scumm->_2byteWidth + 1; + str += 2; + } else + width += getCharWidth(*str++); } return width; } @@ -118,18 +122,25 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) { int h = _vm->_2byteHeight; byte *src = _vm->get2byteCharPtr(idx); - byte *dst = buffer + dst_width * (y + (_vm->_gameId == GID_CMI ? 7 : 2)) + x; + byte *dst = buffer + dst_width * (y + (_vm->_gameId == GID_CMI ? 7 : (_vm->_gameId == GID_DIG ? 2 : 0))) + x; byte bits = 0; char color = (_color != -1) ? _color : 1; + if (_new_colors) - color = (char)0xff; //FIXME; + color = (char)0xff; + + if (g_scumm->_gameId == GID_FT) + color = 1; + for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { if ((i % 8) == 0) bits = *src++; if (bits & revBitMask[i % 8]) { dst[i + 1] = 0; + dst[dst_width + i] = 0; + dst[dst_width + i + 1] = 0; dst[i] = color; } } diff --git a/scumm/string.cpp b/scumm/string.cpp index 36f033fcdf..362864acff 100644 --- a/scumm/string.cpp +++ b/scumm/string.cpp @@ -103,6 +103,8 @@ void ScummEngine::CHARSET_1() { int code = (_heversion >= 80) ? 127 : 64; char value[32]; + bool cmi_pos_hack = false; + if (!_haveMsg) return; @@ -283,6 +285,11 @@ void ScummEngine::CHARSET_1() { warning("CHARSET_1: invalid code %d", c); } } else if (c == 0xFE || c == 0xFF) { + //WORKAROUND + //to avoid korean code 0xfe treated as charset message code. + if(c == 0xFE && checkKSCode(*(buffer + 1), c) && _useCJKMode) { + goto loc_avoid_ks_fe; + } c = *buffer++; switch (c) { case 1: @@ -343,13 +350,19 @@ void ScummEngine::CHARSET_1() { warning("CHARSET_1: invalid code %d", c); } } else { +loc_avoid_ks_fe: _charset->_left = _charset->_nextLeft; _charset->_top = _charset->_nextTop; if (c & 0x80 && _useCJKMode) - if (_language == 6 && ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea && c <= 0xff))) + if (_language == Common::JA_JPN && !checkSJISCode(c)) { c = 0x20; //not in S-JIS - else - c += *buffer++ * 256; + } else { + c += *buffer++ * 256; //LE + if(_gameId == GID_CMI) { //HACK: This fixes korean text position in COMI (off by 6 pixel) + cmi_pos_hack = true; + _charset->_top += 6; + } + } if (_version <= 3) { _charset->printChar(c); } else { @@ -363,6 +376,10 @@ void ScummEngine::CHARSET_1() { } else _charset->printChar(c); } + if(cmi_pos_hack) { + cmi_pos_hack = false; + _charset->_top -= 6; + } _charset->_nextLeft = _charset->_left; _charset->_nextTop = _charset->_top; @@ -389,6 +406,8 @@ void ScummEngine::drawString(int a, const byte *msg) { uint color; int code = (_heversion >= 80) ? 127 : 64; + bool cmi_pos_hack = false; + addMessageToStack(msg, buf, sizeof(buf)); _charset->_top = _string[a].ypos + _screenTop; @@ -491,10 +510,24 @@ void ScummEngine::drawString(int a, const byte *msg) { _charset->_blitAlso = true; } } - if (c >= 0x80 && _useCJKMode) - c += buf[i++] * 256; + if (c & 0x80 && _useCJKMode) { + if (_language == Common::JA_JPN && !checkSJISCode(c)) { + c = 0x20; //not in S-JIS + } else { + c += buf[i++] * 256; + if(_gameId == GID_CMI) { + cmi_pos_hack = true; + _charset->_top += 6; + } + } + } _charset->printChar(c); _charset->_blitAlso = false; + + if(cmi_pos_hack) { + cmi_pos_hack = false; + _charset->_top -= 6; + } } } @@ -729,8 +762,13 @@ void ScummEngine::drawBlastTexts() { do { c = *buf++; if (c != 0 && c != 0xFF) { - if (c >= 0x80 && _useCJKMode) - c += *buf++ * 256; + if (c & 0x80 && _useCJKMode) { + if (_language == Common::JA_JPN && !checkSJISCode(c)) { + c = 0x20; //not in S-JIS + } else { + c += *buf++ * 256; + } + } _charset->printChar(c); } } while (c); -- cgit v1.2.3