[Scummvm-git-logs] scummvm master -> b868dab4c56f62fb6890fedae9c4fd9cd810fb3c
athrxx
noreply at scummvm.org
Tue Apr 30 21:42:24 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:
b868dab4c5 SCUMM: (V3/FM-Towns) - partial fix for bug no. 15027
Commit: b868dab4c56f62fb6890fedae9c4fd9cd810fb3c
https://github.com/scummvm/scummvm/commit/b868dab4c56f62fb6890fedae9c4fd9cd810fb3c
Author: athrxx (athrxx at scummvm.org)
Date: 2024-04-30T23:29:53+02:00
Commit Message:
SCUMM: (V3/FM-Towns) - partial fix for bug no. 15027
(INDY3 (FMTowns): Map lines are drawn incorrectly)
This fix addresses the part concerning the line width of the travel
marker. Turns out that SCUMM3 FM-Towns draws all boxes one
pixel wider and higher on virtual screens other than 1. This also
allows removal of a hack in ScummEngine_v5::o5_drawBox().
I also fixed the old style pause/restart banners, since they incorrectly
used drawBox() for line drawing (which would be glitchy with the now
verdrawing drawBox(). The positive outcome is that we no have original
looking FM-Towns banners (only in Japanese, though, since - unlike the
original - we don't run the English version in 640x480).
The changed method of drawing and removing the banner also fixes
the removal of the black country borders on the map. But it will still
be removed below the pause banner and in the spots where the travel
marker passes the border. So that has to be addressed separately.
Changed paths:
engines/scumm/charset.cpp
engines/scumm/gfx.cpp
engines/scumm/gfx.h
engines/scumm/gfx_gui.cpp
engines/scumm/gfx_towns.cpp
engines/scumm/script_v5.cpp
engines/scumm/scumm.h
engines/scumm/verbs.cpp
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index a432210f2ab..e2c113d774a 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -971,6 +971,10 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
#endif
)
drawBits1(*vs, _left + vs->xstart, drawTop, charPtr, drawTop, origWidth, origHeight);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ else if (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kBannerVirtScreen)
+ drawBits1(*vs, _left * _vm->_textSurfaceMultiplier, drawTop * _vm->_textSurfaceMultiplier, charPtr, drawTop, origWidth, origHeight);
+#endif
else
drawBits1(_vm->_textSurface, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, charPtr, drawTop, origWidth, origHeight);
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 016bf2bf1cd..9a080806817 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -338,7 +338,7 @@ void ScummEngine::initScreens(int b, int h) {
int i;
int adj = 0;
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 4; i++) {
_res->nukeResource(rtBuffer, i + 1);
_res->nukeResource(rtBuffer, i + 5);
}
@@ -360,18 +360,15 @@ void ScummEngine::initScreens(int b, int h) {
clearTextSurface();
}
- if (!getResourceAddress(rtBuffer, 4)) {
- // Since the size of screen 3 is fixed, there is no need to reallocate
- // it if its size changed.
- // Not sure what it is good for, though. I think it may have been used
- // in pre-V7 for the games messages (like 'Pause', Yes/No dialogs,
- // version display, etc.). I don't know about V7, maybe the same is the
- // case there. If so, we could probably just remove it completely.
- if (_game.version >= 7) {
- initVirtScreen(kUnkVirtScreen, (_screenHeight / 2) - 10, _screenWidth, 13, false, false);
- } else {
- initVirtScreen(kUnkVirtScreen, 80, _screenWidth, 13, false, false);
- }
+ if (_game.version >= 7) {
+ initVirtScreen(kBannerVirtScreen, (_screenHeight / 2) - 10, _screenWidth, 13, false, false);
+ } else if (_game.platform == Common::kPlatformFMTowns) {
+ // HACK: The original only ever renders in 640x480 mode. The banners' top and bottom borders are exactly one unscaled pixel high. This will
+ // still allow the text to fit in nicely. It does not work with scaled (2 pixel height) borders, though. So we add two extra pixels...
+ int bannerHeight = (_textSurfaceMultiplier == 1) ? 12 : 20;
+ initVirtScreen(kBannerVirtScreen, (b + adj + h) / 2 - bannerHeight / _textSurfaceMultiplier, _screenWidth * _textSurfaceMultiplier, bannerHeight, false, false);
+ } else {
+ initVirtScreen(kBannerVirtScreen, 80, _screenWidth, 12, false, false);
}
if ((_game.platform == Common::kPlatformNES) && (h != _screenHeight)) {
@@ -379,7 +376,7 @@ void ScummEngine::initScreens(int b, int h) {
// Otherwise we would have to do lots of coordinate adjustments all over
// the code.
adj = 16;
- initVirtScreen(kUnkVirtScreen, 0, _screenWidth, adj, false, false);
+ initVirtScreen(kBannerVirtScreen, 0, _screenWidth, adj, false, false);
}
initVirtScreen(kMainVirtScreen, b + adj, _screenWidth, h - b, true, true);
@@ -463,6 +460,9 @@ VirtScreen *ScummEngine::findVirtScreen(int y) {
VirtScreen *vs = _virtscr;
int i;
+ if (_forceBannerVirtScreen)
+ return &vs[3];
+
for (i = 0; i < 3; i++, vs++) {
if (y >= vs->topline && y < vs->topline + vs->h) {
return vs;
@@ -609,7 +609,13 @@ void ScummEngine::updateDirtyScreen(VirtScreenNumber slot) {
w += 8;
continue;
}
- drawStripToScreen(vs, start * 8, w, top, bottom);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && vs->number == kBannerVirtScreen) {
+ int scl = _textSurfaceMultiplier;
+ towns_drawStripToScreen(vs, start * 8 * scl, (vs->topline + top) * scl, start * 8 * scl, top * scl, w * scl, bottom - top);
+ } else
+#endif
+ drawStripToScreen(vs, start * 8, w, top, bottom);
w = 8;
}
start = i + 1;
@@ -1565,6 +1571,16 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
} else {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
+ if (_game.version == 3 && vs->number != kTextVirtScreen) {
+ // The original FM-Towns v3 interpreter overdraws both width
+ // and height by 1 pixel. I don't know if it is voluntary or
+ // not, so I have added safety checks.
+ if (x + width < vs->w)
+ width++;
+ if (y + height < vs->h)
+ 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);
@@ -1585,11 +1601,6 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
}
void ScummEngine::drawLine(int x1, int y1, int x2, int y2, int color) {
- if (_game.platform == Common::kPlatformFMTowns) {
- drawBox(x1, y1, x2, y2, color);
- return;
- }
-
int effColor, black, white;
int effX1, effY1;
int width, height, widthAccumulator, heightAccumulator, horizontalStrips, originalHeight;
@@ -1667,12 +1678,17 @@ void ScummEngine::drawLine(int x1, int y1, int x2, int y2, int color) {
void ScummEngine::drawPixel(VirtScreen *vs, int x, int y, int16 color, bool useBackbuffer) {
int factor = _isIndy4Jap ? 0 : 8;
+ int wScale = (vs->number == kBannerVirtScreen && _textSurfaceMultiplier == 2) ? 2 : 1;
if (x >= 0 && y >= 0 && _screenWidth + factor > x && _screenHeight > y) {
- if (useBackbuffer)
+ if (useBackbuffer) {
*(vs->getBackPixels(x, y + _screenTop - vs->topline)) = color;
- else
- *(vs->getPixels(x, y + _screenTop - vs->topline)) = color;
- markRectAsDirty(vs->number, x, x + 1, y + _screenTop - vs->topline, y + 1 + _screenTop - vs->topline);
+ } else {
+ // Is it elegant to do the kBannerVirtScreen horizontal scaling here like this? Certainly not,
+ // but it is just what the original interpreter does. So it will at least not break anything.
+ for (int i = 0; i < wScale; ++i)
+ *(vs->getPixels(x * wScale + i, y + _screenTop - vs->topline)) = color;
+ }
+ markRectAsDirty(vs->number, x * wScale, (x + 1) * wScale, y + _screenTop - vs->topline, y + 1 + _screenTop - vs->topline);
}
}
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 6b2995f77cd..be4816db700 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -162,7 +162,7 @@ enum VirtScreenNumber {
kMainVirtScreen = 0, // The 'stage'
kTextVirtScreen = 1, // In V0-V3 games: the area where text is printed
kVerbVirtScreen = 2, // The verb area
- kUnkVirtScreen = 3 // ?? Not sure what this one is good for...
+ kBannerVirtScreen = 3 // The centered pause/restart/message banner area
};
/**
@@ -179,12 +179,6 @@ enum VirtScreenNumber {
* Finally, in V5 games and some V6 games, it's almost the same as in the
* original games, except that there is no separate conversation area.
*
- * If you now wonder what the last screen is/was good for: I am not 100% sure,
- * but it appears that it was used by the original engine to display stuff
- * like the pause message, or questions ("Do you really want to restart?").
- * It seems that it is not used at all by ScummVM, so we probably could just
- * get rid of it and save a couple kilobytes of RAM.
- *
* Each of these virtual screens has a fixed number or id (see also
* \ref VirtScreenNumber).
*/
diff --git a/engines/scumm/gfx_gui.cpp b/engines/scumm/gfx_gui.cpp
index aa3299c0ecf..f8614b61745 100644
--- a/engines/scumm/gfx_gui.cpp
+++ b/engines/scumm/gfx_gui.cpp
@@ -246,7 +246,6 @@ Common::KeyState ScummEngine::showBannerAndPause(int bannerId, int32 waitTime, c
_bannerSaveYStart /= _textSurfaceMultiplier;
}
#endif
-
memcpy(
_bannerMem,
&_virtscr[kMainVirtScreen].getPixels(0, _screenTop)[rowSize * _bannerSaveYStart],
@@ -437,7 +436,6 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co
int startingPointY;
int boxColor;
int textXPos, textYPos;
- bool isV3Towns = (_game.platform == Common::kPlatformFMTowns && _game.version == 3);
_messageBannerActive = true;
@@ -459,89 +457,51 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co
// Pause the engine
PauseToken pt = pauseEngine();
+ _forceBannerVirtScreen = true;
+
// Backup the current charsetId...
int oldId = _charset->getCurID();
_charset->setCurID(_game.version > 3 ? 1 : 0);
// Take all the necessary measurements for the box which
// will contain the string...
- bannerMsgHeight = getGUIStringHeight(bannerMsg) + 3;
+ bannerMsgHeight = _virtscr[kBannerVirtScreen].h;
bannerMsgWidth = getGUIStringWidth(bannerMsg);
if (bannerMsgWidth < 100)
bannerMsgWidth = 100;
- startingPointY = 80;
+ startingPointY = _virtscr[kBannerVirtScreen].topline;
boxColor = 0;
textXPos = _screenWidth / 2;
- textYPos = startingPointY + 2;
-
- if (isV3Towns) {
- boxColor = 8;
- textXPos = (320 - bannerMsgWidth) / 2;
- textYPos = 2 + (_virtscr[kMainVirtScreen].h + _virtscr[kMainVirtScreen].topline - (bannerMsgHeight - 6)) / 2;
-
- // Game specific corrections
- if (_game.id == GID_INDY3)
- textXPos += 8;
- if (_game.id == GID_LOOM)
- textYPos -= 8;
+ textYPos = startingPointY + 2 / _textSurfaceMultiplier;
- startingPointY = textYPos - 2;
+ if (_game.platform == Common::kPlatformFMTowns) {
+ // We replicate an original text centering bug here. The function that measures
+ // the text width in Indy 3 FM-Towns JP adds 10 pixels to the width for each
+ // character (instead of 8, like the other FM-Towns games do it). Fortunately,
+ // that text width measuring function is only used here...
+ if (_game.id == GID_INDY3 && _useCJKMode)
+ bannerMsgWidth = MIN<int>(bannerMsgWidth * 10 / 8, (_screenWidth - 10));
- if (_useCJKMode) {
- textXPos -= _game.id == GID_INDY3 ? 34 : 8;
- }
-
- _bannerSaveYStart = startingPointY;
- } else {
- _bannerSaveYStart = startingPointY - (_game.version == 4 ? 2 : _virtscr[kMainVirtScreen].topline);
- }
-
- // Save the pixels which will be overwritten by the banner,
- // so that we can restore them later...
- if (!_bannerMem) {
- int rowSize = _screenWidth + (_game.version == 4 ? 8 : 0);
-
- // FM-Towns games draw the banner on the text surface, so let's save that
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_game.platform == Common::kPlatformFMTowns && !_textSurfBannerMem) {
- rowSize *= _textSurfaceMultiplier;
- startingPointY *= _textSurfaceMultiplier;
- _textSurfBannerMemSize = (bannerMsgHeight + 2) * rowSize * _textSurfaceMultiplier;
- _textSurfBannerMem = (byte *)malloc(_textSurfBannerMemSize * sizeof(byte));
- if (_textSurfBannerMem) {
- memcpy(
- _textSurfBannerMem,
- &((byte *)_textSurface.getBasePtr(0, _screenTop * _textSurfaceMultiplier))[rowSize * startingPointY],
- _textSurfBannerMemSize);
- }
-
- // We're going to use these same values for saving the
- // virtual screen surface, so let's un-multiply them...
- rowSize /= _textSurfaceMultiplier;
- startingPointY /= _textSurfaceMultiplier;
- }
-#endif
+ boxColor = 8;
+ textXPos = (_screenWidth - bannerMsgWidth) / 2;
- _bannerMemSize = (bannerMsgHeight + 2) * (rowSize);
- _bannerMem = (byte *)malloc(_bannerMemSize * sizeof(byte));
- if (_bannerMem) {
- memcpy(
- _bannerMem,
- &_virtscr[kMainVirtScreen].getPixels(0, _screenTop)[rowSize * _bannerSaveYStart],
- _bannerMemSize);
- }
+ // Loom and Zak FM-Towns do this in drawChar(). Indy 3 doesn't.
+ if (_game.id != GID_INDY3 && _useCJKMode)
+ textXPos -= 8;
}
// Draw the GUI control
- drawBox(0, startingPointY, _screenWidth - 1, startingPointY + bannerMsgHeight, boxColor);
- drawBox(0, startingPointY, _screenWidth - 1, startingPointY, color);
- drawBox(0, startingPointY + bannerMsgHeight, _screenWidth - 1, startingPointY + bannerMsgHeight, color);
+ memset(_virtscr[kBannerVirtScreen].getBasePtr(0, 0), boxColor, _virtscr[kBannerVirtScreen].w * _virtscr[kBannerVirtScreen].h);
+ drawLine(0, startingPointY, _screenWidth - 1, startingPointY, color);
+ drawLine(0, startingPointY + bannerMsgHeight - 1, _screenWidth - 1, startingPointY + bannerMsgHeight - 1, color);
+ drawGUIText(bannerMsg, nullptr, textXPos, textYPos, color, _game.platform != Common::kPlatformFMTowns);
- drawGUIText(bannerMsg, nullptr, textXPos, textYPos, color, !isV3Towns);
+ _forceBannerVirtScreen = false;
- ScummEngine::drawDirtyScreenParts();
+ 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
@@ -550,7 +510,10 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co
bool leftBtnPressed = false, rightBtnPressed = false;
if (waitTime) {
waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed);
- clearBanner();
+ 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 e9579b24e67..e872f5cc510 100644
--- a/engines/scumm/gfx_towns.cpp
+++ b/engines/scumm/gfx_towns.cpp
@@ -36,7 +36,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
return;
assert(_textSurface.getPixels());
- int m = _textSurfaceMultiplier;
+ int m = (vs->number == kBannerVirtScreen) ? 1 : _textSurfaceMultiplier;
const uint8 *src1 = vs->getPixels(srcX, srcY);
const uint8 *src2 = (uint8 *)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m);
@@ -51,7 +51,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
int sp1 = vs->pitch - (width * vs->format.bytesPerPixel);
int sp2 = _textSurface.pitch - width * m;
- if (vs->number == kMainVirtScreen || _game.id == GID_INDY3 || _game.id == GID_ZAK) {
+ if (vs->number == kMainVirtScreen || ((_game.id == GID_INDY3 || _game.id == GID_ZAK) && vs->number != kBannerVirtScreen)) {
if (_outputPixelFormat.bytesPerPixel == 2) {
for (int h = 0; h < height; ++h) {
uint16 *dst1tmp = dst1a;
@@ -111,35 +111,38 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
error ("ScummEngine::towns_drawStripToScreen(): Unexpected text surface multiplier %d", m);
}
- dst1 = dst2;
- const uint8 *src3 = src2;
-
- if (m == 2) {
+ if (vs->number == kBannerVirtScreen) {
dst2 += lp1;
- src3 += lp1;
- for (int w = 0; w < (width << 1); ++w) {
- t = *dst1;
- s2 = *src2++;
- s3 = *src3++;
- *dst2++ = (s3 | (t & _townsLayer2Mask[s3]));
- *dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
- }
- } else if (m== 1) {
- dst2 += width;
- src3 += width;
- for (int w = 0; w < width; ++w) {
- t = *dst1;
- s2 = *src2++;
- *dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
- }
+ dst1 = dst2;
} else {
- error ("ScummEngine::towns_drawStripToScreen(): Unexpected text surface multiplier %d", m);
+ const uint8 *src3 = src2;
+ dst1 = dst2;
+ if (m == 2) {
+ dst2 += lp1;
+ src3 += lp1;
+ for (int w = 0; w < (width << 1); ++w) {
+ t = *dst1;
+ s2 = *src2++;
+ s3 = *src3++;
+ *dst2++ = (s3 | (t & _townsLayer2Mask[s3]));
+ *dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
+ }
+ } else if (m == 1) {
+ dst2 += width;
+ src3 += width;
+ for (int w = 0; w < width; ++w) {
+ t = *dst1;
+ s2 = *src2++;
+ *dst1++ = (s2 | (t & _townsLayer2Mask[s2]));
+ }
+ } else {
+ error("ScummEngine::towns_drawStripToScreen(): Unexpected text surface multiplier %d", m);
+ }
+ src2 = src3 + sp2;
+ dst1 = dst2 + dp2;
+ dst2 += dp2;
}
-
src1 += sp1;
- src2 = src3 + sp2;
- dst1 = dst2 + dp2;
- dst2 += dp2;
}
}
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 9e616915681..54183aecfed 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1010,15 +1010,6 @@ void ScummEngine_v5::o5_drawBox() {
y2 = getVarOrDirectWord(PARAM_2);
color = getVarOrDirectByte(PARAM_3);
- // HACK! In the menu room scripts, LOOM Towns draws the save slots boxes one pixel smaller than expected (on both dimensions).
- // The interpreter (somehow) manages to draw them correctly, though. Our drawBox implementation appears to be correct,
- // and attempting to directly change it breaks some stuff (like the GUI banners), so it's really not clear what we're missing.
- // So, for now, let's do this...
- if (_game.id == GID_LOOM && _game.platform == Common::kPlatformFMTowns && _currentRoom == 70) {
- x2 += 1;
- y2 += 1;
- }
-
drawBox(x, y, x2, y2, color);
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index b44a16b2f14..bb020fd4d3a 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1278,6 +1278,7 @@ public:
int _screenStartStrip = 0, _screenEndStrip = 0;
int _screenTop = 0;
+ bool _forceBannerVirtScreen = false;
// For Mac versions of 320x200 games:
// these versions rendered at 640x480 without any aspect ratio correction;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index ff92b2250d7..a20e6bc0e92 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -638,7 +638,6 @@ void ScummEngine::checkExecVerbs() {
}
VirtScreen *zone = findVirtScreen(_mouse.y);
- // This could be kUnkVirtScreen.
// Fixes bug #2773: "MANIACNES: Crash on click in speechtext-area"
if (!zone)
return;
@@ -723,7 +722,6 @@ void ScummEngine_v2::checkExecVerbs() {
const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
const int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
- // This could be kUnkVirtScreen.
// Fixes bug #2773: "MANIACNES: Crash on click in speechtext-area"
if (!zone)
return;
More information about the Scummvm-git-logs
mailing list