[Scummvm-cvs-logs] scummvm master -> e3dbae886dc138433f07e4fd9e85b14254471359

athrxx athrxx at scummvm.org
Sat Jul 9 22:54:52 CEST 2011


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:
b1cb88ddc2 SCUMM: cleanup sjis font rendering code
e3dbae886d SCUMM: Make use of graphics/sjis code for LOOM PCE sjis drawing


Commit: b1cb88ddc25da64772bbba253bd2210678189ace
    https://github.com/scummvm/scummvm/commit/b1cb88ddc25da64772bbba253bd2210678189ace
Author: athrxx (athrxx at scummvm.org)
Date: 2011-07-09T11:07:55-07:00

Commit Message:
SCUMM: cleanup sjis font rendering code

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h



diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index eaae64d..8adeaa0 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -217,6 +217,9 @@ static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offse
 }
 
 byte *ScummEngine::get2byteCharPtr(int idx) {
+	if (_game.platform == Common::kPlatformFMTowns)
+		return 0;
+
 	switch (_language) {
 	case Common::KO_KOR:
 		idx = ((idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
@@ -314,7 +317,7 @@ CharsetRenderer::~CharsetRenderer() {
 
 CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm)
 	: CharsetRenderer(vm), _bytesPerPixel(0), _fontHeight(0), _numChars(0) {
-	_shadowMode = kNoShadowMode;
+	_shadowMode = false;
 	_shadowColor = 0;
 }
 
@@ -362,17 +365,9 @@ void CharsetRendererV3::setCurID(int32 id) {
 }
 
 int CharsetRendererCommon::getFontHeight() {
-	if (_vm->_useCJKMode) {
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 };
-			static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
-			static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
-			const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2);
-			return (_vm->_game.version == 3) ? 8 : htbl[_curId];
-		} else {
-			return MAX(_vm->_2byteHeight + 1, _fontHeight);
-		}
-	} else
+	if (_vm->_useCJKMode)
+		return MAX(_vm->_2byteHeight + 1, _fontHeight);
+	else
 		return _fontHeight;
 }
 
@@ -380,57 +375,16 @@ int CharsetRendererCommon::getFontHeight() {
 int CharsetRendererClassic::getCharWidth(uint16 chr) {
 	int spacing = 0;
 
- 	if (_vm->_useCJKMode) {
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			if ((chr & 0xff00) == 0xfd00) {
-				chr &= 0xff;
-			} else if (chr >= 256) {
-				spacing = 8;
-			} else if (useTownsFontRomCharacter(chr)) {
-				spacing = 4;
-			}
+ 	if (_vm->_useCJKMode && chr >= 0x80)
+		return _vm->_2byteWidth / 2;
 
-			if (spacing) {
-				if (_vm->_game.id == GID_MONKEY) {
-					spacing++;
-					if (_curId == 2)
-						spacing++;
-				} else if (_vm->_game.id != GID_INDY4 && _curId == 1) {
-					spacing++;
-				}
-			}
-
-		} else if (chr >= 0x80) {
-			return _vm->_2byteWidth / 2;
-		}
-	}
-
-	if (!spacing) {
-		int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
-		if (offs) {
-			spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
-		}
-	}
+	int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+	if (offs)
+		spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
 
 	return spacing;
 }
 
-bool CharsetRendererClassic::useTownsFontRomCharacter(uint16 chr) {
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	if (_vm->_game.platform != Common::kPlatformFMTowns || !_vm->_useCJKMode)
-		return false;
-
-	if (chr < 128) {
-		if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127))
-			return true;
-		return false;
-	}
-	return true;
-#else
-	return false;
-#endif
-}
-
 int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 	int pos = 0;
 	int width = 1;
@@ -608,22 +562,51 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 int CharsetRendererV3::getCharWidth(uint16 chr) {
 	int spacing = 0;
 
-	if (_vm->_useCJKMode) {
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			if (chr >= 256)
-				spacing = 8;
-			else if (chr >= 128)
-				spacing = 4;
-		} else if (chr & 0x80) {
-			spacing = _vm->_2byteWidth / 2;
+	if (_vm->_useCJKMode && (chr & 0x80))
+		spacing = _vm->_2byteWidth / 2;
+	
+	if (!spacing)
+		spacing = *(_widthTable + chr);
+
+	return spacing;
+}
+
+void CharsetRendererV3::enableShadow(bool enable) {
+	_shadowColor = 0;
+	_shadowMode = enable;
+}
+
+void CharsetRendererV3::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+	int y, x;
+	byte bits = 0;
+	uint8 col = _color;
+	int pitch = s.pitch - width * bitDepth;
+	byte *dst2 = dst + s.pitch;
+
+	for (y = 0; y < height && y + drawTop < s.h; y++) {
+		for (x = 0; x < width; x++) {
+			if ((x % 8) == 0)
+				bits = *src++;
+			if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) {
+				if (_shadowMode)
+					dst[1] = dst2[0] = dst2[1] = _shadowColor;
+				dst[0] = col;
+			}
+			dst += bitDepth;
+			dst2 += bitDepth;
 		}
-	}
 
-	if (!spacing) {
-		spacing = *(_widthTable + chr);
+		dst += pitch;
+		dst2 += pitch;
 	}
+}
 
-	return spacing;
+int CharsetRendererV3::getDrawWidthIntern(uint16 chr) {
+	return getCharWidth(chr);
+}
+
+int CharsetRendererV3::getDrawHeightIntern(uint16) {
+	return 8;
 }
 
 void CharsetRendererV3::setColor(byte color) {
@@ -662,43 +645,6 @@ void CharsetRendererPCE::setColor(byte color) {
 }
 #endif
 
-void CharsetRendererCommon::enableShadow(bool enable) {
-	if (enable) {
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			_shadowColor = 8;
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-			_shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
-			if (_vm->_cjkFont) {
-				if (_vm->_game.version == 5) {
-					if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) ||
-						((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) ||
-						((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) {
-							_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
-					} else {
-						_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
-					}
-					_vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3);
-				} else {
-					_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode);
-				}
-			}
-#endif
-			_shadowMode = kFMTOWNSShadowMode;
-		} else {
-			_shadowColor = 0;
-			_shadowMode = kNormalShadowMode;
-		}
-	} else {
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		if (_vm->_cjkFont) {
-			_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
-			_vm->_cjkFont->toggleFlippedMode(false);
-		}
-#endif
-		_shadowMode = kNoShadowMode;
-	}
-}
-
 void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 	// WORKAROUND for bug #1509509: Indy3 Mac does not show black
 	// characters (such as in the grail diary) if ignoreCharsetMask
@@ -721,24 +667,10 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 	if (chr == '@')
 		return;
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	if (_vm->_useCJKMode && chr > 127) {
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			charPtr = 0;
-			width = _vm->_cjkFont->getCharWidth(chr);
-			height = _vm->_cjkFont->getFontHeight();
-		} else {
-			width = _vm->_2byteWidth;
-			height = _vm->_2byteHeight;
-			charPtr = _vm->get2byteCharPtr(chr);
-		}
-	} else
-#endif
-	{
-		charPtr = _fontPtr + chr * 8;
-		width = getCharWidth(chr);
-		height = 8;
-	}
+	charPtr = (_vm->_useCJKMode && chr > 127) ? _vm->get2byteCharPtr(chr) : _fontPtr + chr * 8;
+	width = getDrawWidthIntern(chr);
+	height = getDrawHeightIntern(chr);
+	setDrawCharIntern(chr);
 
 	// Clip at the right side (to avoid drawing "outside" the screen bounds).
 	if (_left + origWidth > _right + 1)
@@ -747,7 +679,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 	origWidth = width;
 	origHeight = height;
 
-	if (_shadowMode != kNoShadowMode) {
+	if (_shadowMode) {
 		width++;
 		height++;
 	}
@@ -769,30 +701,17 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 		_textScreenID = vs->number;
 	}
 
-	if (
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		(_vm->_game.platform != Common::kPlatformFMTowns) &&
-#endif
-		(ignoreCharsetMask || !vs->hasTwoBuffers)) {
+	if ((ignoreCharsetMask || !vs->hasTwoBuffers)) {
 		dst = vs->getPixels(_left, drawTop);
-		if (charPtr)
-			drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->format.bytesPerPixel);
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		else if (_vm->_cjkFont)
-			_vm->_cjkFont->drawChar(*vs, chr, _left, drawTop, _color, _shadowColor);
-#endif
+		drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->format.bytesPerPixel);
 	} else {
 		dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier);
-		if (charPtr)
-			drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.format.bytesPerPixel, (_vm->_textSurfaceMultiplier == 2 && !is2byte));
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		else if (_vm->_cjkFont)
-			_vm->_cjkFont->drawChar(_vm->_textSurface, chr, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, _color, _shadowColor);
-#endif
-		if (is2byte) {
-			origWidth /= _vm->_textSurfaceMultiplier;
-			height /= _vm->_textSurfaceMultiplier;
-		}
+		drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.format.bytesPerPixel);
+	}
+	
+	if (is2byte) {
+		origWidth /= _vm->_textSurfaceMultiplier;
+		height /= _vm->_textSurfaceMultiplier;
 	}
 
 	if (_str.left > _left)
@@ -802,7 +721,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 
 	if (_str.right < _left) {
 		_str.right = _left;
-		if (_shadowMode != kNoShadowMode)
+		if (_shadowMode)
 			_str.right++;
 	}
 
@@ -811,30 +730,12 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 }
 
 void CharsetRendererV3::drawChar(int chr, Graphics::Surface &s, int x, int y) {
-	const byte *charPtr;
-	byte *dst;
-	int width, height;
-	int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
-	if (is2byte) {
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			_vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor);
-			return;
-		}
-		else
-#endif
-		{
-			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;
+	const byte *charPtr = (_vm->_useCJKMode && chr > 127) ? _vm->get2byteCharPtr(chr) : _fontPtr + chr * 8;
+	int width = getDrawWidthIntern(chr);
+	int height = getDrawHeightIntern(chr);
+	setDrawCharIntern(chr);
+
+	byte *dst = (byte *)s.pixels + y * s.pitch + x;
 	drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel);
 }
 
@@ -853,29 +754,6 @@ void CharsetRenderer::translateColor() {
 	}
 }
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-void CharsetRenderer::processTownsCharsetColors(uint8 bytesPerPixel) {
-	if (_vm->_game.platform == Common::kPlatformFMTowns) {
-		for (int i = 0; i < (1 << bytesPerPixel); i++) {
-			uint8 c = _vm->_charsetColorMap[i];
-
-			if (c > 16) {
-				uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12;
-				t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10);
-				t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9);
-				c = t;
-			}
-
-			if (c == 0)
-				c = _vm->_townsOverrideShadowColor;
-
-			c = ((c & 0x0f) << 4) | (c & 0x0f);
-			_vm->_townsCharsetColorMap[i] = c;
-		}
-	}
-}
-#endif
-
 void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {
 	static const SaveLoadEntry charsetRendererEntries[] = {
 		MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)),
@@ -893,10 +771,7 @@ void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {
 }
 
 void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
-	int width, height, origWidth, origHeight;
-	int offsX, offsY;
 	VirtScreen *vs;
-	const byte *charPtr;
 	bool is2byte = (chr >= 256 && _vm->_useCJKMode);
 
 	assertRange(1, _curId, _vm->_numCharsets - 1, "charset");
@@ -911,64 +786,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
 
 	_vm->_charsetColorMap[1] = _color;
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	processTownsCharsetColors(_bytesPerPixel);
-	bool noSjis = false;
-
-	if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) {
-		if ((chr & 0x00ff) == 0x00fd) {
-			chr >>= 8;
-			noSjis = true;
-		}
-	}
-
-	if (useTownsFontRomCharacter(chr) && !noSjis) {
-		charPtr = 0;
-		_vm->_cjkChar = chr;
-		enableShadow(true);
-
-		width = getCharWidth(chr);
-		// For whatever reason MI1 uses a different font width
-		// for alignment calculation and for drawing when
-		// charset 2 is active. This fixes some subtle glitches.
-		if (_vm->_game.id == GID_MONKEY && _curId == 2)
-			width--;
-		origWidth = width;
-
-		origHeight = height = getFontHeight();
-		offsX = offsY = 0;
-	} else if (_vm->_useCJKMode && (chr >= 128) && !noSjis) {
-		enableShadow(true);
-		origWidth = width = _vm->_2byteWidth;
-		origHeight = height = _vm->_2byteHeight;
-		charPtr = _vm->get2byteCharPtr(chr);
-		offsX = offsY = 0;
-		if (_shadowMode != kNoShadowMode) {
-			width++;
-			height++;
-		}
-	} else
-#endif
-	{
-		uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
-		assert(charOffs < 0x14000);
-		if (!charOffs)
-			return;
-		charPtr = _fontPtr + charOffs;
-
-		width = origWidth = charPtr[0];
-		height = origHeight = charPtr[1];
-
-		if (_disableOffsX) {
-			offsX = 0;
-		} else {
-			offsX = (signed char)charPtr[2];
-		}
-
-		offsY = (signed char)charPtr[3];
-
-		charPtr += 4;	// Skip over char header
-	}
+	prepareDraw(chr);
 
 	if (_firstChar) {
 		_str.left = 0;
@@ -977,12 +795,12 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
 		_str.bottom = 0;
 	}
 
-	_top += offsY;
-	_left += offsX;
+	_top += _offsY;
+	_left += _offsX;
 
-	if (_left + origWidth > _right + 1 || _left < 0) {
-		_left += origWidth;
-		_top -= offsY;
+	if (_left + _origWidth > _right + 1 || _left < 0) {
+		_left += _origWidth;
+		_top -= _offsY;
 		return;
 	}
 
@@ -1004,33 +822,29 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
 
 	int drawTop = _top - vs->topline;
 
-	_vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
+	_vm->markRectAsDirty(vs->number, _left, _left + _width, drawTop, drawTop + _height);
 
 	// This check for kPlatformFMTowns and kMainVirtScreen is at least required for the chat with
 	// the navigator's head in front of the ghost ship in Monkey Island 1
-	if (!ignoreCharsetMask
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		|| (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)
-#endif
-		) {
+	if (!ignoreCharsetMask || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)) {
 		_hasMask = true;
 		_textScreenID = vs->number;
 	}
 
-	printCharIntern(is2byte, charPtr, origWidth, origHeight, width, height, vs, ignoreCharsetMask);
+	printCharIntern(is2byte, _charPtr, _origWidth, _origHeight, _width, _height, vs, ignoreCharsetMask);
 
-	_left += origWidth;
+	_left += _origWidth;
 
 	if (_str.right < _left) {
 		_str.right = _left;
-		if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode != kNoShadowMode)
+		if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode)
 			_str.right++;
 	}
 
-	if (_str.bottom < _top + origHeight)
-		_str.bottom = _top + origHeight;
+	if (_str.bottom < _top + _origHeight)
+		_str.bottom = _top + _origHeight;
 
-	_top -= offsY;
+	_top -= _offsY;
 }
 
 void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask) {
@@ -1068,11 +882,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 	} else {
 		Graphics::Surface dstSurface;
 		Graphics::Surface backSurface;
-		if ((ignoreCharsetMask || !vs->hasTwoBuffers)
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-			&& (_vm->_game.platform != Common::kPlatformFMTowns)
-#endif
-			) {
+		if ((ignoreCharsetMask || !vs->hasTwoBuffers)) {
 			dstSurface = *vs;
 			dstPtr = vs->getPixels(_left, drawTop);
 		} else {
@@ -1091,16 +901,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 			drawTop = _top - _vm->_screenTop;
 		}
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		if (!charPtr && _vm->_cjkFont) {
-			_vm->_cjkFont->drawChar(dstSurface, _vm->_cjkChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor);
-		} else
-#endif
-		if (is2byte) {
-			drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight, dstSurface.format.bytesPerPixel);
-		} else {
-			drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight, _vm->_textSurfaceMultiplier == 2);
-		}
+		drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight);
 
 		if (_blitAlso && vs->hasTwoBuffers) {
 			// FIXME: Revisiting this code, I think the _blitAlso mode is likely broken
@@ -1139,54 +940,34 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 	}
 }
 
-void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) {
-	const byte *charPtr;
-	byte *dst;
-	int width, height;
-	int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
-
-	if (is2byte) {
-		enableShadow(true);
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			_vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor);
-			return;
-		} else
-#endif
-		{
-			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;
+void CharsetRendererClassic::prepareDraw(uint16 chr) {
+	uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+	assert(charOffs < 0x14000);
+	if (!charOffs)
+		return;
+	_charPtr = _fontPtr + charOffs;
 
-		width = charPtr[0];
-		height = charPtr[1];
+	_width = _origWidth = _charPtr[0];
+	_height = _origHeight = _charPtr[1];
 
-		charPtr += 4;	// Skip over char header
+	if (_disableOffsX) {
+		_offsX = 0;
+	} else {
+		_offsX = (signed char)_charPtr[2];
 	}
 
-	dst = (byte *)s.pixels + y * s.pitch + x;
+	_offsY = (signed char)_charPtr[3];
 
-	if (is2byte) {
-		drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel);
-	} else {
-		drawBitsN(s, dst, charPtr, *_fontPtr, y, width, height);
-	}
+	_charPtr += 4;	// Skip over char header
 }
 
-void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height,
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	bool scale2x) {
-#else
-	bool) {
-#endif
+void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) {
+	prepareDraw(chr);
+	byte *dst = (byte *)s.pixels + y * s.pitch + x;
+	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) {
 	int y, x;
 	int color;
 	byte numbits, bits;
@@ -1198,38 +979,13 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
 	numbits = 8;
 	byte *cmap = _vm->_charsetColorMap;
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	byte *dst2 = dst;
-
-	if (_vm->_game.platform == Common::kPlatformFMTowns)
-		cmap = _vm->_townsCharsetColorMap;
-	if (scale2x) {
-		dst2 += s.pitch;
-		pitch <<= 1;
-	}
-#endif
-
 	for (y = 0; y < height && y + drawTop < s.h; y++) {
 		for (x = 0; x < width; x++) {
 			color = (bits >> (8 - bpp)) & 0xFF;
 
-			if (color && y + drawTop >= 0) {
+			if (color && y + drawTop >= 0)
 				*dst = cmap[color];
-
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-				if (scale2x)
-					dst[1] = dst2[0] = dst2[1] = dst[0];
-#endif
-			}
 			dst++;
-
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-			if (scale2x) {
-				dst++;
-				dst2 += 2;
-			}
-#endif
-
 			bits <<= bpp;
 			numbits -= bpp;
 			if (numbits == 0) {
@@ -1238,52 +994,89 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
 			}
 		}
 		dst += pitch;
+	}
+}
+
+CharsetRendererTownsV3::CharsetRendererTownsV3(ScummEngine *vm) : CharsetRendererV3(vm), _sjisCurChar(0) {
+}
+
+int CharsetRendererTownsV3::getCharWidth(uint16 chr) {
+	int spacing = 0;
+
+	if (_vm->_useCJKMode) {
+		if (chr >= 256)
+			spacing = 8;
+		else if (chr >= 128)
+			spacing = 4;
+	}
+
+	if (!spacing)
+		spacing = *(_widthTable + chr);
+
+	return spacing;
+}
+
+int CharsetRendererTownsV3::getFontHeight() {
+	return _vm->_useCJKMode ? 8 : _fontHeight;
+}
+
+void CharsetRendererTownsV3::enableShadow(bool enable) {
+	_shadowColor = 8;
+	_shadowMode = enable;
+
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-		dst2 += pitch;
+	_shadowColor = 0x88;
+	if (_vm->_cjkFont)
+		_vm->_cjkFont->setDrawingMode(enable ? Graphics::FontSJIS::kFMTownsShadowMode : Graphics::FontSJIS::kDefaultMode);
 #endif
-	}
 }
 
-void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth,
+void CharsetRendererTownsV3::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	bool scale2x) {
+	if (_sjisCurChar) {
+		assert(_vm->_cjkFont);
+		_vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, _color, _shadowColor);
+		return;
+	}
+
+	dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier);
+	int sfPitch = _vm->_textSurface.pitch;
+	int sfHeight = _vm->_textSurface.h;
+	bool scale2x = (_vm->_textSurfaceMultiplier == 2 && !(_sjisCurChar >= 256 && _vm->_useCJKMode));
 #else
-	bool) {
+	int sfPitch = s.pitch;
+	int sfHeight = s.h;
 #endif
 
 	int y, x;
 	byte bits = 0;
 	uint8 col = _color;
-	int pitch = s.pitch - width * bitDepth;
-	byte *dst2 = dst + s.pitch;
+	int pitch = sfPitch - width * bitDepth;
+	byte *dst2 = dst + sfPitch;
 
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 	byte *dst3 = dst2;
 	byte *dst4 = dst2;
 	if (scale2x) {
-		dst3 = dst2 + s.pitch;
-		dst4 = dst3 + s.pitch;
+		dst3 = dst2 + sfPitch;
+		dst4 = dst3 + sfPitch;
 		pitch <<= 1;
 	}
-	if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5)
-		col = _vm->_townsCharsetColorMap[1];
 #endif
 
-	for (y = 0; y < height && y + drawTop < s.h; y++) {
+	for (y = 0; y < height && y + drawTop < sfHeight; y++) {
 		for (x = 0; x < width; x++) {
 			if ((x % 8) == 0)
 				bits = *src++;
 			if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) {
 				if (bitDepth == 2) {
-					if (_shadowMode != kNoShadowMode) {
+					if (_shadowMode) {
 						WRITE_UINT16(dst + 2, _vm->_16BitPalette[_shadowColor]);
-						WRITE_UINT16(dst + s.pitch, _vm->_16BitPalette[_shadowColor]);
-						if (_shadowMode != kFMTOWNSShadowMode)
-							WRITE_UINT16(dst + s.pitch + 2, _vm->_16BitPalette[_shadowColor]);
+						WRITE_UINT16(dst + sfPitch, _vm->_16BitPalette[_shadowColor]);
 					}
 					WRITE_UINT16(dst, _vm->_16BitPalette[_color]);
 				} else {
-					if (_shadowMode != kNoShadowMode) {
+					if (_shadowMode) {
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 						if (scale2x) {
 							dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor;
@@ -1292,8 +1085,6 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con
 #endif
 						{
 							dst[1] = dst2[0] = _shadowColor;
-							if (_shadowMode != kFMTOWNSShadowMode)
-								dst2[1] = _shadowColor;
 						}
 					}
 					dst[0] = col;
@@ -1324,9 +1115,30 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con
 #endif
 	}
 }
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+int CharsetRendererTownsV3::getDrawWidthIntern(uint16 chr) {
+	if (_vm->_useCJKMode && chr > 127) {
+		assert(_vm->_cjkFont);
+		return _vm->_cjkFont->getCharWidth(chr);
+	}
+	return CharsetRendererV3::getDrawWidthIntern(chr);
+}
+
+int CharsetRendererTownsV3::getDrawHeightIntern(uint16 chr) {
+	if (_vm->_useCJKMode && chr > 127) {
+		assert(_vm->_cjkFont);
+		return _vm->_cjkFont->getFontHeight();
+	}
+	return CharsetRendererV3::getDrawHeightIntern(chr);
+}
+
+void CharsetRendererTownsV3::setDrawCharIntern(uint16 chr) {
+	_sjisCurChar = (_vm->_useCJKMode && chr > 127) ? chr : 0;
+}
+#endif
 
 #ifdef USE_RGB_COLOR
-void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
+void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
 	int y, x;
 	int bitCount = 0;
 	byte bits = 0;
@@ -1341,14 +1153,12 @@ void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const
 				bits = *src++;
 			if ((bits & revBitMask(bitCount % 8)) && y + drawTop >= 0) {
 				if (bitDepth == 2) {
-					if (_shadowMode != kNoShadowMode) {
+					if (_shadowMode)
 						WRITE_UINT16(dst + s.pitch + 2, _vm->_16BitPalette[_shadowColor]);
-					}
 					WRITE_UINT16(dst, _vm->_16BitPalette[_color]);
 				} else {
-					if (_shadowMode != kNoShadowMode) {
+					if (_shadowMode)
 						*(dst + s.pitch + 1) = _shadowColor;
-					}
 					*dst = _color;
 				}
 			}
@@ -1359,6 +1169,18 @@ void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const
 		dst += s.pitch - width * bitDepth;
 	}
 }
+
+int CharsetRendererPCE::getDrawWidthIntern(uint16 chr) {
+	if (_vm->_useCJKMode && chr > 127)
+		return  _vm->_2byteWidth;
+	return CharsetRendererV3::getDrawWidthIntern(chr);
+}
+
+int CharsetRendererPCE::getDrawHeightIntern(uint16 chr) {
+	if (_vm->_useCJKMode && chr > 127)
+		return _vm->_2byteHeight;
+	return CharsetRendererV3::getDrawHeightIntern(chr);
+}
 #endif
 
 #ifdef ENABLE_SCUMM_7_8
@@ -1533,7 +1355,7 @@ void CharsetRendererNES::printChar(int chr, bool ignoreCharsetMask) {
 
 	if (_str.right < _left) {
 		_str.right = _left;
-		if (_shadowMode != kNoShadowMode)
+		if (_shadowMode)
 			_str.right++;
 	}
 
@@ -1556,7 +1378,203 @@ void CharsetRendererNES::drawChar(int chr, Graphics::Surface &s, int x, int y) {
 	drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel);
 }
 
-void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+CharsetRendererTownsClassic::CharsetRendererTownsClassic(ScummEngine *vm) : CharsetRendererClassic(vm), _sjisCurChar(0) {
+}
+
+int CharsetRendererTownsClassic::getCharWidth(uint16 chr) {
+	int spacing = 0;
+
+ 	if (_vm->_useCJKMode) {
+		if ((chr & 0xff00) == 0xfd00) {
+			chr &= 0xff;
+		} else if (chr >= 256) {
+			spacing = 8;
+		} else if (useFontRomCharacter(chr)) {
+			spacing = 4;
+		}
+
+		if (spacing) {
+			if (_vm->_game.id == GID_MONKEY) {
+				spacing++;
+				if (_curId == 2)
+					spacing++;
+			} else if (_vm->_game.id != GID_INDY4 && _curId == 1) {
+				spacing++;
+			}
+		}
+	}
+
+	if (!spacing) {
+		int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+		if (offs)
+			spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
+	}
+
+	return spacing;
+}
+
+int CharsetRendererTownsClassic::getFontHeight() {
+	static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 };
+	static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
+	static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
+	const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2);
+	return _vm->_useCJKMode ? htbl[_curId] : _fontHeight;
+}
+
+void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) {
+	if (_sjisCurChar) {
+		assert(_vm->_cjkFont);
+		_vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor);
+		return;
+	}
+	
+	bool scale2x = (_vm->_textSurfaceMultiplier == 2);
+	dst = (byte *)_vm->_textSurface.pixels + (_top - _vm->_screenTop) * _vm->_textSurface.pitch * _vm->_textSurfaceMultiplier + _left * _vm->_textSurfaceMultiplier;
+
+	int y, x;
+	int color;
+	byte numbits, bits;
+
+	int pitch = _vm->_textSurface.pitch - width;
+
+	assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
+	bits = *src++;
+	numbits = 8;
+	byte *cmap = _vm->_charsetColorMap;
+	byte *dst2 = dst;
+
+	if (_vm->_game.platform == Common::kPlatformFMTowns)
+		cmap = _vm->_townsCharsetColorMap;
+	if (scale2x) {
+		dst2 += _vm->_textSurface.pitch;
+		pitch <<= 1;
+	}
+
+	for (y = 0; y < height && y + drawTop < _vm->_textSurface.h; y++) {
+		for (x = 0; x < width; x++) {
+			color = (bits >> (8 - bpp)) & 0xFF;
+
+			if (color && y + drawTop >= 0) {
+				*dst = cmap[color];
+				if (scale2x)
+					dst[1] = dst2[0] = dst2[1] = dst[0];
+			}
+			dst++;
+
+			if (scale2x) {
+				dst++;
+				dst2 += 2;
+			}
+
+			bits <<= bpp;
+			numbits -= bpp;
+			if (numbits == 0) {
+				bits = *src++;
+				numbits = 8;
+			}
+		}
+		dst += pitch;
+		dst2 += pitch;
+	}
+}
+
+void CharsetRendererTownsClassic::prepareDraw(uint16 chr) {
+	processCharsetColors();
+	bool noSjis = false;
+
+	if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) {
+		if ((chr & 0x00ff) == 0x00fd) {
+			chr >>= 8;
+			noSjis = true;
+		}
+	}
+
+	if (useFontRomCharacter(chr) && !noSjis) {
+		setupShadowMode();
+		_charPtr = 0;
+		_sjisCurChar = chr;
+
+		_width = getCharWidth(chr);
+		// For whatever reason MI1 uses a different font width
+		// for alignment calculation and for drawing when
+		// charset 2 is active. This fixes some subtle glitches.
+		if (_vm->_game.id == GID_MONKEY && _curId == 2)
+			_width--;
+		_origWidth = _width;
+
+		_origHeight = _height = getFontHeight();
+		_offsX = _offsY = 0;
+	} else if (_vm->_useCJKMode && (chr >= 128) && !noSjis) {
+		setupShadowMode();
+		_origWidth = _width = _vm->_2byteWidth;
+		_origHeight = _height = _vm->_2byteHeight;
+		_charPtr = _vm->get2byteCharPtr(chr);
+		_offsX = _offsY = 0;
+		if (_shadowMode) {
+			_width++;
+			_height++;
+		}
+	} else {
+		_sjisCurChar = 0;
+		CharsetRendererClassic::prepareDraw(chr);
+	}
+}
+
+void CharsetRendererTownsClassic::setupShadowMode() {
+	_shadowMode = true;
+	_shadowColor = _vm->_townsCharsetColorMap[0];
+	assert(_vm->_cjkFont);
+
+	if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) ||
+		((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) ||
+		((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) {
+			_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
+	} else {
+		_vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+	}
+
+	_vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3);
+}
+
+bool CharsetRendererTownsClassic::useFontRomCharacter(uint16 chr) {
+	if (!_vm->_useCJKMode)
+		return false;
+
+	// Some SCUMM 5 games contain hard coded logic to determine whether to use
+	// the SCUMM fonts or the FM-Towns font rom to draw a character. For the other
+	// games we will simply check for a character greater 127.
+	if (chr < 128) {
+		if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127))
+			return true;
+		return false;
+	}
+	return true;
+}
+
+void CharsetRendererTownsClassic::processCharsetColors() {
+	for (int i = 0; i < (1 << _bytesPerPixel); i++) {
+		uint8 c = _vm->_charsetColorMap[i];
+
+		if (c > 16) {
+			uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12;
+			t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10);
+			t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9);
+			c = t;
+		}
+
+		if (c == 0)
+			c = _vm->_townsOverrideShadowColor;
+
+		c = ((c & 0x0f) << 4) | (c & 0x0f);
+		_vm->_townsCharsetColorMap[i] = c;
+	}
+}
+#endif
+#endif
+
+void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
 	for (int i = 0; i < 8; i++) {
 		byte c0 = src[i];
 		byte c1 = src[i + 8];
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 4c657b4..13e40ff 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -78,10 +78,6 @@ public:
 	void addLinebreaks(int a, byte *str, int pos, int maxwidth);
 	void translateColor();
 
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	void processTownsCharsetColors(uint8 bytesPerPixel);
-#endif
-
 	virtual void setCurID(int32 id) = 0;
 	int getCurID() { return _curId; }
 
@@ -101,31 +97,26 @@ protected:
 	int _fontHeight;
 	int _numChars;
 
-	enum ShadowMode {
-		kNoShadowMode,
-		kFMTOWNSShadowMode,
-		kNormalShadowMode
-	};
 	byte _shadowColor;
-	ShadowMode _shadowMode;
-
-	void enableShadow(bool enable);
-	virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
-
+	bool _shadowMode;
 
 public:
 	CharsetRendererCommon(ScummEngine *vm);
 
 	void setCurID(int32 id);
 
-	int getFontHeight();
+	virtual int getFontHeight();
 };
 
 class CharsetRendererClassic : public CharsetRendererCommon {
 protected:
-	void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, bool scale2x = false);
+	virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
+	void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);	
+	virtual void prepareDraw(uint16 chr);
 
-	void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);
+	int _width, _height, _origWidth, _origHeight;
+	int _offsX, _offsY;
+	const byte *_charPtr;
 
 public:
 	CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {}
@@ -134,18 +125,34 @@ public:
 	void drawChar(int chr, Graphics::Surface &s, int x, int y);
 
 	int getCharWidth(uint16 chr);
+};
+
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+class CharsetRendererTownsClassic : public CharsetRendererClassic {
+public:
+	CharsetRendererTownsClassic(ScummEngine *vm);
+
+	int getCharWidth(uint16 chr);
+	int getFontHeight();
+
+private:
+	void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
+	void prepareDraw(uint16 chr);
+	void setupShadowMode();
+	bool useFontRomCharacter(uint16 chr);
+	void processCharsetColors();
 
-	// Some SCUMM 5 games contain hard coded logic to determine whether to use
-	// the SCUMM fonts or the FM-Towns font rom to draw a character. For the other
-	// games we will simply check for a character greater 127.
-	bool useTownsFontRomCharacter(uint16 chr);
+	uint16 _sjisCurChar;
 };
+#endif
+#endif
 
 class CharsetRendererNES : public CharsetRendererCommon {
 protected:
 	byte *_trTable;
 
-	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
+	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
 
 public:
 	CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {}
@@ -160,6 +167,12 @@ public:
 
 class CharsetRendererV3 : public CharsetRendererCommon {
 protected:
+	virtual void enableShadow(bool enable);
+	virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+	virtual int getDrawWidthIntern(uint16 chr);
+	virtual int getDrawHeightIntern(uint16 chr);
+	virtual void setDrawCharIntern(uint16 chr) {}
+
 	const byte *_widthTable;
 
 public:
@@ -169,13 +182,34 @@ public:
 	void drawChar(int chr, Graphics::Surface &s, int x, int y);
 	void setCurID(int32 id);
 	void setColor(byte color);
+	virtual int getCharWidth(uint16 chr);
+};
+
+class CharsetRendererTownsV3 : public CharsetRendererV3 {
+public:
+	CharsetRendererTownsV3(ScummEngine *vm);
+
 	int getCharWidth(uint16 chr);
+	int getFontHeight();
+	
+private:
+	void enableShadow(bool enable);
+	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+	int getDrawWidthIntern(uint16 chr);
+	int getDrawHeightIntern(uint16 chr);
+	void setDrawCharIntern(uint16 chr);
+#endif
+	uint16 _sjisCurChar;
 };
 
 #ifdef USE_RGB_COLOR
 class CharsetRendererPCE : public CharsetRendererV3 {
 protected:
-	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
+	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+
+	int getDrawWidthIntern(uint16 chr);
+	int getDrawHeightIntern(uint16 chr);
 
 public:
 	CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 7b136dc..173fb01 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -284,7 +284,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 	_townsScreen = 0;
 	_cjkFont = 0;
-	_cjkChar = 0;
 #endif
 	_shadowPalette = NULL;
 	_shadowPaletteSize = 0;
@@ -1350,13 +1349,23 @@ void ScummEngine::setupCharsetRenderer() {
 			_charset = new CharsetRendererPCE(this);
 		else
 #endif
+		if (_game.platform == Common::kPlatformFMTowns)
+			_charset = new CharsetRendererTownsV3(this);
+		else
 			_charset = new CharsetRendererV3(this);
 #ifdef ENABLE_SCUMM_7_8
 	} else if (_game.version == 8) {
 		_charset = new CharsetRendererNut(this);
 #endif
 	} else {
-		_charset = new CharsetRendererClassic(this);
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+		if (_game.platform == Common::kPlatformFMTowns)
+			_charset = new CharsetRendererTownsClassic(this);
+		else
+#endif
+#endif
+			_charset = new CharsetRendererClassic(this);
 	}
 }
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 01bde90..ed9f650 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -46,9 +46,10 @@
 /* This disables the dual layer mode which is used in FM-Towns versions
  * of SCUMM games and which emulates the behavior of the original code.
  * The only purpose is code size reduction for certain backends.
- * SCUMM 3 (FM-Towns) games will run in normal (DOS VGA) mode, which should
- * work just fine in most situations. Some glitches might occur. SCUMM 5 games
- * will not work without dual layer (and 16 bit color) support.
+ * SCUMM 3 (FM-Towns) games will run in English in normal (DOS VGA) mode,
+ * which should work just fine in most situations. Some glitches might
+ * occur. Japanese mode and SCUMM 5 FM-Towns games will not work without
+ * dual layer (and 16 bit color) support.
  */
 #define DISABLE_TOWNS_DUAL_LAYER_MODE
 #endif
@@ -345,6 +346,7 @@ class ResourceManager;
 class ScummEngine : public Engine {
 	friend class ScummDebugger;
 	friend class CharsetRenderer;
+	friend class CharsetRendererTownsClassic;
 	friend class ResourceManager;
 
 public:
@@ -1333,7 +1335,6 @@ public:
 	byte _townsPaletteFlags;
 	byte _townsCharsetColorMap[16];
 	Graphics::FontSJIS *_cjkFont;
-	uint16 _cjkChar;
 
 protected:
 	void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);


Commit: e3dbae886dc138433f07e4fd9e85b14254471359
    https://github.com/scummvm/scummvm/commit/e3dbae886dc138433f07e4fd9e85b14254471359
Author: athrxx (athrxx at scummvm.org)
Date: 2011-07-09T11:07:58-07:00

Commit Message:
SCUMM: Make use of graphics/sjis code for LOOM PCE sjis drawing

Also move font rom loader to graphics/sjis and make the necessary changes there.

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    graphics/sjis.cpp
    graphics/sjis.h



diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 8adeaa0..bba29bb 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -54,30 +54,25 @@ void ScummEngine::loadCJKFont() {
 		error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build");
 #else
 		// use FM-TOWNS font rom, since game files don't have kanji font resources
-		_cjkFont = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns);
+		_cjkFont = Graphics::FontSJIS::createFont(_game.platform);
 		if (!_cjkFont)
 			error("SCUMM::Font: Could not open file 'FMT_FNT.ROM'");
 		_textSurfaceMultiplier = 2;
 		_useCJKMode = true;
 #endif
 	} else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) {
-		int numChar = 3418;
-		_2byteWidth = 12;
-		_2byteHeight = 12;
 		// use PC-Engine System Card, since game files don't have kanji font resources
-		if (!fp.open("pce.cdbios")) {
-			error("SCUMM::Font: Could not open System Card pce.cdbios");
-		} else {
-			_useCJKMode = true;
-			debug(2, "Loading PC-Engine System Card");
+		_cjkFont = Graphics::FontSJIS::createFont(_game.platform);
+		if (!_cjkFont)
+			error("SCUMM::Font: Could not open file 'pce.cdbios'");
 
-			// A 0x200 byte header can be present at the beginning of the syscard. Seek past it too.
-			fp.seek((fp.size() & 0x200) ? 0x30200 : 0x30000);
+		_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode);
+		_cjkFont->setCharSpacing(-1);
+		_cjkFont->setLineSpacing(-1);
+		_2byteWidth = _cjkFont->getMaxFontWidth();
+		_2byteHeight = _cjkFont->getFontHeight();
+		_useCJKMode = true;		
 
-			_2byteFontPtr = new byte[_2byteWidth * _2byteHeight * numChar / 8];
-			fp.read(_2byteFontPtr, _2byteWidth * _2byteHeight * numChar / 8);
-			fp.close();
-		}
 	} else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) {
 		int numChar = 1413;
 		_2byteWidth = 16;
@@ -161,63 +156,8 @@ void ScummEngine::loadCJKFont() {
 	}
 }
 
-static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offset
-	// rangeTbl maps SJIS char-codes to the PCE System Card font rom.
-	// Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range.
-	const int rangeCnt = 45;
-	static const uint16 rangeTbl[rangeCnt][2] = {
-		// Symbols
-		{0x8140,0x817E},{0x8180,0x81AC},
-		// 0-9
-		{0x824F,0x8258},
-		// Latin upper
-		{0x8260,0x8279},
-		// Latin lower
-		{0x8281,0x829A},
-		// Kana
-		{0x829F,0x82F1},{0x8340,0x837E},{0x8380,0x8396},
-		// Greek upper
-		{0x839F,0x83B6},
-		// Greek lower
-		{0x83BF,0x83D6},
-		// Cyrillic upper
-		{0x8440,0x8460},
-		// Cyrillic lower
-		{0x8470,0x847E},{0x8480,0x8491},
-		// Kanji
-		{0x889F,0x88FC},
-		{0x8940,0x897E},{0x8980,0x89FC},
-		{0x8A40,0x8A7E},{0x8A80,0x8AFC},
-		{0x8B40,0x8B7E},{0x8B80,0x8BFC},
-		{0x8C40,0x8C7E},{0x8C80,0x8CFC},
-		{0x8D40,0x8D7E},{0x8D80,0x8DFC},
-		{0x8E40,0x8E7E},{0x8E80,0x8EFC},
-		{0x8F40,0x8F7E},{0x8F80,0x8FFC},
-		{0x9040,0x907E},{0x9080,0x90FC},
-		{0x9140,0x917E},{0x9180,0x91FC},
-		{0x9240,0x927E},{0x9280,0x92FC},
-		{0x9340,0x937E},{0x9380,0x93FC},
-		{0x9440,0x947E},{0x9480,0x94FC},
-		{0x9540,0x957E},{0x9580,0x95FC},
-		{0x9640,0x967E},{0x9680,0x96FC},
-		{0x9740,0x977E},{0x9780,0x97FC},
-		{0x9840,0x9872}
-	};
-
-	int ch = (f << 8) | (s & 0xFF);
-	int offset = 0;
-	for (int i = 0; i < rangeCnt; ++i) {
-		if (ch >= rangeTbl[i][0] && ch <= rangeTbl[i][1])
-			return offset + ch - rangeTbl[i][0];
-		offset += rangeTbl[i][1] - rangeTbl[i][0] + 1;
-	}
-
-	debug(4, "Invalid Char: 0x%x", ch);
-	return 0;
-}
-
 byte *ScummEngine::get2byteCharPtr(int idx) {
-	if (_game.platform == Common::kPlatformFMTowns)
+	if (_game.platform == Common::kPlatformFMTowns || _game.platform == Common::kPlatformPCEngine)
 		return 0;
 
 	switch (_language) {
@@ -225,10 +165,7 @@ byte *ScummEngine::get2byteCharPtr(int idx) {
 		idx = ((idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
 		break;
 	case Common::JA_JPN:
-		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
-			idx = SJIStoPCEChunk((idx % 256), (idx / 256));
-			return _2byteFontPtr + (_2byteWidth * _2byteHeight / 8) * idx;
-		} else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) {
+		if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) {
 			// init pointer to charset resource
 			if (_2byteFontPtr[0] == 0xFF) {
 				int charsetId = 5;
@@ -672,13 +609,13 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
 	height = getDrawHeightIntern(chr);
 	setDrawCharIntern(chr);
 
+	origWidth = width;
+	origHeight = height;
+
 	// Clip at the right side (to avoid drawing "outside" the screen bounds).
 	if (_left + origWidth > _right + 1)
 		return;
 
-	origWidth = width;
-	origHeight = height;
-
 	if (_shadowMode) {
 		width++;
 		height++;
@@ -1139,15 +1076,25 @@ void CharsetRendererTownsV3::setDrawCharIntern(uint16 chr) {
 
 #ifdef USE_RGB_COLOR
 void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+	if (_sjisCurChar) {
+		assert(_vm->_cjkFont);
+		uint16 col1 = _color;
+		uint16 col2 = _shadowColor;
+
+		if (s.format.bytesPerPixel == 2) {
+			col1 = _vm->_16BitPalette[col1];
+			col2 = _vm->_16BitPalette[col2];
+		}
+
+		_vm->_cjkFont->drawChar(dst, _sjisCurChar, s.pitch, s.format.bytesPerPixel, col1, col2, -1, -1);
+		return;
+	}
+
 	int y, x;
-	int bitCount = 0;
 	byte bits = 0;
 
-	const bool resetLineBitCount = (_vm->_language != Common::JA_JPN || width != 12);
-
 	for (y = 0; y < height && y + drawTop < s.h; y++) {
-		if (resetLineBitCount)
-			bitCount = 0;
+		int bitCount = 0;
 		for (x = 0; x < width; x++) {
 			if ((bitCount % 8) == 0)
 				bits = *src++;
@@ -1171,16 +1118,24 @@ void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const
 }
 
 int CharsetRendererPCE::getDrawWidthIntern(uint16 chr) {
-	if (_vm->_useCJKMode && chr > 127)
-		return  _vm->_2byteWidth;
+	if (_vm->_useCJKMode && chr > 127) {
+		assert(_vm->_cjkFont);
+		return _vm->_cjkFont->getCharWidth(chr);
+	}
 	return CharsetRendererV3::getDrawWidthIntern(chr);
 }
 
 int CharsetRendererPCE::getDrawHeightIntern(uint16 chr) {
-	if (_vm->_useCJKMode && chr > 127)
-		return _vm->_2byteHeight;
+	if (_vm->_useCJKMode && chr > 127) {
+		assert(_vm->_cjkFont);
+		return _vm->_cjkFont->getFontHeight();
+	}
 	return CharsetRendererV3::getDrawHeightIntern(chr);
 }
+
+void CharsetRendererPCE::setDrawCharIntern(uint16 chr) {
+	_sjisCurChar = (_vm->_useCJKMode && chr > 127) ? chr : 0;
+}
 #endif
 
 #ifdef ENABLE_SCUMM_7_8
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 13e40ff..4a57996 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -205,14 +205,17 @@ private:
 
 #ifdef USE_RGB_COLOR
 class CharsetRendererPCE : public CharsetRendererV3 {
-protected:
+private:
 	void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
 
 	int getDrawWidthIntern(uint16 chr);
 	int getDrawHeightIntern(uint16 chr);
+	void setDrawCharIntern(uint16 chr);
+
+	uint16 _sjisCurChar;
 
 public:
-	CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {}
+	CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm), _sjisCurChar(0) {}
 
 	void setColor(byte color);
 };
diff --git a/graphics/sjis.cpp b/graphics/sjis.cpp
index 10c780b..03c3ced 100644
--- a/graphics/sjis.cpp
+++ b/graphics/sjis.cpp
@@ -40,10 +40,27 @@ FontSJIS *FontSJIS::createFont(const Common::Platform platform) {
 	// Try the font ROM of the specified platform
 	if (platform == Common::kPlatformFMTowns) {
 		ret = new FontTowns();
-		if (ret && ret->loadData())
-			return ret;
+		if (ret) {
+			if (ret->loadData())
+				return ret;
+		}
 		delete ret;
-	}
+	} else if (platform == Common::kPlatformPCEngine) {
+		ret = new FontPCEngine();
+		if (ret) {
+			if (ret->loadData())
+				return ret;
+		}
+		delete ret;
+	} // TODO: PC98 font rom support
+	  /* else if (platform == Common::kPlatformPC98) {		
+		ret = new FontPC98();
+		if (ret) {
+			if (ret->loadData())
+				return ret;
+		}
+		delete ret;
+	}*/
 
 	// Try ScummVM's font.
 	ret = new FontSjisSVM(platform);
@@ -59,65 +76,83 @@ void FontSJIS::drawChar(Graphics::Surface &dst, uint16 ch, int x, int y, uint32
 }
 
 FontSJISBase::FontSJISBase()
-	: _drawMode(kDefaultMode), _flippedMode(false), _fontWidth(16), _fontHeight(16) {
+	: _drawMode(kDefaultMode), _flippedMode(false), _fontWidth(16), _fontHeight(16), _charSpacing(0), _lineSpacing(0), _bitPosNewLineMask(0) {
 }
 
 void FontSJISBase::setDrawingMode(DrawingMode mode) {
-	_drawMode = mode;
+	if (hasFeature(1 << mode))
+		_drawMode = mode;
+	else
+		warning("Unsupported drawing mode selected");
 }
 
 void FontSJISBase::toggleFlippedMode(bool enable) {
-	_flippedMode = enable;
+	if (hasFeature(kFeatFlipped))
+		_flippedMode = enable;
+	else
+		warning("Flipped mode unsupported by this font");	
+}
+
+void FontSJISBase::setCharSpacing(int spacing) {
+	_charSpacing = spacing;
+}
+
+void FontSJISBase::setLineSpacing(int spacing) {
+	_lineSpacing = spacing;
 }
 
 uint FontSJISBase::getFontHeight() const {
 	switch (_drawMode) {
 	case kOutlineMode:
-		return _fontHeight + 2;
+		return _fontHeight + _lineSpacing + 2;
 
 	case kDefaultMode:
-		return _fontHeight;
+		return _fontHeight + _lineSpacing;
 
 	default:
-		return _fontHeight + 1;
+		return _fontHeight + _lineSpacing + 1;
 	}
 }
 
 uint FontSJISBase::getMaxFontWidth() const {
 	switch (_drawMode) {
 	case kOutlineMode:
-		return _fontWidth + 2;
+		return _fontWidth + _charSpacing + 2;
 
 	case kDefaultMode:
-		return _fontWidth;
+		return _fontWidth + _charSpacing;
 
 	default:
-		return _fontWidth + 1;
+		return _fontWidth + _charSpacing + 1;
 	}
 }
 
 uint FontSJISBase::getCharWidth(uint16 ch) const {
 	if (isASCII(ch))
-		return (_drawMode == kOutlineMode) ? 10 : (_drawMode == kDefaultMode ? 8 : 9);
+		return ((_drawMode == kOutlineMode) ? 10 : (_drawMode == kDefaultMode ? 8 : 9)) + _charSpacing;
 	else
 		return getMaxFontWidth();
 }
 
 template<typename Color>
 void FontSJISBase::blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c) const {
+	uint8 bitPos = 0;
+	uint8 mask = 0;
+
 	for (int y = 0; y < h; ++y) {
 		Color *d = (Color *)dst;
 		dst += pitch;
 
-		uint8 mask = 0;
+		bitPos &= _bitPosNewLineMask;
 		for (int x = 0; x < w; ++x) {
-			if (!(x % 8))
+			if (!(bitPos % 8))
 				mask = *glyph++;
 
 			if (mask & 0x80)
 				*d = c;
 
 			++d;
+			++bitPos;
 			mask <<= 1;
 		}
 	}
@@ -176,9 +211,6 @@ const uint8 *FontSJISBase::flipCharacter(const uint8 *glyph, const int w) const
 		0x0F, 0x8F, 0x4F, 0xC7, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x97, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
 	};
 
-	// TODO: This code looks like it will only work with 16 pixel wide
-	// characters we should really take care that we only call it on these
-	// or we fix this to support a generic width.
 	for (int i = 0; i < w; i++) {
 		_tempGlyph[i] = flipData[glyph[(w * 2 - 1) - i]];
 		_tempGlyph[(w * 2 - 1) - i] = flipData[glyph[i]];
@@ -225,9 +257,6 @@ void FontSJISBase::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1,
 	}
 
 #ifndef DISABLE_FLIPPED_MODE
-	// TODO: This code inside flopCharater looks like it will only work with
-	// 16 pixel wide characters we should really take care that we only call
-	// it on these or we fix it to support a generic width.
 	if (_flippedMode)
 		glyphSource = flipCharacter(glyphSource, width);
 #endif
@@ -303,7 +332,7 @@ const uint8 *FontTowns::getCharData(uint16 ch) const {
 		uint8 f = ch & 0xFF;
 		uint8 s = ch >> 8;
 
-		// copied from scumm\charset.cpp
+		// moved from scumm\charset.cpp
 		enum {
 			KANA = 0,
 			KANJI = 1,
@@ -392,6 +421,98 @@ const uint8 *FontTowns::getCharData(uint16 ch) const {
 	}
 }
 
+bool FontTowns::hasFeature(int feat) const {
+	static const int features = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow | kFeatFlipped;
+	return (features & feat) ? true : false;
+}
+
+// PC-Engine ROM font
+
+bool FontPCEngine::loadData() {
+	Common::SeekableReadStream *data = SearchMan.createReadStreamForMember("pce.cdbios");
+	if (!data)
+		return false;
+	
+	data->seek((data->size() & 0x200) ? 0x30200 : 0x30000);
+	data->read(_fontData12x12, kFont12x12Chars * 18);
+	
+	_fontWidth = _fontHeight = 12;
+	_bitPosNewLineMask = _fontWidth & 7;
+
+	bool retValue = !data->err();
+	delete data;
+	return retValue;
+}
+
+const uint8 *FontPCEngine::getCharData(uint16 ch) const {
+	// Converts sjis code to pce font offset
+	// (moved from scumm\charset.cpp).
+	// rangeTbl maps SJIS char-codes to the PCE System Card font rom.
+	// Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range.
+	const int rangeCnt = 45;
+	static const uint16 rangeTbl[rangeCnt][2] = {
+		// Symbols
+		{0x8140,0x817E},{0x8180,0x81AC},
+		// 0-9
+		{0x824F,0x8258},
+		// Latin upper
+		{0x8260,0x8279},
+		// Latin lower
+		{0x8281,0x829A},
+		// Kana
+		{0x829F,0x82F1},{0x8340,0x837E},{0x8380,0x8396},
+		// Greek upper
+		{0x839F,0x83B6},
+		// Greek lower
+		{0x83BF,0x83D6},
+		// Cyrillic upper
+		{0x8440,0x8460},
+		// Cyrillic lower
+		{0x8470,0x847E},{0x8480,0x8491},
+		// Kanji
+		{0x889F,0x88FC},
+		{0x8940,0x897E},{0x8980,0x89FC},
+		{0x8A40,0x8A7E},{0x8A80,0x8AFC},
+		{0x8B40,0x8B7E},{0x8B80,0x8BFC},
+		{0x8C40,0x8C7E},{0x8C80,0x8CFC},
+		{0x8D40,0x8D7E},{0x8D80,0x8DFC},
+		{0x8E40,0x8E7E},{0x8E80,0x8EFC},
+		{0x8F40,0x8F7E},{0x8F80,0x8FFC},
+		{0x9040,0x907E},{0x9080,0x90FC},
+		{0x9140,0x917E},{0x9180,0x91FC},
+		{0x9240,0x927E},{0x9280,0x92FC},
+		{0x9340,0x937E},{0x9380,0x93FC},
+		{0x9440,0x947E},{0x9480,0x94FC},
+		{0x9540,0x957E},{0x9580,0x95FC},
+		{0x9640,0x967E},{0x9680,0x96FC},
+		{0x9740,0x977E},{0x9780,0x97FC},
+		{0x9840,0x9872}
+	};
+
+	ch = (ch << 8) | (ch >> 8);
+	int offset = 0;
+	for (int i = 0; i < rangeCnt; ++i) {
+		if (ch >= rangeTbl[i][0] && ch <= rangeTbl[i][1]) {
+			return _fontData12x12 + 18 * (offset + ch - rangeTbl[i][0]);
+			break;
+		}
+		offset += rangeTbl[i][1] - rangeTbl[i][0] + 1;
+	}
+	
+	debug(4, "Invalid Char: 0x%x", ch);
+	return 0;
+}
+
+bool FontPCEngine::hasFeature(int feat) const {
+	// Outline mode not supported due to use of _bitPosNewLineMask. This could be implemented,
+	// but is not needed for any particular target at the moment.
+	// Flipped mode is also not supported since the hard coded table (taken from SCUMM 5 FM-TOWNS)
+	// is set up for font sizes of 8/16. This mode is also not required at the moment, since
+	// there aren't any SCUMM 5 PC-Engine games.
+	static const int features = kFeatDefault | kFeatShadow | kFeatFMTownsShadow;
+	return (features & feat) ? true : false;
+}
+
 // ScummVM SJIS font
 
 FontSjisSVM::FontSjisSVM(const Common::Platform platform)
@@ -464,6 +585,15 @@ const uint8 *FontSjisSVM::getCharData(uint16 c) const {
 		return getCharDataDefault(c);
 }
 
+bool FontSjisSVM::hasFeature(int feat) const {
+	// Flipped mode is not supported since the hard coded table (taken from SCUMM 5 FM-TOWNS)
+	// is set up for font sizes of 8/16. This mode is also not required at the moment, since
+	// there aren't any SCUMM 5 PC-Engine games.
+	static const int features16 = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow | kFeatFlipped;
+	static const int features12 = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow;
+	return (((_fontWidth == 12) ? features12 : features16) & feat) ? true : false;
+}
+
 const uint8 *FontSjisSVM::getCharDataPCE(uint16 c) const {
 	if (isASCII(c))
 		return 0;
diff --git a/graphics/sjis.h b/graphics/sjis.h
index 62e6801..de2d4b3 100644
--- a/graphics/sjis.h
+++ b/graphics/sjis.h
@@ -75,7 +75,7 @@ public:
 	virtual bool loadData() = 0;
 
 	/**
-	 * Enable drawing with outline or shadow.
+	 * Enable drawing with outline or shadow if supported by the Font.
 	 *
 	 * After changing outline state, getFontHeight and getMaxFontWidth / getCharWidth might return
 	 * different values!
@@ -90,11 +90,17 @@ public:
 	virtual void setDrawingMode(DrawingMode mode) {}
 
 	/**
-	 * Enable flipped character drawing (e.g. in the MI1 circus scene after Guybrush gets shot out of the cannon).
+	 * Enable flipped character drawing if supported by the Font (e.g. in the MI1 circus scene after Guybrush gets shot out of the cannon).
 	 */
 	virtual void toggleFlippedMode(bool enable) {}
 
 	/**
+	 * Set spacing between characters and lines. This affects font height / char width
+	 */
+	virtual void setCharSpacing(int spacing) {}
+	virtual void setLineSpacing(int spacing) {}
+
+	/**
 	 * Returns the height of the font.
 	 */
 	virtual uint getFontHeight() const = 0;
@@ -140,6 +146,10 @@ public:
 
 	virtual void toggleFlippedMode(bool enable);
 
+	virtual void setCharSpacing(int spacing);
+
+	virtual void setLineSpacing(int spacing);
+
 	virtual uint getFontHeight() const;
 
 	virtual uint getMaxFontWidth() const;
@@ -162,16 +172,28 @@ protected:
 	DrawingMode _drawMode;
 	bool _flippedMode;
 	int _fontWidth, _fontHeight;
-
+	int _charSpacing, _lineSpacing;
+	uint8 _bitPosNewLineMask;
+	
 	bool isASCII(uint16 ch) const;
 
 	virtual const uint8 *getCharData(uint16 c) const = 0;
+
+	enum DrawingFeature {
+		kFeatDefault		= 1 << 0,
+		kFeatOutline		= 1 << 1,
+		kFeatShadow			= 1 << 2,
+		kFeatFMTownsShadow	= 1 << 3,
+		kFeatFlipped		= 1 << 4
+	};
+
+	virtual bool hasFeature(int feat) const = 0;
 };
 
 /**
  * FM-TOWNS ROM based SJIS compatible font.
  *
- * This is used in KYRA and SCI.
+ * This is used in KYRA, SCUMM and SCI.
  */
 class FontTowns : public FontSJISBase {
 public:
@@ -189,6 +211,31 @@ private:
 	uint8 _fontData8x16[kFont8x16Chars * 32];
 
 	virtual const uint8 *getCharData(uint16 c) const;
+
+	bool hasFeature(int feat) const;
+};
+
+/**
+ * PC-Engine System Card based SJIS compatible font.
+ *
+ * This is used in LOOM.
+ */
+class FontPCEngine : public FontSJISBase {
+public:
+	/**
+	 * Loads the ROM data from "pce.cdbios".
+	 */
+	bool loadData();
+private:
+	enum {
+		kFont12x12Chars = 3418
+	};
+
+	uint8 _fontData12x12[kFont12x12Chars * 18];
+
+	virtual const uint8 *getCharData(uint16 c) const;
+
+	bool hasFeature(int feat) const;
 };
 
 /**
@@ -215,6 +262,8 @@ private:
 
 	virtual const uint8 *getCharData(uint16 c) const;
 
+	bool hasFeature(int feat) const;
+
 	const uint8 *getCharDataPCE(uint16 c) const;
 	const uint8 *getCharDataDefault(uint16 c) const;
 






More information about the Scummvm-git-logs mailing list