[Scummvm-cvs-logs] SF.net SVN: scummvm:[52966] scummvm/trunk

athrxx at users.sourceforge.net athrxx at users.sourceforge.net
Fri Oct 1 21:24:53 CEST 2010


Revision: 52966
          http://scummvm.svn.sourceforge.net/scummvm/?rev=52966&view=rev
Author:   athrxx
Date:     2010-10-01 19:24:52 +0000 (Fri, 01 Oct 2010)

Log Message:
-----------
SCUMM/FM-TOWNS: fix palette and other graphics issues

This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this  dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).

Old savegames (saved before this update) will load, but you?\226?\128?\153ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.

This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they?\226?\128?\153ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).

Japanese font drawing hasn?\226?\128?\153t been improved much yet. This will be a separate task.

Modified Paths:
--------------
    scummvm/trunk/engines/scumm/actor.cpp
    scummvm/trunk/engines/scumm/charset.cpp
    scummvm/trunk/engines/scumm/charset.h
    scummvm/trunk/engines/scumm/cursor.cpp
    scummvm/trunk/engines/scumm/detection_tables.h
    scummvm/trunk/engines/scumm/gfx.cpp
    scummvm/trunk/engines/scumm/gfx.h
    scummvm/trunk/engines/scumm/module.mk
    scummvm/trunk/engines/scumm/palette.cpp
    scummvm/trunk/engines/scumm/room.cpp
    scummvm/trunk/engines/scumm/saveload.cpp
    scummvm/trunk/engines/scumm/saveload.h
    scummvm/trunk/engines/scumm/script_v4.cpp
    scummvm/trunk/engines/scumm/script_v5.cpp
    scummvm/trunk/engines/scumm/scumm-md5.h
    scummvm/trunk/engines/scumm/scumm.cpp
    scummvm/trunk/engines/scumm/scumm.h
    scummvm/trunk/engines/scumm/string.cpp
    scummvm/trunk/engines/scumm/verbs.cpp
    scummvm/trunk/graphics/surface.cpp

Added Paths:
-----------
    scummvm/trunk/engines/scumm/gfx_towns.cpp

Modified: scummvm/trunk/engines/scumm/actor.cpp
===================================================================
--- scummvm/trunk/engines/scumm/actor.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/actor.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -2159,7 +2159,10 @@
 		((ScummEngine_v7 *)this)->clearSubtitleQueue();
 #endif
 	} else {
-		restoreCharsetBg();
+		if (_game.platform == Common::kPlatformFMTowns)
+			towns_restoreCharsetBg();
+		else
+			restoreCharsetBg();
 	}
 }
 

Modified: scummvm/trunk/engines/scumm/charset.cpp
===================================================================
--- scummvm/trunk/engines/scumm/charset.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/charset.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -655,6 +655,12 @@
 	} else
 		useShadow = false;
 
+	if (_vm->_game.platform == Common::kPlatformFMTowns) {
+		_color = (_color & 0x0f) | ((_color & 0x0f) << 4);
+		if (_color == 0)
+			_color = 0x88;
+	}
+
 	enableShadow(useShadow);
 
 	translateColor();
@@ -672,7 +678,7 @@
 void CharsetRendererCommon::enableShadow(bool enable) {
 	if (enable) {
 		if (_vm->_game.platform == Common::kPlatformFMTowns) {
-			_shadowColor = 8;
+			_shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
 			_shadowMode = kFMTOWNSShadowMode;
 		} else {
 			_shadowColor = 0;
@@ -683,7 +689,6 @@
 	}
 }
 
-
 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
@@ -724,8 +729,8 @@
 	origHeight = height;
 
 	if (_shadowMode != kNoShadowMode) {
-		width++;
-		height++;
+		width += _vm->_textSurfaceMultiplier;
+		height += _vm->_textSurfaceMultiplier;
 	}
 
 	if (_firstChar) {
@@ -744,7 +749,8 @@
 		_hasMask = true;
 		_textScreenID = vs->number;
 	}
-	if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {
+
+	if ((_vm->_game.platform != Common::kPlatformFMTowns || (_vm->_game.id == GID_LOOM && !is2byte)) && (ignoreCharsetMask || !vs->hasTwoBuffers)) {
 		dst = vs->getPixels(_left, drawTop);
 		drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
 	} else {
@@ -801,6 +807,27 @@
 	}
 }
 
+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;
+		}
+	}
+}
+
 void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {
 	static const SaveLoadEntry charsetRendererEntries[] = {
 		MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)),
@@ -836,6 +863,8 @@
 
 	_vm->_charsetColorMap[1] = _color;
 
+	processTownsCharsetColors(_bytesPerPixel);
+
 	if (is2byte) {
 		enableShadow(true);
 		charPtr = _vm->get2byteCharPtr(chr);
@@ -851,7 +880,7 @@
 
 		width = charPtr[0];
 		height = charPtr[1];
-
+	
 		if (_disableOffsX) {
 			offsX = 0;
 		} else {
@@ -866,8 +895,8 @@
 	origHeight = height;
 
 	if (_shadowMode != kNoShadowMode) {
-		width++;
-		height++;
+		width += _vm->_textSurfaceMultiplier;
+		height += _vm->_textSurfaceMultiplier;
 	}
 	if (_firstChar) {
 		_str.left = 0;
@@ -905,7 +934,9 @@
 
 	_vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
 
-	if (!ignoreCharsetMask) {
+	// 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 || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)) {
 		_hasMask = true;
 		_textScreenID = vs->number;
 	}
@@ -961,7 +992,7 @@
 	} else {
 		Graphics::Surface dstSurface;
 		Graphics::Surface backSurface;
-		if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {
+		if (_vm->_game.platform != Common::kPlatformFMTowns && (ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {
 			dstSurface = *vs;
 			dstPtr = vs->getPixels(_left, drawTop);
 		} else {
@@ -1064,13 +1095,14 @@
 	assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
 	bits = *src++;
 	numbits = 8;
+	byte *cmap = (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap : _vm->_charsetColorMap;
 
 	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) {
-				*dst = _vm->_charsetColorMap[color];
+				*dst = cmap[color];
 			}
 			dst++;
 			bits <<= bpp;
@@ -1087,6 +1119,7 @@
 void CharsetRendererCommon::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 = (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] : _color;
 
 	for (y = 0; y < height && y + drawTop < s.h; y++) {
 		for (x = 0; x < width; x++) {
@@ -1108,7 +1141,7 @@
 						if (_shadowMode != kFMTOWNSShadowMode)
 							*(dst + s.pitch + 1) = _shadowColor;
 					}
-					*dst = _color;
+					*dst = col;
 				}
 			}
 			dst += bitDepth;

Modified: scummvm/trunk/engines/scumm/charset.h
===================================================================
--- scummvm/trunk/engines/scumm/charset.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/charset.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -79,6 +79,7 @@
 	int getStringWidth(int a, const byte *str);
 	void addLinebreaks(int a, byte *str, int pos, int maxwidth);
 	void translateColor();
+	void processTownsCharsetColors(uint8 bytesPerPixel);
 
 	virtual void setCurID(int32 id) = 0;
 	int getCurID() { return _curId; }

Modified: scummvm/trunk/engines/scumm/cursor.cpp
===================================================================
--- scummvm/trunk/engines/scumm/cursor.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/cursor.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -554,11 +554,14 @@
 	uint16 color;
 	const uint16 *src = _cursorImages[_currentCursor];
 
-	if (_bytesPerPixel == 2) {
+	if (_bytesPerPixelOutput == 2) {
 		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
 			byte r, g, b;
 			colorPCEToRGB(default_pce_cursor_colors[idx], &r, &g, &b);
 			color = get16BitColor(r, g, b);
+		} else if (_game.platform == Common::kPlatformFMTowns) {
+			byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3];
+			color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]);
 		} else {
 			color = _16BitPalette[default_cursor_colors[idx]];
 		}
@@ -570,18 +573,20 @@
 		memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));
 	}
 
-	_cursor.hotspotX = _cursorHotspots[2 * _currentCursor];
-	_cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1];
-	_cursor.width = 16;
-	_cursor.height = 16;
+	_cursor.hotspotX = _cursorHotspots[2 * _currentCursor] * _textSurfaceMultiplier;
+	_cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1] * _textSurfaceMultiplier;
+	_cursor.width = 16 * _textSurfaceMultiplier;
+	_cursor.height = 16 * _textSurfaceMultiplier;
 
+	int scl = (_game.platform == Common::kPlatformFMTowns) ? (_bytesPerPixelOutput * _textSurfaceMultiplier) : 1;
+
 	for (i = 0; i < 16; i++) {
 		for (j = 0; j < 16; j++) {
 			if (src[i] & (1 << j)) {
-				if (_bytesPerPixel == 2)
-					WRITE_UINT16(_grabbedCursor + 32 * i + (15 - j) * 2, color);
-				else
-					_grabbedCursor[16 * i + 15 - j] = color;
+				byte *dst1 = _grabbedCursor + 16 * scl * i * _textSurfaceMultiplier + (15 - j) * scl;
+				byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1;
+				for (int b = 0; b < scl; b++)
+					*dst1++ = *dst2++ = color;
 			}
 		}
 	}

Modified: scummvm/trunk/engines/scumm/detection_tables.h
===================================================================
--- scummvm/trunk/engines/scumm/detection_tables.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/detection_tables.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -220,6 +220,7 @@
 	{"zak", "V2",       "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
 	{"zak", "FM-TOWNS",    0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
 
+
 	{"indy3", "EGA",      "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
 	{"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR,             0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
 	{"indy3", "VGA",      "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS,                  Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
@@ -243,10 +244,12 @@
 	{"monkey", "FM-TOWNS",     0, GID_MONKEY,     5, 0, MDT_TOWNS,                        GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
 	{"monkey", "SEGA",         0, GID_MONKEY,     5, 0, MDT_NONE,                         GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},
 
-	{"monkey2",  0, 0, GID_MONKEY2,  5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+	{"monkey2",  "", 0, GID_MONKEY2,  5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+	{"monkey2", "FM-TOWNS", 0, GID_MONKEY2,  5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH},
 
-	{"atlantis", "" , 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
+	{"atlantis", "", 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
 	{"atlantis", "Floppy", 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+	{"atlantis", "FM-TOWNS", 0, GID_INDY4,    5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NONE},
 
 	{"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
 	{"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH},

Modified: scummvm/trunk/engines/scumm/gfx.cpp
===================================================================
--- scummvm/trunk/engines/scumm/gfx.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/gfx.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -51,7 +51,6 @@
 static void clear8Col(byte *dst, int dstPitch, int height, uint8 bitDepth);
 
 static void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height);
-static void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h);
 
 struct StripTable {
 	int offsets[160];
@@ -323,6 +322,16 @@
 		_res->nukeResource(rtBuffer, i + 5);
 	}
 
+	if (_townsScreen) {
+		if (!_townsClearLayerFlag && (h - b != _virtscr[kMainVirtScreen].h))
+			_townsScreen->clearLayer(0);
+
+		if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) {
+			_textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0);
+			_townsScreen->clearLayer(1);
+		}
+	}
+
 	if (!getResourceAddress(rtBuffer, 4)) {
 		// Since the size of screen 3 is fixed, there is no need to reallocate
 		// it if its size changed.
@@ -611,18 +620,9 @@
 	int m = _textSurfaceMultiplier;
 	int vsPitch;
 	int pitch = vs->pitch;
+	vsPitch = vs->pitch - width * vs->bytesPerPixel;
 
-	if (_useCJKMode && _textSurfaceMultiplier == 2) {
-		scale2x(_fmtownsBuf, _screenWidth * m, (const byte *)src, vs->pitch,  width, height);
-		src = _fmtownsBuf;
 
-		vsPitch = _screenWidth * m - width * m;
-
-	} else {
-		vsPitch = vs->pitch - width * vs->bytesPerPixel;
-	}
-
-
 	if (_game.version < 7) {
 		// For The Dig, FT and COMI, we just blit everything to the screen at once.
 		// For older games, things are more complicated. First off, we need to
@@ -643,7 +643,10 @@
 #ifdef USE_ARM_GFX_ASM
 		asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch);
 #else
-		if (_bytesPerPixel == 2) {
+		if (_game.platform == Common::kPlatformFMTowns) {
+			towns_drawStripToScreen(vs, x, y, x, top, width, height);
+			return;	
+		} else if (_bytesPerPixelOutput == 2) {
 			const byte *srcPtr = (const byte *)src;
 			const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m);
 			byte *dstPtr = _compositeBuf;
@@ -824,28 +827,6 @@
 	*height = dsty - *y;
 }
 
-void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
-	/* dst and dstPitch should both be even. So the use of (void *) in
-	 * the following casts to avoid the unnecessary warning is valid. */
-	uint16 *dstL1 = (uint16 *)(void *)dst;
-	uint16 *dstL2 = (uint16 *)(void *)(dst + dstPitch);
-
-	const int dstAdd = dstPitch - w;
-	const int srcAdd = srcPitch - w;
-
-	while (h--) {
-		for (int x = 0; x < w; ++x) {
-			uint16 col = *src++;
-			col |= col << 8;
-			*dstL1++ = col;
-			*dstL2++ = col;
-		}
-		dstL1 += dstAdd; dstL2 += dstAdd;
-		src += srcAdd;
-	}
-}
-
-
 #pragma mark -
 #pragma mark --- Background buffers & charset mask ---
 #pragma mark -
@@ -1017,7 +998,7 @@
 	VirtScreen *vs;
 	byte *screenBuf;
 
-	if (rect.top < 0)
+ 	if (rect.top < 0)
 		rect.top = 0;
 	if (rect.left >= rect.right || rect.top >= rect.bottom)
 		return;
@@ -1028,6 +1009,9 @@
 	if (rect.left > vs->w)
 		return;
 
+	if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154)
+		rect.right = 320;
+	
 	// Convert 'rect' to local (virtual screen) coordinates
 	rect.top -= vs->topline;
 	rect.bottom -= vs->topline;
@@ -1047,10 +1031,21 @@
 	if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) {
 		blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height, vs->bytesPerPixel);
 		if (vs->number == kMainVirtScreen && _charset->_hasMask) {
-			byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop);
-			fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height, _textSurface.bytesPerPixel);
+			if (_game.platform == Common::kPlatformFMTowns) {
+				byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier);
+				fill(mask, _textSurface.pitch, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+			} else {
+				byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop);
+				fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+			}
 		}
 	} else {
+		if (_game.platform == Common::kPlatformFMTowns) {
+			backColor |= (backColor << 4);
+			byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier);
+			fill(mask, _textSurface.pitch, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+		}
+			
 		if (_game.features & GF_16BIT_COLOR)
 			fill(screenBuf, vs->pitch, _16BitPalette[backColor], width, height, vs->bytesPerPixel);
 		else
@@ -1102,7 +1097,10 @@
 }
 
 void ScummEngine::clearTextSurface() {
-	fill((byte*)_textSurface.pixels,  _textSurface.pitch,  CHARSET_MASK_TRANSPARENCY,  _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel);
+	if (_townsScreen)
+		_townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0);
+
+	fill((byte*)_textSurface.pixels,  _textSurface.pitch,  _game.platform == Common::kPlatformFMTowns ? 0 : CHARSET_MASK_TRANSPARENCY,  _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel);
 }
 
 byte *ScummEngine::getMaskBuffer(int x, int y, int z) {
@@ -1256,13 +1254,25 @@
 	backbuff = vs->getPixels(x, y);
 	bgbuff = vs->getBackPixels(x, y);
 
-	if (color == -1) {
-		if (vs->number != kMainVirtScreen)
-			error("can only copy bg to main window");
-		blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel);
-		if (_charset->_hasMask) {
-			byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier);
-			fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+	// A check for -1 might be wrong in all cases since o5_drawBox() in its current form
+	// is definitely not capable of passing a parameter of -1 (color range is 0 - 255).
+	// Just to make sure I don't break anything I restrict the code change to FM-Towns
+	// version 5 games where this change is necessary to fix certain long standing bugs.
+	if (color == -1 || (color >= 254 && _game.platform == Common::kPlatformFMTowns && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4))) {
+		if (_game.platform == Common::kPlatformFMTowns) {
+			if (color == 254) {
+				color = color;
+				towns_setupPalCycleField(x, y, x2, y2);
+			}
+		} else {
+			if (vs->number != kMainVirtScreen)
+				error("can only copy bg to main window");
+
+			blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel);
+			if (_charset->_hasMask) {
+				byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier);
+				fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+			}
 		}
 	} else if (_game.heversion >= 72) {
 		// Flags are used for different methods in HE games
@@ -1293,10 +1303,20 @@
 			fill(backbuff, vs->pitch, flags, width, height, vs->bytesPerPixel);
 		}
 	} else {
-		if (_game.features & GF_16BIT_COLOR)
+		if (_game.features & GF_16BIT_COLOR) {
 			fill(backbuff, vs->pitch, _16BitPalette[color], width, height, vs->bytesPerPixel);
-		else
+		} else {
+			if (_game.platform == Common::kPlatformFMTowns) {
+				color = ((color & 0x0f) << 4) | (color & 0x0f);
+				byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop + vs->topline) * _textSurfaceMultiplier);
+				fill(mask, _textSurface.pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+				
+				if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4)
+					return;
+			}
+			
 			fill(backbuff, vs->pitch, color, width, height, vs->bytesPerPixel);
+		}
 	}
 }
 
@@ -1703,6 +1723,11 @@
 		warning("Gdi::drawBitmap, strip drawn to %d below window bottom %d", y + height, vs->h);
 	}
 
+	if (_vm->_townsPaletteFlags & 2) {
+		int cx = (x - _vm->_screenStartStrip) << 3;
+		_vm->_textSurface.fillRect(Common::Rect(cx * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, (cx  + width - 1) * _vm->_textSurfaceMultiplier, (y + height - 1) * _vm->_textSurfaceMultiplier), 0);
+	}
+
 	_vertStripNextInc = height * vs->pitch - 1 * vs->bytesPerPixel;
 
 	_objectMode = (flag & dbObjectMode) == dbObjectMode;
@@ -3662,6 +3687,9 @@
 		// Fill screen 0 with black
 		memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h);
 
+		if (_game.version == 3 && _game.platform == Common::kPlatformFMTowns)
+			_textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0);
+
 		// Fade to black with the specified effect, if any.
 		switch (effect) {
 		case 1:
@@ -3856,15 +3884,10 @@
 		x = offsets[i] % vs->pitch;
 		y = offsets[i] / vs->pitch;
 
-		if (_useCJKMode && _textSurfaceMultiplier == 2) {
-			int m = _textSurfaceMultiplier;
-			byte *dst = _fmtownsBuf + x * m + y * m * _screenWidth * m;
-			scale2x(dst, _screenWidth * m, vs->getPixels(x, y), vs->pitch,  width, height);
-
-			_system->copyRectToScreen(dst, _screenWidth * m, x * m, (y + vs->topline) * m, width * m, height * m);
-		} else {
+		if (_game.platform == Common::kPlatformFMTowns)
+			towns_drawStripToScreen(vs, x, y + vs->topline, x, y, width, height);
+		else
 			_system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
-		}
 
 
 		if (++blits >= blits_before_refresh) {
@@ -3904,23 +3927,19 @@
 		y = 1 + step;
 		while (y < vs->h) {
 			moveScreen(0, -step, vs->h);
-
-			src = vs->getPixels(0, y - step);
-			if (_useCJKMode && m == 2) {
-				int x1 = 0, y1 = vs->h - step;
-				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
-				scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
-				src = dst;
-				vsPitch = _screenWidth * 2;
+			
+			if (_townsScreen) {
+				towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step);
+			} else {
+				src = vs->getPixels(0, y - step);
+				_system->copyRectToScreen(src,
+					vsPitch,
+					0, (vs->h - step) * m,
+					vs->w * m, step * m);
+				_system->updateScreen();
 			}
-
-			_system->copyRectToScreen(src,
-				vsPitch,
-				0 * m, (vs->h - step) * m,
-				vs->w * m, step * m);
-			_system->updateScreen();
+			
 			waitForTimer(delay);
-
 			y += step;
 		}
 		break;
@@ -3929,21 +3948,19 @@
 		y = 1 + step;
 		while (y < vs->h) {
 			moveScreen(0, step, vs->h);
-			src = vs->getPixels(0, vs->h - y);
-			if (_useCJKMode && m == 2) {
-				int x1 = 0, y1 = 0;
-				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
-				scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
-				src = dst;
-				vsPitch = _screenWidth * 2;
+
+			if (_townsScreen) {
+				towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step);
+			} else {
+				src = vs->getPixels(0, vs->h - y);
+				_system->copyRectToScreen(src,
+					vsPitch,
+					0, 0,
+					vs->w * m, step * m);
+				_system->updateScreen();
 			}
-			_system->copyRectToScreen(src,
-				vsPitch,
-				0, 0,
-				vs->w * m, step * m);
-			_system->updateScreen();
+			
 			waitForTimer(delay);
-
 			y += step;
 		}
 		break;
@@ -3952,21 +3969,19 @@
 		x = 1 + step;
 		while (x < vs->w) {
 			moveScreen(-step, 0, vs->h);
-			src = vs->getPixels(x - step, 0);
-			if (_useCJKMode && m == 2) {
-				int x1 = vs->w - step, y1 = 0;
-				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
-				scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
-				src = dst;
-				vsPitch = _screenWidth * 2;
+			
+			if (_townsScreen) {
+				towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h);
+			} else {
+				src = vs->getPixels(x - step, 0);
+				_system->copyRectToScreen(src,
+					vsPitch,
+					(vs->w - step) * m, 0,
+					step * m, vs->h * m);
+				_system->updateScreen();
 			}
-			_system->copyRectToScreen(src,
-				vsPitch,
-				(vs->w - step) * m, 0,
-				step * m, vs->h * m);
-			_system->updateScreen();
+
 			waitForTimer(delay);
-
 			x += step;
 		}
 		break;
@@ -3975,21 +3990,19 @@
 		x = 1 + step;
 		while (x < vs->w) {
 			moveScreen(step, 0, vs->h);
-			src = vs->getPixels(vs->w - x, 0);
-			if (_useCJKMode && m == 2) {
-				int x1 = 0, y1 = 0;
-				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
-				scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
-				src = dst;
-				vsPitch = _screenWidth * 2;
-			}
-			_system->copyRectToScreen(src,
-				vsPitch,
-				0, 0,
-				step, vs->h);
-			_system->updateScreen();
+
+			if (_townsScreen) {
+				towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h);
+			} else {
+				src = vs->getPixels(vs->w - x, 0);
+				_system->copyRectToScreen(src,
+					vsPitch,
+					0, 0,
+					step, vs->h);
+				_system->updateScreen();
+			}	
+
 			waitForTimer(delay);
-
 			x += step;
 		}
 		break;

Modified: scummvm/trunk/engines/scumm/gfx.h
===================================================================
--- scummvm/trunk/engines/scumm/gfx.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/gfx.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -26,6 +26,9 @@
 #ifndef SCUMM_GFX_H
 #define SCUMM_GFX_H
 
+#include "common/system.h"
+#include "common/list.h"
+
 #include "graphics/surface.h"
 
 namespace Scumm {
@@ -421,6 +424,64 @@
 };
 #endif
 
+// Helper class for FM-Towns output (required for specific hardware effects like
+// switching graphics layers on and off).
+
+class TownsScreen {
+public:
+	TownsScreen(OSystem *system, int width, int height, int bpp);
+	~TownsScreen();
+
+	void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0);
+	void clearLayer(int layer);
+	void fillLayerRect(int layer, int x, int y, int w, int h, int col);
+	//void copyRectToLayer(int layer, int x, int y, int w, int h, const uint8 *src);
+	
+	uint8 *getLayerPixels(int layer, int x, int y);
+	int getLayerPitch(int layer);
+	int getLayerHeight(int layer);
+	int getLayerBpp(int layer);
+	int getLayerScaleW(int layer);
+	int getLayerScaleH(int layer);
+	
+	void addDirtyRect(int x, int y, int w, int h);
+	void toggleLayers(int flag);
+	void update();
+
+private:
+	void updateOutputBuffer();
+	void outputToScreen();
+	uint16 calc16BitColor(const uint8 *palEntry);
+
+	struct TownsScreenLayer {
+		uint8 *pixels;
+		uint8 *palette;
+		int pitch;
+		int height;
+		int bpp;
+		int numCol;
+		uint8 scaleW;
+		uint8 scaleH;
+		bool onBottom;
+		bool enabled;
+		bool ready;
+
+		uint16 *bltInternX;
+		uint8 **bltInternY;
+	} _layers[2];
+	
+	uint8 *_outBuffer;
+
+	int _height;
+	int _width;
+	int _pitch;
+	int _bpp;
+	
+	int _numDirtyRects;
+	Common::List<Common::Rect> _dirtyRects;	
+	OSystem *_system;
+};
+
 } // End of namespace Scumm
 
 #endif

Added: scummvm/trunk/engines/scumm/gfx_towns.cpp
===================================================================
--- scummvm/trunk/engines/scumm/gfx_towns.cpp	                        (rev 0)
+++ scummvm/trunk/engines/scumm/gfx_towns.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -0,0 +1,508 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/endian.h"
+
+#include "scumm/scumm.h"
+#include "scumm/charset.h"
+#include "scumm/util.h"
+#include "scumm/resource.h"
+
+namespace Scumm {
+
+void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int width, int height) {
+	if (width <= 0 || height <= 0)
+		return;
+
+	assert(_textSurface.pixels);
+
+	int m = _textSurfaceMultiplier;
+
+	uint8 *src1 = vs->getPixels(srcX, srcY);
+	uint8 *src2 = (uint8*)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m);
+	uint8 *dst1 = _townsScreen->getLayerPixels(0, dstX, dstY);
+	uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m);
+
+	int dp1 = _townsScreen->getLayerPitch(0) - width * _townsScreen->getLayerBpp(0);
+	int dp2 = _townsScreen->getLayerPitch(1) - width * m * _townsScreen->getLayerBpp(1);
+	int sp1 = vs->pitch - (width * vs->bytesPerPixel);
+	int sp2 = _textSurface.pitch - width * m;
+				
+	if (vs->number == kMainVirtScreen) {
+		for (int h = 0; h < height; ++h) {
+			if (_bytesPerPixelOutput == 2) {
+				for (int w = 0; w < width; ++w) {
+					WRITE_UINT16(dst1, _16BitPalette[*src1++]);
+					dst1 += _bytesPerPixelOutput;
+				}
+
+				src1 += sp1;
+				dst1 += dp1;
+			} else {
+				memcpy(dst1, src1, width);
+				src1 += vs->pitch;
+				dst1 += _townsScreen->getLayerPitch(0);
+			}
+			
+			for (int sH = 0; sH < m; ++sH) {
+				memcpy(dst2, src2, width * m);
+				src2 += _textSurface.pitch;
+				dst2 += _townsScreen->getLayerPitch(1);
+			}
+		}					
+	} else {
+		dst1 = dst2;
+		for (int h = 0; h < height; ++h) {
+			for (int w = 0; w < width; ++w) {
+				uint8 t = (*src1++) & 0x0f;
+				memset(dst1, (t << 4) | t, m);
+				dst1 += m;
+			}
+
+			dst1 = dst2;
+			uint8 *src3 = src2;
+			
+			if (m == 2) {
+				dst2 += _townsScreen->getLayerPitch(1);
+				src3 += _townsScreen->getLayerPitch(1);
+			}
+
+			for (int w = 0; w < width * m; ++w) {
+				*dst2++ = (*src3 | (*dst1 & _townsLayer2Mask[*src3]));
+				*dst1 = (*src2 | (*dst1 & _townsLayer2Mask[*src2]));
+				src2++;
+				src3++;
+				dst1++;
+			}
+
+			src1 += sp1;			
+			src2 = src3 + sp2;
+			dst1 = dst2 + dp2;
+			dst2 += dp2;
+		}
+	}
+
+	_townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m);
+}
+
+bool ScummEngine::towns_isRectInStringBox(int x1, int y1, int x2, int y2) {
+	if (_game.platform == Common::kPlatformFMTowns && _charset->_hasMask && y1 <= _curStringRect.bottom && x1 <= _curStringRect.right && y2 >= _curStringRect.top && x2 >= _curStringRect.left)
+		return true;
+	return false;
+}
+
+void ScummEngine::towns_restoreCharsetBg() {
+	if (_curStringRect.left != -1) {
+		restoreBackground(_curStringRect, 0);
+		_curStringRect.left = -1;
+		_charset->_hasMask = false;
+		_nextLeft = _string[0].xpos;
+	}
+
+	_nextLeft = _string[0].xpos;
+	_nextTop = _string[0].ypos;
+}
+
+#ifdef USE_RGB_COLOR
+void ScummEngine::towns_setPaletteFromPtr(const byte *ptr, int numcolor) {
+	setPaletteFromPtr(ptr, numcolor);
+
+	if (_game.version == 5)
+		towns_setTextPaletteFromPtr(_currentPalette);
+
+	_townsOverrideShadowColor = 1;
+	int m = 48;
+	for (int i = 1; i < 16; ++i) {
+		int val = _currentPalette[i * 3] + _currentPalette[i * 3 + 1] + _currentPalette[i * 3 + 2];
+		if (m > val) {
+			_townsOverrideShadowColor = i;
+			m = val;
+		}
+	}
+}
+
+void ScummEngine::towns_setTextPaletteFromPtr(const byte *ptr) {
+	memcpy(_textPalette, ptr, 48);
+}
+#endif
+
+void ScummEngine::towns_setupPalCycleField(int x1, int y1, int x2, int y2) {
+	if (_numCyclRects >= 10)
+		return;
+	_cyclRects[_numCyclRects].left = x1;
+	_cyclRects[_numCyclRects].top = y1;
+	_cyclRects[_numCyclRects].right = x2;
+	_cyclRects[_numCyclRects].bottom = y2;
+	_numCyclRects++;
+	_townsPaletteFlags |= 1;
+}
+
+void ScummEngine::towns_processPalCycleField() {
+	for (int i = 0; i < _numCyclRects; i++) {
+		int x1 = _cyclRects[i].left - _virtscr[kMainVirtScreen].xstart;
+		int x2 = _cyclRects[i].right - _virtscr[kMainVirtScreen].xstart;
+		if (x1 < 0)
+			x1 = 0;
+		if (x2 > 320)
+			x2 = 320;
+		if (x2 > 0)
+			markRectAsDirty(kMainVirtScreen, x1, x2, _cyclRects[i].top, _cyclRects[i].bottom);
+	}
+}
+
+void ScummEngine::towns_resetPalCycleFields() {
+	_numCyclRects = 0;
+	_townsPaletteFlags &= ~1;
+}
+
+const uint8 ScummEngine::_townsLayer2Mask[] = {
+	0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define DIRTY_RECTS_MAX 20
+#define FULL_REDRAW (DIRTY_RECTS_MAX + 1)
+
+TownsScreen::TownsScreen(OSystem *system, int width, int height, int bpp) :
+	_system(system), _width(width), _height(height), _bpp(bpp), _pitch(width * bpp) {
+	memset(&_layers[0], 0, sizeof(TownsScreenLayer));
+	memset(&_layers[1], 0, sizeof(TownsScreenLayer));
+	_outBuffer = new byte[_pitch * _height];
+	memset(_outBuffer, 0, _pitch * _height);
+
+	setupLayer(0, width, height, 256);
+}
+
+TownsScreen::~TownsScreen() {
+	delete[] _layers[0].pixels;
+	delete[] _layers[1].pixels;
+	delete[] _layers[0].bltInternX;
+	delete[] _layers[1].bltInternX;
+	delete[] _layers[0].bltInternY;
+	delete[] _layers[1].bltInternY;
+	delete[] _outBuffer;
+	_dirtyRects.clear();
+}
+
+void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void *pal) {
+	if (layer < 0 || layer > 1)
+		return;
+
+	TownsScreenLayer *l = &_layers[layer];
+
+	if (numCol >> 15)
+		error("TownsScreen::setupLayer(): No more than 32767 colors supported.");
+
+	if (width > _width || height > _height)
+		error("TownsScreen::setupLayer(): Layer width/height must be equal or less than screen width/height");
+	
+	l->scaleW = _width / width;
+	l->scaleH = _height / height;
+
+	if ((float)l->scaleW !=	((float)_width / (float)width) || (float)l->scaleH != ((float)_height / (float)height))
+		error("TownsScreen::setupLayer(): Layer width/height must be equal or an EXACT half, third, etc. of screen width/height.\n More complex aspect ratio scaling is not supported.");
+
+	if (width <= 0 || height <= 0 || numCol < 16)
+		error("TownsScreen::setupLayer(): Invalid width/height/number of colors setting.");
+
+	l->height = height;
+	l->numCol = numCol;
+	l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1;
+	l->pitch = width * l->bpp;
+	l->palette = (uint8*)pal;
+
+	if (l->palette && _bpp == 1)
+		warning("TownsScreen::setupLayer(): Layer palette usage requires 15 bit graphics setting.\nLayer palette will be ignored.");
+
+	delete[] l->pixels;
+	l->pixels = new uint8[l->pitch * l->height];
+	assert(l->pixels);
+	memset(l->pixels, 0, l->pitch * l->height);
+
+	// build offset tables to speed up merging/scaling layers
+	delete[] l->bltInternX;
+	l->bltInternX = new uint16[_width];
+	for (int i = 0; i < _width; ++i)
+		l->bltInternX[i] = (i / l->scaleW) * l->bpp;
+
+	delete[] l->bltInternY;
+	l->bltInternY = new uint8*[_height];
+	for (int i = 0; i < _height; ++i)
+		l->bltInternY[i] = l->pixels + (i / l->scaleH) * l->pitch;
+
+	l->enabled = true;
+	l->onBottom = (!layer || !_layers[0].enabled);
+	l->ready = true;
+}
+	
+void TownsScreen::clearLayer(int layer) {
+	if (layer < 0 || layer > 1)
+		return;
+
+	TownsScreenLayer *l = &_layers[layer];
+	if (!l->ready)
+		return;
+
+	memset(l->pixels, 0, l->pitch * l->height);
+	_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+	_numDirtyRects = FULL_REDRAW;
+}
+
+
+void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) {
+	if (layer < 0 || layer > 1 || w <= 0 || h <= 0)
+		return;
+
+	TownsScreenLayer *l = &_layers[layer];
+	if (!l->ready)
+		return;
+
+	assert(x >= 0 && y >= 0 && ((x + w) * l->bpp) <= (l->pitch) && (y + h) <= (l->height));
+
+	uint8 *pos = l->pixels + y * l->pitch + x * l->bpp;
+	
+	for (int i = 0; i < h; ++i) {
+		if (l->bpp == 2) {
+			for (int ii = 0; ii < w; ++ii) {			
+				WRITE_UINT16(pos, col);
+				pos += 2;
+			}
+			pos += (l->pitch - w * 2);
+		} else {
+			memset(pos, col, w);
+			pos += l->pitch;
+		}
+	}
+	addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH);
+}
+
+uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) {
+	if (layer < 0 || layer > 1)
+		return 0;
+
+	TownsScreenLayer *l = &_layers[layer];
+	if (!l->ready)
+		return 0;
+
+	return l->pixels + y * l->pitch + x * l->bpp;
+}
+
+int TownsScreen::getLayerPitch(int layer) {
+	if (layer >= 0 && layer < 2)
+		return _layers[layer].pitch;
+	return 0;
+}
+
+int TownsScreen::getLayerHeight(int layer) {
+	if (layer >= 0 && layer < 2)
+		return _layers[layer].height;
+	return 0;
+}
+
+int TownsScreen::getLayerBpp(int layer) {
+	if (layer >= 0 && layer < 2)
+		return _layers[layer].bpp;
+	return 0;
+}
+
+int TownsScreen::getLayerScaleW(int layer) {
+	if (layer >= 0 && layer < 2)
+		return _layers[layer].scaleW;
+	return 0;
+}
+
+int TownsScreen::getLayerScaleH(int layer) {
+	if (layer >= 0 && layer < 2)
+		return _layers[layer].scaleH;
+	return 0;
+}
+
+void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
+	if (w <= 0 || h <= 0 || _numDirtyRects > DIRTY_RECTS_MAX)
+		return;	
+	
+	if (_numDirtyRects == DIRTY_RECTS_MAX) {
+		// full redraw
+		_dirtyRects.clear();
+		_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+		_numDirtyRects++;
+		return;
+	}
+
+	int x2 = x + w - 1;
+	int y2 = y + h - 1;
+
+	assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height);
+
+	bool skip = false;
+	for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) {
+		// Try to merge new rect with an existing rect (only once, since trying to merge
+		// more than one overlapping rect would be causing more overhead than doing any good).
+		if (x > r->left && x < r->right && y > r->top && y < r->bottom) {
+			x = r->left;
+			y = r->top;
+			skip = true;
+		}
+		
+		if (x2 > r->left && x2 < r->right && y > r->top && y < r->bottom) {
+			x2 = r->right;
+			y = r->top;
+			skip = true;
+		}
+		
+		if (x2 > r->left && x2 < r->right && y2 > r->top && y2 < r->bottom) {
+			x2 = r->right;
+			y2 = r->bottom;
+			skip = true;
+		}
+		
+		if (x > r->left && x < r->right && y2 > r->top && y2 < r->bottom) {
+			x = r->left;
+			y2 = r->bottom;
+			skip = true;
+		}		
+				
+		if (skip) {
+			r->left = x;
+			r->top = y;
+			r->right = x2;
+			r->bottom = y2;
+			break;
+		}
+	}
+
+	if (!skip) {
+		_dirtyRects.push_back(Common::Rect(x, y, x2, y2));
+		_numDirtyRects++;
+	}
+}
+
+void TownsScreen::toggleLayers(int flag) {
+	if (flag < 0 || flag > 3)
+		return;
+
+	for (int i = 0; i < 2; ++i) {
+		_layers[i].enabled = (flag & (i + 1)) ? true : false;
+		_layers[i].onBottom = (!i || !_layers[0].enabled);
+	}
+
+	_dirtyRects.clear();
+	_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+	_numDirtyRects = FULL_REDRAW;
+
+	memset(_outBuffer, 0, _pitch * _height);
+	updateOutputBuffer();
+	outputToScreen();
+
+	_system->updateScreen();
+}
+
+void TownsScreen::update() {
+	updateOutputBuffer();
+	outputToScreen();
+}
+
+void TownsScreen::updateOutputBuffer() {
+	for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) {
+		for (int i = 0; i < 2; i++) {
+
+			TownsScreenLayer *l = &_layers[i];
+			if (!l->enabled || !l->ready)
+				continue;
+
+			uint8 *dst = _outBuffer + r->top * _pitch + r->left * _bpp;
+			int ptch = _pitch - (r->right - r->left + 1) * _bpp;
+
+			for (int y = r->top; y <= r->bottom; ++y) {
+				if (l->bpp == _bpp && l->scaleW == 1 && l->onBottom) {
+					memcpy(dst, l->bltInternY[y] + l->bltInternX[r->left], (r->right + 1 - r->left) * _bpp);
+					dst += _pitch;
+
+				} else if (_bpp == 2) {
+					for (int x = r->left; x <= r->right; ++x) {
+						uint8 *src = l->bltInternY[y] + l->bltInternX[x];
+						if (l->bpp == 1) {
+							uint8 col = *src;
+							if (col || l->onBottom) {
+								if (l->numCol == 16)
+									col = (col >> 4) & (col & 0x0f);
+								WRITE_LE_UINT16(dst, calc16BitColor(&l->palette[col * 3]));
+							}
+						} else {
+							WRITE_LE_UINT16(dst, READ_LE_UINT16(src));
+						}
+						dst += 2;
+					}
+					dst += ptch;
+
+				} else {
+					for (int x = r->left; x <= r->right; ++x) {
+						uint8 col = *(l->bltInternY[y] + l->bltInternX[x]);
+						if (col || l->onBottom) {
+							if (l->numCol == 16)
+								col = (col >> 4) & (col & 0x0f);						
+							*dst = col;
+						}
+						dst++;
+					}
+					dst += ptch;
+				}				
+			}
+		}
+	}
+}
+
+void TownsScreen::outputToScreen() {
+	for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i)
+		_system->copyRectToScreen(_outBuffer + i->top * _pitch + i->left * _bpp, _pitch, i->left, i->top, i->right - i->left + 1, i->bottom - i->top + 1);
+	_dirtyRects.clear();
+	_numDirtyRects = 0;
+}
+
+uint16 TownsScreen::calc16BitColor(const uint8 *palEntry) {
+	uint16 ar = (palEntry[0] & 0xf8) << 7;
+	uint16 ag = (palEntry[1] & 0xf8) << 2;
+	uint16 ab = (palEntry[2] >> 3);
+	uint16 col = ar | ag | ab;
+	return col;
+}
+
+#undef DIRTY_RECTS_MAX
+#undef FULL_REDRAW
+
+} // End of namespace Scumm


Property changes on: scummvm/trunk/engines/scumm/gfx_towns.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Modified: scummvm/trunk/engines/scumm/module.mk
===================================================================
--- scummvm/trunk/engines/scumm/module.mk	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/module.mk	2010-10-01 19:24:52 UTC (rev 52966)
@@ -16,6 +16,7 @@
 	dialogs.o \
 	file.o \
 	file_nes.o \
+	gfx_towns.o \
 	gfx.o \
 	he/resource_he.o \
 	he/script_v60he.o \

Modified: scummvm/trunk/engines/scumm/palette.cpp
===================================================================
--- scummvm/trunk/engines/scumm/palette.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/palette.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -30,6 +30,7 @@
 #include "scumm/scumm_v6.h"
 #include "scumm/scumm_v8.h"
 #include "scumm/util.h"
+#include "scumm/charset.h"
 
 namespace Scumm {
 
@@ -139,6 +140,22 @@
 		0x00, 0x00, 0x00, 	0x00, 0xFF, 0x00
 	};
 
+#ifdef USE_RGB_COLOR
+	static const byte tableTownsV3Palette[] = {
+		0x00, 0x00, 0x00,	0x00, 0x00, 0xA0,	0x00, 0xA0, 0x00,	0x00, 0xA0, 0xA0,
+		0xA0, 0x00, 0x00,	0xA0, 0x00, 0xA0,	0xA0, 0x60, 0x00,	0xA0, 0xA0, 0xA0,
+		0x60, 0x60, 0x60,	0x60, 0x60, 0xE0,	0x00, 0xE0, 0x00,	0x00, 0xE0, 0xE0,
+		0xE0, 0x80, 0x80,	0xE0, 0x00, 0xE0,	0xE0, 0xE0, 0x00,	0xE0, 0xE0, 0xE0
+	};
+
+	static const byte tableTownsLoomPalette[] = {
+		0x00, 0x00, 0x00,	0x00, 0x00, 0xAB,	0x00, 0xAB, 0x00,	0x00, 0xAB, 0xAB,
+		0xAB, 0x00, 0x00,	0x69, 0x29, 0x45,	0x8C, 0x4D, 0x14,	0xAB, 0xAB, 0xAB,
+		0x57, 0x3F, 0x57,	0x57, 0x57, 0xFF,	0x57, 0xFF, 0x57,	0x57, 0xFF, 0xFF,
+		0xFF, 0x57, 0x57,	0xD6, 0x94, 0x40,	0xFF, 0xFF, 0x57,	0xFF, 0xFF, 0xFF
+	};
+#endif
+
 	if (_game.version <= 1) {
 		if (_game.platform == Common::kPlatformApple2GS) {
 			// TODO: unique palette?
@@ -198,6 +215,17 @@
 			// else we initialise and then lock down the first 16 colors.
 			if (_renderMode != Common::kRenderEGA)
 				setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3);
+		} else if (_game.platform == Common::kPlatformFMTowns) {
+			if (_game.id == GID_INDY4 || _game.id == GID_MONKEY2)
+				_townsClearLayerFlag = 0;
+#ifdef USE_RGB_COLOR
+			else if (_game.id == GID_LOOM)
+				towns_setTextPaletteFromPtr(tableTownsLoomPalette); 
+			else if (_game.version == 3)
+				towns_setTextPaletteFromPtr(tableTownsV3Palette);
+#endif
+			
+			_townsScreen->toggleLayers(_townsActiveLayerFlags);
 		}
 		setDirtyColors(0, 255);
 	}
@@ -465,6 +493,9 @@
 	int valueToAdd;
 	int i, j;
 
+	if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+		return;
+
 	valueToAdd = VAR(VAR_TIMER);
 	if (valueToAdd < VAR(VAR_TIMER_NEXT))
 		valueToAdd = VAR(VAR_TIMER_NEXT);
@@ -506,6 +537,9 @@
 }
 
 void ScummEngine::palManipulateInit(int resID, int start, int end, int time) {
+	if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+		return;
+
 	byte *string1 = getStringAddress(resID);
 	byte *string2 = getStringAddress(resID + 1);
 	byte *string3 = getStringAddress(resID + 2);
@@ -973,6 +1007,10 @@
 	pals = getPalettePtr(_curPalIndex, _roomResource);
 	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
 		setPCEPaletteFromPtr(pals);
+#ifdef USE_RGB_COLOR
+	} else if (_game.platform == Common::kPlatformFMTowns) {
+		towns_setPaletteFromPtr(pals);
+#endif
 	} else {
 		setPaletteFromPtr(pals);
 	}
@@ -1069,10 +1107,21 @@
 		}
 	}
 
-	_system->setPalette(palette_colors, first, num);
-
 	_palDirtyMax = -1;
 	_palDirtyMin = 256;
+
+#ifdef USE_RGB_COLOR
+	if (_game.platform == Common::kPlatformFMTowns) {
+		p = palette_colors;
+		for (i = first; i < first + num; ++i) {
+			_16BitPalette[i] = get16BitColor(p[0], p[1], p[2]);
+			p += 4;
+		}
+		return;
+	}
+#endif
+
+	_system->setPalette(palette_colors, first, num);	
 }
 
 } // End of namespace Scumm

Modified: scummvm/trunk/engines/scumm/room.cpp
===================================================================
--- scummvm/trunk/engines/scumm/room.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/room.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -194,6 +194,9 @@
 	showActors();
 
 	_egoPositioned = false;
+
+	towns_resetPalCycleFields();
+
 	runEntryScript();
 	if (_game.version >= 1 && _game.version <= 2) {
 		runScript(5, 0, 0, 0);

Modified: scummvm/trunk/engines/scumm/saveload.cpp
===================================================================
--- scummvm/trunk/engines/scumm/saveload.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/saveload.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -1294,9 +1294,36 @@
 	//
 	// Save/load palette data
 	//
-	if (_16BitPalette) {
+	if (_16BitPalette && !(_game.platform == Common::kPlatformFMTowns && s->isLoading() && s->getVersion() < VER(82))) {
 		s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16);
 	}
+
+	// FM-Towns specific (extra palette data, color cycle data, etc.)
+	if (s->getVersion() >= VER(82)) {
+		const SaveLoadEntry townsFields[] = {
+			MKLINE(Common::Rect, left, sleInt16, VER(82)),
+			MKLINE(Common::Rect, top, sleInt16, VER(82)),
+			MKLINE(Common::Rect, right, sleInt16, VER(82)),
+			MKLINE(Common::Rect, bottom, sleInt16, VER(82)),
+			MKEND()
+		};
+
+		const SaveLoadEntry townsExtraEntries[] = {
+			MKLINE(ScummEngine, _townsOverrideShadowColor, sleUint8, VER(82)),
+			MKLINE(ScummEngine, _numCyclRects, sleUint8, VER(82)),
+			MKLINE(ScummEngine, _townsPaletteFlags, sleUint8, VER(82)),
+			MKLINE(ScummEngine, _townsClearLayerFlag, sleUint8, VER(82)),
+			MKLINE(ScummEngine, _townsActiveLayerFlags, sleUint8, VER(82)),
+			MKEND()
+		};
+
+		s->saveLoadArrayOf(_textPalette, 48, sizeof(_textPalette[0]), sleUint8);		
+		s->saveLoadArrayOf(_cyclRects, 10, sizeof(_cyclRects[0]), townsFields);
+		s->saveLoadArrayOf(&_curStringRect, 1, sizeof(_curStringRect), townsFields);
+		s->saveLoadArrayOf(_townsCharsetColorMap, 16, sizeof(_townsCharsetColorMap[0]), sleUint8);		
+		s->saveLoadEntries(this, townsExtraEntries);
+	}
+
 	if (_shadowPaletteSize) {
 		s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte);
 		// _roomPalette didn't show up until V21 save games
@@ -1459,6 +1486,17 @@
 
 	// This is probably only needed for Loom.
 	s->saveLoadEntries(this, cursorEntries);
+
+	// Reset cursors for old FM-Towns savegames saved with 256 color setting.
+	// Otherwise the cursor will be messed up when displayed in the new hi color setting.
+	if (_game.platform == Common::kPlatformFMTowns && _bytesPerPixelOutput == 2 && s->isLoading() && s->getVersion() < VER(82)) {
+		if (_game.id == GID_LOOM) {
+			redefineBuiltinCursorFromChar(1, 1);
+			redefineBuiltinCursorHotspot(1, 0, 0);
+		} else {
+			resetCursors();
+		}
+	}
 }
 
 #ifdef ENABLE_SCUMM_7_8

Modified: scummvm/trunk/engines/scumm/saveload.h
===================================================================
--- scummvm/trunk/engines/scumm/saveload.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/saveload.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -50,7 +50,7 @@
  * only saves/loads those which are valid for the version of the savegame
  * which is being loaded/saved currently.
  */
-#define CURRENT_VER 81
+#define CURRENT_VER 82
 
 /**
  * An auxillary macro, used to specify savegame versions. We use this instead

Modified: scummvm/trunk/engines/scumm/script_v4.cpp
===================================================================
--- scummvm/trunk/engines/scumm/script_v4.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/script_v4.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -111,62 +111,15 @@
 	if ((_opcode & 0x1F) == 3) {
 		a = getVarOrDirectWord(PARAM_1);
 
-#if 1
 		if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
-			// FIXME / TODO: OK the first thing to note is: at least in Zak256,
-			// maybe also in other games, this opcode does a bit more. I added
-			// some stubs here, but somebody with a full IDA or more knowledge
-			// about this will have to fill in the gaps. At least now we know
-			// that something is missing here :-)
-
 			if (a == 4) {
-				//printf("o5_oldRoomEffect ODDBALL: _opcode = 0x%x, a = 0x%x\n", _opcode, a);
-				// No idea what byte_2FCCF is, but it's a globale boolean flag.
-				// I only add it here as a temporary hack to make the pseudo code compile.
-				// Maybe it is just there as a reentry protection guard, given
-				// how it is used? It might also correspond to _screenEffectFlag.
-				int byte_2FCCF = 0;
-
-				// For now, we force a redraw of the screen background. This
-				// way the Zak end credits seem to work mostly correct.
-				VirtScreen *vs = &_virtscr[kMainVirtScreen];
-				restoreBackground(Common::Rect(0, vs->topline, vs->w, vs->topline + vs->h));
-				vs->setDirtyRange(0, vs->h);
-				updateDirtyScreen(kMainVirtScreen);
-
-				if (byte_2FCCF) {
-					// Here now "sub_1C44" is called, which sets byte_2FCCF to 0 then
-					// calls yet another sub (which also reads byte_2FCCF):
-
-					byte_2FCCF = 0;
-					//call sub_0BB3
-
-
-					// Now sub_085C is called. This is quite simply: it sets
-					// 0xF000 bytes. starting at 0x40000 to 0. No idea what that
-					// buffer is, maybe a screen buffer, though. Note that
-					// 0xF000 = 320*192.
-					// Maybe this is also the charset mask being cleaned?
-
-					// call sub_085C
-
-
-					// And then sub_1C54 is called, which is almost identical to
-					// the above sub_1C44, only it sets byte_2FCCF to 1:
-
-					byte_2FCCF = 1;
-					// call sub_0BB3
-
-				} else {
-					// Here only sub_085C is called (see comment above)
-
-					// call sub_085C
-				}
-			return;
+				_textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0);
+				if (_townsScreen)
+					_townsScreen->clearLayer(1);
+				return;
 			}
-#endif
-
 		}
+
 		if (a) {
 			_switchRoomEffect = (byte)(a & 0xFF);
 			_switchRoomEffect2 = (byte)(a >> 8);

Modified: scummvm/trunk/engines/scumm/script_v5.cpp
===================================================================
--- scummvm/trunk/engines/scumm/script_v5.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/script_v5.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -1617,7 +1617,7 @@
 
 void ScummEngine_v5::o5_roomOps() {
 	int a = 0, b = 0, c, d, e;
-	const bool paramsBeforeOpcode = (_game.version == 3 && _game.platform != Common::kPlatformPCEngine);
+	const bool paramsBeforeOpcode = (_game.version == 3 && (_game.platform != Common::kPlatformPCEngine || _game.platform != Common::kPlatformFMTowns));
 
 	if (paramsBeforeOpcode) {
 		a = getVarOrDirectWord(PARAM_1);
@@ -1714,21 +1714,51 @@
 		if (a) {
 			if (_game.platform == Common::kPlatformFMTowns) {
 				switch (a) {
-				case 8: // compose kMainVirtScreen over a screen buffer
-				case 9: // call 0x110:0x20 _ax=0x601 _edx=2
-				case 10: // call 0x110:0x20 _ax=0x601 _edx=3
-				case 11: // clear screen 0x1C:0x45000 sizeof(640 * 320)
-				case 12: // call 0x110:0x20 _ax=0x601 _edx=0
-				case 13: // call 0x110:0x20 _ax=0x601 _edx=1
-				case 16: // enable clearing of a screen buffer in drawBitmap()
-				case 17: // disable clearing of a screen buffer in drawBitmap()
-				case 18: // clear a screen buffer
+				case 8:
+					towns_drawStripToScreen(&_virtscr[kMainVirtScreen], 0, _virtscr[kMainVirtScreen].topline, 0, 0, _virtscr[kMainVirtScreen].w, _virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h);
+					_townsScreen->update();
+					return;
+				case 9:
+					_townsActiveLayerFlags = 2;
+					_townsScreen->toggleLayers(_townsActiveLayerFlags);
+					return;
+				case 10:
+					_townsActiveLayerFlags = 3;
+					_townsScreen->toggleLayers(_townsActiveLayerFlags);
+					return;
+				case 11:
+					_townsScreen->clearLayer(1);
+					return;
+				case 12:
+					_townsActiveLayerFlags = 0;
+					_townsScreen->toggleLayers(_townsActiveLayerFlags);
+					return;
+				case 13:
+					_townsActiveLayerFlags = 1;
+					_townsScreen->toggleLayers(_townsActiveLayerFlags);
+					return;
+				case 16: // enable clearing of layer 2 buffer in drawBitmap()
+					_townsPaletteFlags |= 2;
+					return;
+				case 17: // disable clearing of layer 2 buffer in drawBitmap()
+					_townsPaletteFlags &= ~2;
+					return;
+				case 18: // clear kMainVirtScreen layer 2 buffer
+					_textSurface.fillRect(Common::Rect(0, _virtscr[kMainVirtScreen].topline * _textSurfaceMultiplier, _textSurface.pitch, (_virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h) * _textSurfaceMultiplier), 0);
 				case 19: // enable palette operations (palManipulate(), cyclePalette() etc.)
+					_townsPaletteFlags |= 1;
+					return;
 				case 20: // disable palette operations
-				case 21: // disable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
-				case 22: // enable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
+					_townsPaletteFlags &= ~1;
+					return;
+				case 21: // disable clearing of layer 0 in initScreens()
+					_townsClearLayerFlag = 1;
+					return;
+				case 22: // enable clearing of layer 0 in initScreens()
+					_townsClearLayerFlag = 0;
+					return;
 				case 30:
-					debug(0, "o5_roomOps: unhandled FM-TOWNS fadeEffect %d", a);
+					_townsOverrideShadowColor = 3;
 					return;
 				}
 			}

Modified: scummvm/trunk/engines/scumm/scumm-md5.h
===================================================================
--- scummvm/trunk/engines/scumm/scumm-md5.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/scumm-md5.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -117,7 +117,7 @@
 	{ "2723fea3dae0cb47768c424b145ae0e7", "tentacle", "Floppy", "Floppy", 7932, Common::EN_ANY, Common::kPlatformPC },
 	{ "27b2ef1653089fe5b897d9cc89ce784f", "balloon", "HE 80", "", -1, Common::RU_RUS, Common::kPlatformWindows },
 	{ "27b3a4224ad63d5b04627595c1c1a025", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformAmiga },
-	{ "28d24a33448fab6795850bc9f159a4a2", "atlantis", "", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },
+	{ "28d24a33448fab6795850bc9f159a4a2", "atlantis", "FM-TOWNS", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },
 	{ "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformPC },
 	{ "28f07458f1b6c24e118a1ea056827701", "lost", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
 	{ "2a208ffbcd0e83e86f4356e6f64aa6e1", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC },
@@ -183,7 +183,7 @@
 	{ "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown },
 	{ "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown },
 	{ "425205754fa749f4f0b0dd9d09fa45fd", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
-	{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
+	{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
 	{ "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 },
 	{ "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown },
 	{ "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC },
@@ -206,7 +206,7 @@
 	{ "4c4820518e16e1a0e3616a3b021a04f3", "catalog", "HE CUP", "Preview", 10927456, Common::DE_DEU, Common::kPlatformUnknown },
 	{ "4cb9c3618f71668f8e4346c8f323fa82", "monkey2", "", "", 10700, Common::EN_ANY, Common::kPlatformMacintosh },
 	{ "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows },
-	{ "4d34042713958b971cb139fba4658586", "atlantis", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
+	{ "4d34042713958b971cb139fba4658586", "atlantis", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
 	{ "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
 	{ "4dc780f1bc587a193ce8a97652791438", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformAmiga },
 	{ "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", "HE 90", "Demo", 18394, Common::EN_USA, Common::kPlatformUnknown },
@@ -501,7 +501,7 @@
 	{ "c6907d44f1166941d982864cd42cdc89", "pajama2", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
 	{ "c782fbbe74a987c3df8ac73cd3e289ed", "freddi", "HE 73", "", -1, Common::SE_SWE, Common::kPlatformMacintosh },
 	{ "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC },
-	{ "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },
+	{ "c7be10f775404fd9785a8b92a06d240c", "atlantis", "FM-TOWNS", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },
 	{ "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
 	{ "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown },
 	{ "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -552,7 +552,7 @@
 	{ "d8323015ecb8b10bf53474f6e6b0ae33", "dig", "", "", 16304, Common::UNK_LANG, Common::kPlatformUnknown },
 	{ "d917f311a448e3cc7239c31bddb00dd2", "samnmax", "", "CD", 9080, Common::EN_ANY, Common::kPlatformUnknown },
 	{ "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", "", "Demo", 6478, Common::EN_ANY, Common::kPlatformPC },
-	{ "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns },
+	{ "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "FM-TOWNS", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns },
 	{ "da6269b18fcb08189c0aa9c95533cce2", "monkey", "CD", "CD", 8955, Common::IT_ITA, Common::kPlatformPC },
 	{ "da669b20271b85182e9c17a2a37ea02e", "monkey2", "", "", -1, Common::DE_DEU, Common::kPlatformAmiga },
 	{ "db21a6e338fe3b70c2723b6530865bf2", "PuttTime", "HE 85", "", -1, Common::FR_FRA, Common::kPlatformUnknown },

Modified: scummvm/trunk/engines/scumm/scumm.cpp
===================================================================
--- scummvm/trunk/engines/scumm/scumm.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/scumm.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -258,7 +258,7 @@
 	_switchRoomEffect2 = 0;
 	_switchRoomEffect = 0;
 
-	_bytesPerPixel = 1;
+	_bytesPerPixelOutput = _bytesPerPixel = 1;
 	_doEffect = false;
 	_snapScroll = false;
 	_currentLights = 0;
@@ -278,6 +278,7 @@
 	_hePalettes = NULL;
 	_hePaletteSlot = 0;
 	_16BitPalette = NULL;
+	_townsScreen = 0;
 	_shadowPalette = NULL;
 	_shadowPaletteSize = 0;
 	memset(_currentPalette, 0, sizeof(_currentPalette));
@@ -318,6 +319,13 @@
 
 	_skipDrawObject = 0;
 
+	_townsPaletteFlags = 0;
+	_townsClearLayerFlag = 1;
+	_townsActiveLayerFlags = 3;
+	memset(&_curStringRect, -1, sizeof(Common::Rect));
+	memset(&_cyclRects, 0, 16 * sizeof(Common::Rect));
+	_numCyclRects = 0;
+	
 	//
 	// Init all VARS to 0xFF
 	//
@@ -533,16 +541,19 @@
 		_screenHeight = 200;
 	}
 
-	_bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1;
+	_bytesPerPixelOutput = _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1;
 
+#ifdef USE_RGB_COLOR
+	if (_game.platform == Common::kPlatformFMTowns)
+		_bytesPerPixelOutput = 2;
+#endif
+
 	// Allocate gfx compositing buffer (not needed for V7/V8 games).
 	if (_game.version < 7)
-		_compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixel);
+		_compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixelOutput);
 	else
 		_compositeBuf = 0;
 
-	_fmtownsBuf = 0;
-
 	_herculesBuf = 0;
 	if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
 		_herculesBuf = (byte *)malloc(Common::kHercW * Common::kHercH);
@@ -608,8 +619,11 @@
 
 	free(_compositeBuf);
 	free(_herculesBuf);
-	free(_fmtownsBuf);
 
+	free(_16BitPalette);
+
+	delete _townsScreen;
+
 	delete _debugger;
 
 	delete _res;
@@ -1116,14 +1130,19 @@
 			screenWidth *= _textSurfaceMultiplier;
 			screenHeight *= _textSurfaceMultiplier;
 		}
-		if (_game.features & GF_16BIT_COLOR) {
+		if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns) {
 #ifdef USE_RGB_COLOR
 			Graphics::PixelFormat format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 			initGraphics(screenWidth, screenHeight, screenWidth > 320, &format);
 			if (format != _system->getScreenFormat())
 				return Common::kUnsupportedColorMode;
 #else
-			error("16bit color support is required for this game");
+			if (_game.platform == Common::kPlatformFMTowns) {
+				warning("Starting game without the required 16bit color support.\nYou will experience severe color glitches");
+				initGraphics(screenWidth, screenHeight, (screenWidth > 320));
+			} else {
+				error("16bit color support is required for this game");
+			}
 #endif
 		} else {
 			initGraphics(screenWidth, screenHeight, (screenWidth > 320));
@@ -1241,13 +1260,8 @@
 
 	_res->setHeapThreshold(400000, maxHeapThreshold);
 
-	if (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) {
-		free(_fmtownsBuf);
-		_fmtownsBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier);
-	}
-
 	free(_compositeBuf);
-	_compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixel);
+	_compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixelOutput);
 }
 
 #ifdef ENABLE_SCUMM_7_8
@@ -1325,6 +1339,18 @@
 
 	debug(9, "resetScumm");
 
+#ifdef USE_RGB_COLOR
+	if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns)
+		_16BitPalette = (uint16 *)calloc(512, sizeof(uint16));
+#endif
+
+	if (_game.platform == Common::kPlatformFMTowns) {
+		delete _townsScreen;
+		_townsScreen = new TownsScreen(_system, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, _bytesPerPixelOutput);
+		_townsScreen->setupLayer(0, _screenWidth, _screenHeight, (_bytesPerPixelOutput == 2) ? 32767 : 256);
+		_townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 16, _textPalette);
+	}
+
 	if (_game.version == 0) {
 		initScreens(8, 144);
 	} else if ((_game.id == GID_MANIAC) && (_game.version <= 1) && !(_game.platform == Common::kPlatformNES)) {
@@ -1517,8 +1543,6 @@
 
 
 	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
-		_16BitPalette = (uint16 *)calloc(512, sizeof(uint16));
-
 		// Load tile set and palette for the distaff
 		byte *roomptr = getResourceAddress(rtRoom, 90);
 		assert(roomptr);
@@ -1928,6 +1952,10 @@
 	while (!shouldQuit()) {
 		_sound->updateCD(); // Loop CD Audio if needed
 		parseEvents();
+
+		if (_townsScreen)
+			_townsScreen->update();
+
 		_system->updateScreen();
 		if (_system->getMillis() >= start_time + msec_delay)
 			break;
@@ -2055,6 +2083,8 @@
 		goto load_game;
 	}
 
+	towns_processPalCycleField();
+
 	if (_currentRoom == 0) {
 		if (_game.version > 3)
 			CHARSET_1();
@@ -2442,6 +2472,10 @@
 	} else {
 		// Update the screen to make it less likely that the player will see a
 		// brief cursor palette glitch when the GUI is disabled.
+
+		if (_townsScreen)
+			_townsScreen->update();
+
 		_system->updateScreen();
 
 		// Resume sound & video

Modified: scummvm/trunk/engines/scumm/scumm.h
===================================================================
--- scummvm/trunk/engines/scumm/scumm.h	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/scumm.h	2010-10-01 19:24:52 UTC (rev 52966)
@@ -972,6 +972,7 @@
 
 	Common::RenderMode _renderMode;
 	uint8 _bytesPerPixel;
+	uint8 _bytesPerPixelOutput;
 
 protected:
 	ColorCycle _colorCycle[16];	// Palette cycles
@@ -1044,6 +1045,7 @@
 	void setRoomPalette(int pal, int room);
 	void setPCEPaletteFromPtr(const byte *ptr);
 	virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+
 	virtual void setPalColor(int index, int r, int g, int b);
 	void setDirtyColors(int min, int max);
 	const byte *findPalInPals(const byte *pal, int index);
@@ -1077,7 +1079,7 @@
 	// Screen rendering
 	byte *_compositeBuf;
 	byte *_herculesBuf;
-	byte *_fmtownsBuf;
+	
 	virtual void drawDirtyScreenParts();
 	void updateDirtyScreen(VirtScreenNumber slot);
 	void drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b);
@@ -1221,7 +1223,7 @@
 	void restoreCharsetBg();
 	void clearCharsetMask();
 	void clearTextSurface();
-
+	
 	virtual void initCharset(int charset);
 
 	virtual void printString(int m, const byte *msg);
@@ -1397,6 +1399,36 @@
 
 	// Exists both in V7 and in V72HE:
 	byte VAR_NUM_GLOBAL_OBJS;
+
+	// FM-Towns specific
+public:
+	bool towns_isRectInStringBox(int x1, int y1, int x2, int y2);
+	byte _townsPaletteFlags;
+	byte _townsCharsetColorMap[16];
+
+protected:
+	void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
+#ifdef USE_RGB_COLOR
+	void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+	void towns_setTextPaletteFromPtr(const byte *ptr);
+#endif
+	void towns_setupPalCycleField(int x1, int y1, int x2, int y2);
+	void towns_processPalCycleField();
+	void towns_resetPalCycleFields();
+	void towns_restoreCharsetBg();
+
+	Common::Rect _cyclRects[16];
+	int _numCyclRects;
+	
+	Common::Rect _curStringRect;
+
+	byte _townsOverrideShadowColor;	
+	byte _textPalette[48];
+	byte _townsClearLayerFlag;
+	byte _townsActiveLayerFlags;
+	static const uint8 _townsLayer2Mask[];
+
+	TownsScreen *_townsScreen;
 };
 
 } // End of namespace Scumm

Modified: scummvm/trunk/engines/scumm/string.cpp
===================================================================
--- scummvm/trunk/engines/scumm/string.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/string.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -508,6 +508,9 @@
 	if (_game.version >= 5)
 		memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
 
+	if (_keepText && _game.platform == Common::kPlatformFMTowns)
+		memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect));
+
 	if (_talkDelay)
 		return;
 
@@ -539,7 +542,10 @@
 			_nextTop = _string[0].ypos + _screenTop;
 #endif
 		} else {
-			restoreCharsetBg();
+			if (_game.platform == Common::kPlatformFMTowns)
+				towns_restoreCharsetBg();
+			else
+				restoreCharsetBg();
 		}
 	}
 
@@ -660,6 +666,9 @@
 		}
 	}
 
+	if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3))
+		memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect));
+
 #ifdef ENABLE_SCUMM_7_8
 	if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
 		((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());

Modified: scummvm/trunk/engines/scumm/verbs.cpp
===================================================================
--- scummvm/trunk/engines/scumm/verbs.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/engines/scumm/verbs.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -1451,9 +1451,10 @@
 	VerbSlot *vs;
 
 	vs = &_verbs[verb];
+	uint8 col = ((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 : vs->bkcolor;
 
 	if (vs->oldRect.left != -1) {
-		restoreBackground(vs->oldRect, vs->bkcolor);
+		restoreBackground(vs->oldRect, col);
 		vs->oldRect.left = -1;
 	}
 }

Modified: scummvm/trunk/graphics/surface.cpp
===================================================================
--- scummvm/trunk/graphics/surface.cpp	2010-10-01 19:16:09 UTC (rev 52965)
+++ scummvm/trunk/graphics/surface.cpp	2010-10-01 19:24:52 UTC (rev 52966)
@@ -24,6 +24,7 @@
 
 #include "common/algorithm.h"
 #include "common/util.h"
+#include "common/endian.h"
 #include "graphics/primitives.h"
 #include "graphics/surface.h"
 
@@ -185,9 +186,6 @@
 // to support 16bpp (or marked as just working for 8bpp
 // surfaces).
 void Surface::move(int dx, int dy, int height) {
-	// This function currently just works with 8bpp surfaces
-	assert(bytesPerPixel == 1);
-
 	// Short circuit check - do we have to do anything anyway?
 	if ((dx == 0 && dy == 0) || height <= 0)
 		return;
@@ -198,46 +196,58 @@
 	// vertical movement
 	if (dy > 0) {
 		// move down - copy from bottom to top
-		dst = (byte *)pixels + (height - 1) * w;
-		src = dst - dy * w;
+		dst = (byte *)pixels + (height - 1) * pitch;
+		src = dst - dy * pitch;
 		for (y = dy; y < height; y++) {
-			memcpy(dst, src, w);
-			src -= w;
-			dst -= w;
+			memcpy(dst, src, pitch);
+			src -= pitch;
+			dst -= pitch;
 		}
 	} else if (dy < 0) {
 		// move up - copy from top to bottom
 		dst = (byte *)pixels;
-		src = dst - dy * w;
+		src = dst - dy * pitch;
 		for (y = -dy; y < height; y++) {
-			memcpy(dst, src, w);
-			src += w;
-			dst += w;
+			memcpy(dst, src, pitch);
+			src += pitch;
+			dst += pitch;
 		}
 	}
 
 	// horizontal movement
 	if (dx > 0) {
 		// move right - copy from right to left
-		dst = (byte *)pixels + (w - 1);
-		src = dst - dx;
+		dst = (byte *)pixels + (pitch - bytesPerPixel);
+		src = dst - (dx * bytesPerPixel);
 		for (y = 0; y < height; y++) {
 			for (x = dx; x < w; x++) {
-				*dst-- = *src--;
+				if (bytesPerPixel == 1) {
+					*dst-- = *src--;
+				} else if (bytesPerPixel == 2) {
+					WRITE_LE_UINT16(dst, READ_LE_UINT16(src));
+					src -= 2;
+					dst -= 2;
+				}
 			}
-			src += w + (w - dx);
-			dst += w + (w - dx);
+			src += pitch + (pitch - dx * bytesPerPixel);
+			dst += pitch + (pitch - dx * bytesPerPixel);
 		}
 	} else if (dx < 0)  {
 		// move left - copy from left to right
 		dst = (byte *)pixels;
-		src = dst - dx;
+		src = dst - (dx * bytesPerPixel);
 		for (y = 0; y < height; y++) {
 			for (x = -dx; x < w; x++) {
-				*dst++ = *src++;
+				if (bytesPerPixel == 1) {
+					*dst++ = *src++;
+				} else if (bytesPerPixel == 2) {
+					WRITE_LE_UINT16(dst, READ_LE_UINT16(src));
+					src += 2;
+					dst += 2;
+				}
 			}
-			src += w - (w + dx);
-			dst += w - (w + dx);
+			src += pitch - (pitch + dx * bytesPerPixel);
+			dst += pitch - (pitch + dx * bytesPerPixel);
 		}
 	}
 }


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list