[Scummvm-git-logs] scummvm master -> ecb03f2e7c6bed78af5dffb1a6fee8d586007666

athrxx noreply at scummvm.org
Sat May 4 15:07:15 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ecb03f2e7c SCUMM: (V3/FM-Towns) - fix bug no. 15027


Commit: ecb03f2e7c6bed78af5dffb1a6fee8d586007666
    https://github.com/scummvm/scummvm/commit/ecb03f2e7c6bed78af5dffb1a6fee8d586007666
Author: athrxx (athrxx at scummvm.org)
Date: 2024-05-04T16:43:06+02:00

Commit Message:
SCUMM: (V3/FM-Towns) - fix bug no. 15027

("INDY3 (FMTowns): Map lines are drawn incorrectly, plus more
issues when leaving Germany")

The bug with the wrong colors is caused by the shadow palette, which
gets set with the colors for the modified country borders, but gets
reset immediately after drawing the background. So when the map
travel marker makes an area dirty, it would be restored with the wrong
color. Now, the original V3 FM-Towns interpreter can draw on layer 2
directly, without declaring anything dirty, effectively bypassing the
normal SCUMM drawing. This avoids any change to the background.
This happens for drawBox, but also when displaying or hiding the pause
banner.

In SCUMM5 drawBox works the way we had it implemented, drawing
on the text surface (MI1: also on the vs buffer) and then making that
rect dirty.

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/gfx.h
    engines/scumm/gfx_gui.cpp
    engines/scumm/gfx_towns.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 85ef120de1f..dd32a51be76 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1313,11 +1313,7 @@ void ScummEngine::clearCharsetMask() {
 }
 
 void ScummEngine::clearTextSurface() {
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-	if (_townsScreen)
-		_townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0);
-#endif
-
+	towns_fillTopLayerRect(0, 0, _textSurface.w, _textSurface.h, 0);
 	fill((byte *)_textSurface.getPixels(),  _textSurface.pitch,
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 		_game.platform == Common::kPlatformFMTowns ? 0 :
@@ -1507,7 +1503,14 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
 	if (width <= 0 || height <= 0)
 		return;
 
-	markRectAsDirty(vs->number, x, x2, y, y2);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+	// Some FM-Towns games draw directly on layer 2, without setting the virtscreen dirty, bypassing the normal drawing
+	// routines. It can make a difference, e. g. bug no. 15027 ("INDY3 (FMTowns): Map lines are drawn incorrectly, plus
+	// more issues when leaving Germany"). Making the virtscreen dirty, would cause some wrong colors, due to the way the
+	// scripts handle the shadow palette there.
+	if (!_townsScreen || _game.platform != Common::kPlatformFMTowns || _game.version != 3 || vs->number == kTextVirtScreen)
+#endif
+		markRectAsDirty(vs->number, x, x2, y, y2);
 
 	backbuff = vs->getPixels(x, y);
 	bgbuff = vs->getBackPixels(x, y);
@@ -1581,11 +1584,25 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
 						height++;
 				}
 
-				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.format.bytesPerPixel);
+				// Some FM-Towns games draw directly on layer 2, without setting the virtscreeb dirty, bypassing the
+				// normal drawing routines. It can make a difference, e. g. bug no. 15027 ("INDY3 (FMTowns): Map lines
+				// are drawn incorrectly, plus more issues when leaving Germany"). Making the virtscreen dirty, would
+				// cause some wrong colors, due to the way the scripts handle the shadow palette there.
+				if (_game.version == 3 && vs->number != kTextVirtScreen) {
+					towns_fillTopLayerRect(x * _textSurfaceMultiplier,
+						(y - _screenTop + vs->topline) * _textSurfaceMultiplier, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, color);
+					return;
+				}
+
+				if (vs->number == kBannerVirtScreen) {
+					byte *mask = _virtscr[kBannerVirtScreen].getPixels(x, y);
+					fill(mask, vs->pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, vs->format.bytesPerPixel);
+				} else {
+					byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop + vs->topline) * _textSurfaceMultiplier);
+					fill(mask, _textSurface.pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.format.bytesPerPixel);
+				}
 
-				if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4 || ((_game.id == GID_INDY3 || _game.id == GID_ZAK) && vs->number != kTextVirtScreen) || (_game.id == GID_LOOM && vs->number == kMainVirtScreen))
+				if (_game.id != GID_MONKEY && !(_game.version == 3 && vs->number == kTextVirtScreen))
 					return;
 			}
 #endif
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index be4816db700..ce0c62eda0c 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -563,7 +563,7 @@ public:
 class TownsScreen {
 public:
 	enum {
-		kDirtyRectsMax = 20,
+		kDirtyRectsMax = 50,
 		kFullRedraw = (kDirtyRectsMax + 1)
 	};
 public:
@@ -572,7 +572,8 @@ public:
 
 	void setupLayer(int layer, int width, int height, int scaleW, int scaleH, int numCol, void *srcPal = 0);
 	void clearLayer(int layer);
-	void fillLayerRect(int layer, int x, int y, int w, int h, int col);
+	void fillRect(int layer, int x, int y, int w, int h, int col);
+	void swapAreaWithBuffer(int layer, int x, int y, int w, int h, byte *buffer);
 	void addDirtyRect(int x, int y, int w, int h);
 	void toggleLayers(int flags);
 	void scrollLayer(int layer, int offset, int top, int bottom, bool fast);
diff --git a/engines/scumm/gfx_gui.cpp b/engines/scumm/gfx_gui.cpp
index f8614b61745..8bac9901ac3 100644
--- a/engines/scumm/gfx_gui.cpp
+++ b/engines/scumm/gfx_gui.cpp
@@ -500,8 +500,18 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co
 
 	_forceBannerVirtScreen = false;
 
-	drawDirtyScreenParts();
-	updateDirtyScreen(kBannerVirtScreen);
+	if (_game.platform == Common::kPlatformFMTowns) {
+		// FM-Towns games just exchange the vs content with the respective screen layer area
+		// without making any virtscreen strips dirty. It can make a difference, e. g. in bug
+		// no. 15027 ("INDY3 (FMTowns): Map lines are drawn incorrectly, plus more issues when
+		// leaving Germany"). Making the virtscreen dirty, would cause some wrong colors, due
+		// to the way the scripts handle the shadow palette there.
+		VirtScreen *vs = &_virtscr[kBannerVirtScreen];
+		towns_swapVirtScreenArea(vs, 0, vs->topline * _textSurfaceMultiplier, vs->w, vs->h);
+	} else {
+		drawDirtyScreenParts();
+		updateDirtyScreen(kBannerVirtScreen);
+	}
 
 	// Wait until the engine receives a new Keyboard or Mouse input,
 	// unless we have specified a positive waitTime: in that case, the banner
@@ -510,10 +520,15 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co
 	bool leftBtnPressed = false, rightBtnPressed = false;
 	if (waitTime) {
 		waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed);
-		memset(_virtscr[kBannerVirtScreen].getBasePtr(0, 0), 0, _virtscr[kBannerVirtScreen].w * _virtscr[kBannerVirtScreen].h);
-		_virtscr[kBannerVirtScreen].setDirtyRange(0, _virtscr[kBannerVirtScreen].h);
-		updateDirtyScreen(kBannerVirtScreen);
-		_virtscr[kMainVirtScreen].setDirtyRange(startingPointY - _virtscr[kMainVirtScreen].topline, startingPointY - _virtscr[kMainVirtScreen].topline + _virtscr[kBannerVirtScreen].h);
+		if (_game.platform == Common::kPlatformFMTowns) {
+			VirtScreen *vs = &_virtscr[kBannerVirtScreen];
+			towns_swapVirtScreenArea(vs, 0, vs->topline * _textSurfaceMultiplier, vs->w, vs->h);
+		} else {
+			memset(_virtscr[kBannerVirtScreen].getBasePtr(0, 0), 0, _virtscr[kBannerVirtScreen].w * _virtscr[kBannerVirtScreen].h);
+			_virtscr[kBannerVirtScreen].setDirtyRange(0, _virtscr[kBannerVirtScreen].h);
+			updateDirtyScreen(kBannerVirtScreen);
+			_virtscr[kMainVirtScreen].setDirtyRange(startingPointY - _virtscr[kMainVirtScreen].topline, startingPointY - _virtscr[kMainVirtScreen].topline + _virtscr[kBannerVirtScreen].h);
+		}
 	}
 
 	// Restore the text surface...
diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp
index e872f5cc510..699f6830cfa 100644
--- a/engines/scumm/gfx_towns.cpp
+++ b/engines/scumm/gfx_towns.cpp
@@ -99,13 +99,12 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
 				uint16 *d = reinterpret_cast<uint16*>(dst1);
 				for (int w = 0; w < width; ++w) {
 					t = (*src1++) & 0x0f;
-					t |= (t << 4);
 					*d++ = (t << 8) | t;
 				}
 			} else if (m == 1) {
 				for (int w = 0; w < width; ++w) {
 					t = (*src1++) & 0x0f;
-					*dst1++ = (t << 4) | t;
+					*dst1++ = t;
 				}
 			} else {
 				error ("ScummEngine::towns_drawStripToScreen(): Unexpected text surface multiplier %d", m);
@@ -122,8 +121,8 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
 					src3 += lp1;
 					for (int w = 0; w < (width << 1); ++w) {
 						t = *dst1;
-						s2 = *src2++;
-						s3 = *src3++;
+						s2 = *src2++ & 0x0f;
+						s3 = *src3++ & 0x0f;
 						*dst2++ = (s3 | (t & _townsLayer2Mask[s3]));
 						*dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
 					}
@@ -132,7 +131,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
 					src3 += width;
 					for (int w = 0; w < width; ++w) {
 						t = *dst1;
-						s2 = *src2++;
+						s2 = *src2++ & 0x0f;
 						*dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
 					}
 				} else {
@@ -149,6 +148,20 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
 	_townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m);
 }
 
+
+void ScummEngine::towns_fillTopLayerRect(int x1, int y1, int x2, int y2, int col) {
+	if (!_townsScreen)
+		return;
+	_townsScreen->fillRect(1, x1, y1, x2, y2, col);
+}
+
+void ScummEngine::towns_swapVirtScreenArea(VirtScreen *vs, int x, int y, int w, int h) {
+	if (!_townsScreen)
+		return;
+
+	_townsScreen->swapAreaWithBuffer(1, x, y, w, h, vs->getPixels(0, 0));
+}
+
 void ScummEngine::towns_clearStrip(int strip) {
 	if (!_townsScreen)
 		return;
@@ -346,22 +359,7 @@ void ScummEngine::towns_resetPalCycleFields() {
 }
 
 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
+	0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
 TownsScreen::TownsScreen(OSystem *system) :	_system(system), _width(0), _height(0), _pixelFormat(system->getScreenFormat()), _numDirtyRects(0) {
@@ -433,7 +431,7 @@ void TownsScreen::clearLayer(int layer) {
 }
 
 
-void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) {
+void TownsScreen::fillRect(int layer, int x, int y, int w, int h, int col) {
 	if ((layer & ~1) || w <= 0 || h <= 0)
 		return;
 
@@ -460,6 +458,35 @@ void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col)
 	addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH);
 }
 
+void TownsScreen::swapAreaWithBuffer(int layer, int x, int y, int w, int h, byte *buffer) {
+	if ((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) {
+				SWAP(*(uint16*)buffer, *(uint16*)pos);
+				pos += 2;
+				buffer += 2;
+			}
+			pos += (l->pitch - w * 2);
+		} else {
+			for (int ii = 0; ii < w; ++ii)
+				SWAP(*buffer++, *pos++);
+			pos += (l->pitch - w);
+		}
+	}
+	addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH);
+}
+
 uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) const {
 	if (layer & ~1)
 		return nullptr;
@@ -486,42 +513,25 @@ void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
 	int x2 = x + w - 1;
 	int y2 = y + h - 1;
 
-	assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height);
+	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 (y == r->top && y2 == r->bottom) {
+			if ((x >= r->left && x <= r->right) || (x2 >= r->left - 1 && x2 <= r->right)) {
+				r->left = MIN<int>(x, r->left);
+				r->right = MAX<int>(x2, r->right);
+				skip = true;
+			}
 		}
-
-		if (skip) {
-			r->left = x;
-			r->top = y;
-			r->right = x2;
-			r->bottom = y2;
-			break;
+		if (x == r->left && x2 == r->right) {
+			if ((y >= r->top && y <= r->bottom) || (y2 >= r->top - 1 && y2 <= r->bottom)) {
+				r->top = MIN<int>(y, r->top);
+				r->bottom = MAX<int>(y2, r->bottom);
+				skip = true;
+			}
 		}
 	}
 
@@ -632,7 +642,7 @@ template<typename dstPixelType, typename srcPixelType, int scaleW, int scaleH, b
 				if (sizeof(srcPixelType) == 1) {
 					if (col || l->onBottom) {
 						if (srcCol4bit)
-							col = (col >> 4) & (col & 0x0f);
+							col = col & 0x0f;
 						dstPixelType col2 = l->bltTmpPal[col];
 						*dst10a = col2;
 						if (scaleW == 2)
@@ -657,7 +667,7 @@ template<typename dstPixelType, typename srcPixelType, int scaleW, int scaleH, b
 			} else {
 				if (col || l->onBottom) {
 					if (srcCol4bit)
-						col = (col >> 4) & (col & 0x0f);
+						col = col & 0x0f;
 					*dst10a = col;
 					if (scaleW == 2)
 						*++dst10a = col;
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 7d13ec783b5..674b4f2f0cd 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1867,6 +1867,8 @@ public:
 
 protected:
 	void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
+	void towns_fillTopLayerRect(int x1, int y1, int x2, int y2, int col);
+	void towns_swapVirtScreenArea(VirtScreen *vs, int x, int y, int w, int h);
 	void towns_clearStrip(int strip);
 #ifdef USE_RGB_COLOR
 	void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1);
@@ -1912,6 +1914,8 @@ protected:
 	void scrollRight() { redrawBGStrip(0, 1); }
 	void towns_updateGfx() {}
 	void towns_waitForScroll(int waitForDirection, int threshold = 0) {}
+	void towns_fillTopLayerRect(int x1, int y1, int x2, int y2, int col) {}
+	void towns_swapVirtScreenArea(VirtScreen *vs, int x, int y, int w, int h);
 #endif // DISABLE_TOWNS_DUAL_LAYER_MODE
 };
 




More information about the Scummvm-git-logs mailing list