[Scummvm-git-logs] scummvm master -> 4f9ae442d55bc9a64c5119f8267908e9fb9e33fc
athrxx
athrxx at scummvm.org
Thu Feb 11 19:08:39 UTC 2021
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
7bcf3ef3e3 SCUMM: fix bug 11946 (INDY4: Dialogue skip passes several lines at once)
3fc39a84dd KYRA: minor adl driver comments cleanup
9e1e9f1640 KYRA: remove unnecessary declaration
e0976257e3 KYRA: update engine state comments
4f9ae442d5 SCUMM: (FM-TOWNS) - add smooth scrolling
Commit: 7bcf3ef3e35e2f6da799d66c47e7966721e22f3a
https://github.com/scummvm/scummvm/commit/7bcf3ef3e35e2f6da799d66c47e7966721e22f3a
Author: athrxx (athrxx at scummvm.org)
Date: 2021-02-11T20:01:23+01:00
Commit Message:
SCUMM: fix bug 11946 (INDY4: Dialogue skip passes several lines at once)
I have checked the behavior of original INDY4 and also DOTT (since it is a later SCUMM version). Both games do actually suppress the whole dialogue line when pressing '.', even when it is a multi part line (containing one or more '3' command codes). So we don't have a bug here. But this applies to speech enabled setting only. In text only mode the original allows to step through the dialogue line by line (and also through the message parts separated by the '3' command). I have fixed this from disasm so that it matches the original behavior. I've also confirmed for SAMNMAX from disasm (not extensively, just checked whether it has the voicemode == 2 check at the beginning of startTalkSound()).
Changed paths:
engines/scumm/actor.cpp
engines/scumm/sound.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index cb951d5071..6f03c46377 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2912,6 +2912,7 @@ void ScummEngine::stopTalk() {
_haveMsg = 0;
_talkDelay = 0;
+ _sound->_sfxMode = 0;
act = getTalkingActor();
if (act && act < 0x80) {
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 4f0801df6d..ec3e318865 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -544,6 +544,9 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
return;
}
} else {
+ // This has been verified for INDY4, DOTT and SAM
+ if (_vm->_voiceMode == 2 && _vm->_game.version <= 6)
+ return;
if (_sfxFilename.empty()) {
warning("startTalkSound: SFX file not found");
Commit: 3fc39a84dd719ec5d4fedb763b667a17041d3880
https://github.com/scummvm/scummvm/commit/3fc39a84dd719ec5d4fedb763b667a17041d3880
Author: athrxx (athrxx at scummvm.org)
Date: 2021-02-11T20:01:23+01:00
Commit Message:
KYRA: minor adl driver comments cleanup
Changed paths:
engines/kyra/sound/drivers/adlib.cpp
diff --git a/engines/kyra/sound/drivers/adlib.cpp b/engines/kyra/sound/drivers/adlib.cpp
index 10ee70d54c..f1f998b19d 100644
--- a/engines/kyra/sound/drivers/adlib.cpp
+++ b/engines/kyra/sound/drivers/adlib.cpp
@@ -68,12 +68,6 @@ public:
void setSfxVolume(uint8 volume) override;
private:
- // These variables have not yet been named, but some of them are partly
- // known nevertheless:
- //
- // unk39 - Currently unused, except for updateCallback56()
- // unk40 - Currently unused, except for updateCallback56()
-
struct Channel {
bool lock; // New to ScummVM
uint8 opExtraLevel2;
@@ -173,7 +167,6 @@ private:
// The sound data has two lookup tables:
// * One for programs, starting at offset 0.
// * One for instruments, starting at offset 300, 500, or 1000.
-
const uint8 *getInstrument(int instrumentId) {
return getProgram(_numPrograms + instrumentId);
}
@@ -246,15 +239,8 @@ private:
int update_setSoundTrigger(Channel &channel, const uint8 *values);
int update_setTempoReset(Channel &channel, const uint8 *values);
int updateCallback56(Channel &channel, const uint8 *values);
-private:
- // These variables have not yet been named, but some of them are partly
- // known nevertheless:
- //
- // _unkTable2[] - Unknown. Currently only used by updateCallback46()
- // _unkTable2_1[] - One of the tables in _unkTable2[]
- // _unkTable2_2[] - One of the tables in _unkTable2[]
- // _unkTable2_3[] - One of the tables in _unkTable2[]
+private:
int _curChannel;
uint8 _soundTrigger;
@@ -790,9 +776,6 @@ void AdLibDriver::resetAdLibState() {
}
}
-// Old calling style: output0x388(0xABCD)
-// New calling style: writeOPL(0xAB, 0xCD)
-
void AdLibDriver::writeOPL(byte reg, byte val) {
_adlib->writeReg(reg, val);
}
@@ -1580,7 +1563,7 @@ int AdLibDriver::update_setPriority(Channel &channel, const uint8 *values) {
// - _beatDivider is not further modified
//
// callback()
-// - _beatDivCnt is a countdown, gets reinitialized to _beatDivider on zero
+// - _beatDivCnt is a countdown, gets reinitialized to _beatDivider on zero
// - _beatCounter is incremented when _beatDivCnt is reset, i.e., it's a
// counter which updates with the global _tempo divided by _beatDivider.
//
Commit: 9e1e9f1640870369bb6ad7533800e5db13fcea50
https://github.com/scummvm/scummvm/commit/9e1e9f1640870369bb6ad7533800e5db13fcea50
Author: athrxx (athrxx at scummvm.org)
Date: 2021-02-11T20:01:24+01:00
Commit Message:
KYRA: remove unnecessary declaration
Changed paths:
engines/kyra/sound/drivers/segacd.h
diff --git a/engines/kyra/sound/drivers/segacd.h b/engines/kyra/sound/drivers/segacd.h
index 3f1466f9e8..762feb4618 100644
--- a/engines/kyra/sound/drivers/segacd.h
+++ b/engines/kyra/sound/drivers/segacd.h
@@ -28,10 +28,6 @@
#include "common/scummsys.h"
#include "common/array.h"
-namespace Common {
- class SeekableReadStream;
-}
-
namespace Audio {
class Mixer;
}
Commit: e0976257e3d880d02cb915d913c3c286988b2aaf
https://github.com/scummvm/scummvm/commit/e0976257e3d880d02cb915d913c3c286988b2aaf
Author: athrxx (athrxx at scummvm.org)
Date: 2021-02-11T20:01:24+01:00
Commit Message:
KYRA: update engine state comments
(these were ancient, from times before EOB support)
Changed paths:
engines/kyra/kyra_v1.h
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index 229b1a65b0..591126db14 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -55,56 +55,36 @@ class KyraMetaEngine;
*
* Status of this engine:
*
- * The KYRA engine supports all three Kyrandia games by Westwood. It also
- * supports Westwood's Lands of Lore. There are various platform ports of
- * the different games, almost all of them are fully supported. Only the
- * Macintosh port of Kyrandia 1 makes a difference here, which lacks support
- * for sound effects and music.
- *
- * The different translations of the games are mostly supported, since every
- * translation requires some work for kyra.dat for example, it is almost
- * impossible to support translations, without owning them. There a currently
- * a few reported unsupported translations:
- *
- * - Official translations
- * None known.
- * - Probably official translations (currently no sources are known to verify this)
- * Kyrandia 2 Spanish (feature request #2499966 "KYRA2: Add support for Spanish floppy version")
- * - Doubtful official translations (no sources here either, but less likely to be official)
- * Kyrandia 1 Korean (feature request #1758252 "KYRA1: Add support for Korean/DOS version")
- * Kyrandia 2 Polish (feature request #2146192 "KYRA2: Add support for Polish floppy version")
- * - Fan translations:
- * Kyrandia 3 Russian (feature request #2812792 "Kyrandia3 Russian")
+ * The KYRA engine supports the following games by Westwood:
+ * - Eye of the Beholder (fully supported)
+ * - Eye of the Beholder II - The Legend of Darkmoon (fully supported)
+ * - The Legend of Kyrandia (fully supported)
+ * - The Legend of Kyrandia 2: Hand of Fate (fully supported)
+ * - Lands of Lore: The Throne of Chaos (fully supported)
+ * - The Legend of Kyrandia 3: Malcolm's Revenge (fully supported)
*
- * The primary maintainer for the engine is LordHoto, although some parts are
- * maintained by _athrxx. If you have questions about parts of the code, the
- * following rough description might help in determining who you should ask:
- * _athrxx is the maintainer for the Lands of Lore subengine, he also
- * maintains most of the FM-TOWNS and PC98 specific code (especially the sound
- * code, also some ingame code) and the Kyrandia 2 sequence player code.
- * LordHoto is responsible for the rest of the codebase, he also worked on the
- * graphics output for 16 color PC98 games.
+ * There are various platform ports of the different games, almost all of
+ * them are fully supported. We also offer legacy graphics modes like CGA
+ * and EGA for games that originally supported this and also try to offer
+ * all sound drivers that the originals had.
+ * Some execeptions:
+ * - The PC-98 version of Eye of the Beholder II is not yet supported.
+ * - We don't support NES or Gameboy versions of Eye of the Beholder.
+ * - The Macintosh version of Kyrandia 1 lacks sound effects and music.
*
- * Other people who worked on this engine include cyx, who initially started
- * to work on Kyrandia 1 support. Vinterstum, who did various things for
- * Kyrandia 1 and started to work on the Kyrandia 2 sequence player code and
- * also on the TIM script code. Eriktorbjorn, who helped out naming our AdLib
- * player code and also contributed a work around for a music bug in the
- * "Pool of Sorrow" scene of Kyrandia 1, which is also present in the original.
- * He also contributed the VQA player for Kyrandia 3.
+ * The official translations of the games of which we are aware are mostly
+ * supported. Some of the more rare versions (of which we don't even know
+ * whether they are official or fan-translated) are missing. Unfortunately,
+ * adding new languages is more complicated than just adding a detection
+ * entry. It usually requires extra resources in kyra.dat and sometimes
+ * even adjustments to the code. So it is almost impossible to support
+ * translations without owning them.
*
- * The engine is mostly finished code wise. A possible remaining task is
- * proper refactoring, which might help in reducing binary size and along with
- * it runtime memory use, but of course might lead to regressions (since the
- * current code makes no problems on our low end ports, it is pretty minor
- * priority though, since the benefit would be mostly nicer code). The biggest
- * task left is the kyra.dat handling.
+ * Apart from the official translations we also support an increasing number
+ * of fan translations. If there is a request for a new fan translation we
+ * might demand that the requesting person supplies the necessary kyra.dat
+ * resource files.
*
- * Games using this engine:
- * - The Legend of Kyrandia (fully supported, except for Macintosh port, which lacks sound)
- * - (The) Hand of Fate (fully supported)
- * - Malcolm's Revenge (fully supported)
- * - Lands of Lore: The Throne of Chaos (fully supported)
*/
namespace Kyra {
Commit: 4f9ae442d55bc9a64c5119f8267908e9fb9e33fc
https://github.com/scummvm/scummvm/commit/4f9ae442d55bc9a64c5119f8267908e9fb9e33fc
Author: athrxx (athrxx at scummvm.org)
Date: 2021-02-11T20:01:24+01:00
Commit Message:
SCUMM: (FM-TOWNS) - add smooth scrolling
This is mostly based on ZAK and MI2 disasm (and a bit LOOM). For MI1 and INDY4 I have at least checked with the FM-Towns UNZ emulator to ensure that they actually have the smooth scrolling.
The smooth scrolling is a bit tricky with regard to the timing. The scrolling tends to be a bit too slow for the engine, since it happens at a speed of 1 pixel per 60Hz tick, while the engine sometimes writes one or more new 8 pixel strips at the same time. So the scrolling often has to catch up to the engine. The original has a compensation mechanism which I tried to adapt. We'll see if it needs more fine tuning...
The Loom intro even glitches in the UNZ FM-Towns emulator due overflowing the scrolling surface (and causing a wraparound). But I made a fix for that.
Since we do have a bug report about the speed of the ZAK intro (and the new scrolling code does affect the timing) I have run the ZAK intro in ScummVM and in the FM-Towns UNZ emulator (with i80386DX, 16 MHz setting) side by side and they seem totally in sync. If I reduce the MHz setting in the emulator to 10 MHz on the emulator the intro will play slower, but still not play through the whole song. With 8 MHz I get close to finishing the song, but this seems to be almost to slow to run the game properly. So I'll leave that bug ticket open...
Changed paths:
engines/scumm/gfx.cpp
engines/scumm/gfx.h
engines/scumm/gfx_towns.cpp
engines/scumm/input.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index a8478aacce..86d58679ff 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -372,6 +372,11 @@ void ScummEngine::initScreens(int b, int h) {
_screenH = h;
_gdi->init();
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ // The right most visible strip for FM-Towns smooth scrolling
+ _scrollFeedStrips[0] = _gdi->_numStrips - 1;
+#endif
}
void ScummEngine::initVirtScreen(VirtScreenNumber slot, int top, int width, int height, bool twobufs,
@@ -661,13 +666,13 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
assert(IS_ALIGNED(text, 4));
assert(0 == (width & 3));
- // Compose the text over the game graphics
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
towns_drawStripToScreen(vs, x, y, x, top, width, height);
return;
} else
#endif
+ // Compose the text over the game graphics
if (_outputPixelFormat.bytesPerPixel == 2) {
const byte *srcPtr = (const byte *)src;
const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m);
@@ -965,10 +970,10 @@ void ScummEngine::redrawBGAreas() {
diff = camera._cur.x - camera._last.x;
if (!_fullRedraw && diff == 8) {
val = -1;
- redrawBGStrip(_gdi->_numStrips - 1, 1);
+ scrollLeft();
} else if (!_fullRedraw && diff == -8) {
val = +1;
- redrawBGStrip(0, 1);
+ scrollRight();
} else if (_fullRedraw || diff != 0) {
if (_game.version <= 5) {
((ScummEngine_v5 *)this)->clearFlashlight();
@@ -3769,7 +3774,7 @@ void ScummEngine::fadeIn(int effect) {
_screenEffectFlag = true;
return;
}
-
+ towns_waitForScroll(0);
updatePalette();
switch (effect) {
@@ -3815,8 +3820,9 @@ void ScummEngine::fadeIn(int effect) {
}
void ScummEngine::fadeOut(int effect) {
- VirtScreen *vs = &_virtscr[kMainVirtScreen];
+ towns_waitForScroll(0);
+ VirtScreen *vs = &_virtscr[kMainVirtScreen];
vs->setDirtyRange(0, 0);
if (_game.version < 7)
camera._last.x = camera._cur.x;
@@ -3859,10 +3865,7 @@ void ScummEngine::fadeOut(int effect) {
// Just blit screen 0 to the display (i.e. display will be black)
vs->setDirtyRange(0, vs->h);
updateDirtyScreen(kMainVirtScreen);
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_townsScreen)
- _townsScreen->update();
-#endif
+ towns_updateGfx();
break;
case 134:
dissolveEffect(1, 1);
@@ -4062,6 +4065,13 @@ void ScummEngine::dissolveEffect(int width, int height) {
}
void ScummEngine::scrollEffect(int dir) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ // The FM-Towns versions use smooth scrolling here, but only for left and right.
+ if (_game.platform == Common::kPlatformFMTowns && dir > 1) {
+ towns_scriptScrollEffect((dir & 1) * 2 - 1);
+ return;
+ }
+#endif
VirtScreen *vs = &_virtscr[kMainVirtScreen];
int x, y;
@@ -4084,7 +4094,7 @@ void ScummEngine::scrollEffect(int dir) {
//up
y = 1 + step;
while (y < vs->h) {
- moveScreen(0, -step, vs->h);
+ moveScreen(0, -step * m, vs->h * m);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step);
@@ -4107,7 +4117,7 @@ void ScummEngine::scrollEffect(int dir) {
// down
y = 1 + step;
while (y < vs->h) {
- moveScreen(0, step, vs->h);
+ moveScreen(0, step * m, vs->h * m);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_townsScreen) {
towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step);
@@ -4130,21 +4140,14 @@ void ScummEngine::scrollEffect(int dir) {
// left
x = 1 + step;
while (x < vs->w) {
- moveScreen(-step, 0, vs->h);
+ moveScreen(-step * m, 0, vs->h * m);
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_townsScreen) {
- towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h);
- } else
-#endif
- {
- src = vs->getPixels(x - step, 0);
- _system->copyRectToScreen(src,
- vsPitch,
- (vs->w - step) * m, 0,
- step * m, vs->h * m);
- _system->updateScreen();
- }
+ src = vs->getPixels(x - step, 0);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ (vs->w - step) * m, 0,
+ step * m, vs->h * m);
+ _system->updateScreen();
waitForTimer(delay);
x += step;
@@ -4154,21 +4157,14 @@ void ScummEngine::scrollEffect(int dir) {
// right
x = 1 + step;
while (x < vs->w) {
- moveScreen(step, 0, vs->h);
-
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_townsScreen) {
- towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h);
- } else
-#endif
- {
- src = vs->getPixels(vs->w - x, 0);
- _system->copyRectToScreen(src,
- vsPitch,
- 0, 0,
- step, vs->h);
- _system->updateScreen();
- }
+ moveScreen(step * m, 0, vs->h * m);
+
+ src = vs->getPixels(vs->w - x, 0);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ 0, 0,
+ step, vs->h);
+ _system->updateScreen();
waitForTimer(delay);
x += step;
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 6c8503c683..1629567fa4 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -456,28 +456,28 @@ public:
#endif
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
-// Helper class for FM-Towns output (required for specific hardware effects like
-// switching graphics layers on and off).
+// 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, Graphics::PixelFormat &format);
~TownsScreen();
- void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0);
+ 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 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 toggleLayers(int flags);
+ void scrollLayers(int flags, int offset);
void update();
+ bool isScrolling(int direction, int threshold = 0) const { return (direction == 0) ? _scrollRemainder != threshold : (direction == 1 ? _scrollRemainder > threshold : _scrollRemainder < threshold); }
+
+ uint8 *getLayerPixels(int layer, int x, int y) const;
+ int getLayerPitch(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].pitch : 0; }
+ int getLayerWidth(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].width : 0; }
+ int getLayerHeight(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].height : 0; }
+ int getLayerBpp(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].bpp : 0; }
+ int getLayerScaleW(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleW : 0; }
+ int getLayerScaleH(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleH : 0; }
private:
void updateOutputBuffer();
@@ -488,9 +488,12 @@ private:
uint8 *pixels;
uint8 *palette;
int pitch;
+ int width;
int height;
+ int modW;
int bpp;
int numCol;
+ int hScroll;
uint8 scaleW;
uint8 scaleH;
bool onBottom;
@@ -507,6 +510,8 @@ private:
int _height;
int _width;
int _pitch;
+ uint16 _scrollOffset;
+ int _scrollRemainder;
Graphics::PixelFormat _pixelFormat;
int _numDirtyRects;
diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp
index 166e55e41d..56a43beacb 100644
--- a/engines/scumm/gfx_towns.cpp
+++ b/engines/scumm/gfx_towns.cpp
@@ -32,45 +32,61 @@
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)
+ if (width <= 0 || height <= 0 || !_townsScreen)
return;
assert(_textSurface.getPixels());
-
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);
+ const uint8 *src1 = vs->getPixels(srcX, srcY);
+ const uint8 *src2 = (uint8 *)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m);
+ int dstXScr = dstX + _scrollDestOffset;
+ uint8 *dst1 = _townsScreen->getLayerPixels(0, dstXScr, dstY);
+ uint16 *dst1a = (uint16*)dst1;
uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m);
- int dp1 = _townsScreen->getLayerPitch(0) - width * _townsScreen->getLayerBpp(0);
+ int lw1 = _townsScreen->getLayerWidth(0);
int dp2 = _townsScreen->getLayerPitch(1) - width * m * _townsScreen->getLayerBpp(1);
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) {
- for (int h = 0; h < height; ++h) {
- if (_outputPixelFormat.bytesPerPixel == 2) {
+ if (_outputPixelFormat.bytesPerPixel == 2) {
+ for (int h = 0; h < height; ++h) {
+ uint16 *dst1tmp = dst1a;
+ int xpos = dstXScr;
for (int w = 0; w < width; ++w) {
- *(uint16 *)dst1 = _16BitPalette[*src1++];
- dst1 += _outputPixelFormat.bytesPerPixel;
+ *dst1a++ = _16BitPalette[*src1++];
+ if (++xpos == lw1) {
+ dst1a -= lw1;
+ xpos = 0;
+ }
}
-
src1 += sp1;
- dst1 += dp1;
- } else {
- memcpy(dst1, src1, width);
- src1 += vs->pitch;
- dst1 += _townsScreen->getLayerPitch(0);
+ dst1a = dst1tmp + lw1;
}
-
- for (int sH = 0; sH < m; ++sH) {
- memcpy(dst2, src2, width * m);
- src2 += _textSurface.pitch;
- dst2 += _townsScreen->getLayerPitch(1);
+ } else {
+ for (int h = 0; h < height; ++h) {
+ uint8 *dst1tmp = dst1;
+ int xpos = dstXScr;
+ for (int w = 0; w < width; ++w) {
+ *dst1++ = *src1++;
+ if (++xpos == lw1) {
+ dst1 -= lw1;
+ xpos = 0;
+ }
+ }
+ src1 += sp1;
+ dst1 = dst1tmp + lw1;
}
}
+
+ for (int h = 0; h < height * m; ++h) {
+ memcpy(dst2, src2, width * m);
+ src2 += _textSurface.pitch;
+ dst2 += _townsScreen->getLayerPitch(1);
+ }
+
} else {
dst1 = dst2;
for (int h = 0; h < height; ++h) {
@@ -81,7 +97,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
}
dst1 = dst2;
- uint8 *src3 = src2;
+ const uint8 *src3 = src2;
if (m == 2) {
dst2 += _townsScreen->getLayerPitch(1);
@@ -106,6 +122,83 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in
_townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m);
}
+void ScummEngine::towns_clearStrip(int strip) {
+ if (!_townsScreen)
+ return;
+
+ int h = _screenHeight;
+ int pitch = _townsScreen->getLayerPitch(0) >> 2;
+ uint32 *dst1 = (uint32*)_townsScreen->getLayerPixels(0, (strip * 8 + _scrollDestOffset) % _townsScreen->getLayerWidth(0), 0);
+ if (_game.version == 5) {
+ dst1 += (90 * pitch);
+ h = 56;
+ }
+ uint32 *dst2 = dst1 + 1;
+ uint32 *dst3 = dst2 + 1;
+ uint32 *dst4 = dst3 + 1;
+
+ if (_townsScreen->getLayerBpp(0) == 2) {
+ while (h--) {
+ *dst1 = *dst2 = *dst3 = *dst4 = 0;
+ dst1 += pitch;
+ dst2 += pitch;
+ dst3 += pitch;
+ dst4 += pitch;
+ }
+ } else {
+ while (h--) {
+ *dst1 = *dst2 = 0;
+ dst1 += pitch;
+ dst2 += pitch;
+ }
+ }
+}
+
+void ScummEngine::requestScroll(int dir) {
+ if (_game.platform == Common::kPlatformFMTowns) {
+ int lw = _townsScreen->getLayerWidth(0);
+ // Wait for opposite direction scroll to finish.
+ towns_waitForScroll(-dir);
+
+ // WORKAROUND: In the LOOM intro the messenger nymph moves up the mountain so fast
+ // (and thus accumulates so many scroll requests) that this will make the graphics
+ // layer (width 512) wrap around. This causes a visible graphics glitch. The glitch
+ // is exactly the same when starting the game in the UNZ emulator, so this is not
+ // a mistake on my part. I work around this by adding an extra wait if the queued
+ // up scroll requests exceed the critical mark. It is actually the exact same fix
+ // that the SCUMM 5 games use for scrollEffect().
+ towns_waitForScroll(dir, (lw - _screenWidth - 8) * dir);
+
+ _scrollDestOffset = (_scrollDestOffset - 8 * dir) % lw;
+ _scrollRequest += (8 * dir);
+ towns_clearStrip(_scrollFeedStrips[dir + 1]);
+ }
+ redrawBGStrip(_scrollFeedStrips[dir + 1], 1);
+}
+
+void ScummEngine::towns_waitForScroll(int waitForDirection, int threshold) {
+ while (!shouldQuit() && _townsScreen && (_scrollRequest || _townsScreen->isScrolling(waitForDirection, threshold)))
+ waitForTimer(0);
+}
+
+void ScummEngine::towns_updateGfx() {
+ if (!_townsScreen)
+ return;
+
+ uint32 cur = _system->getMillis();
+ while (_scrollTimer <= cur) {
+ if (!_scrollTimer)
+ _scrollTimer = cur;
+ _scrollTimer += 1000 / 60;
+ _townsScreen->scrollLayers(1, _scrollRequest);
+ if (_townsScreen->isScrolling(0))
+ _scrollDeltaAdjust = 1;
+ _scrollRequest = 0;
+ }
+
+ _townsScreen->update();
+}
+
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;
@@ -124,6 +217,33 @@ void ScummEngine::towns_restoreCharsetBg() {
_nextTop = _string[0].ypos;
}
+void ScummEngine::towns_scriptScrollEffect(int dir) {
+ VirtScreen *vs = &_virtscr[kMainVirtScreen];
+ const int stripWidth = 8;
+ int destX = _gdi->_numStrips - 1;
+ int srcX = 0;
+ int layerW = _townsScreen->getLayerWidth(0);
+ int threshold = (layerW - vs->w - stripWidth) * dir;
+ if (dir == 1)
+ SWAP(destX, srcX);
+
+ // Wait for opposite direction scroll to finish.
+ towns_waitForScroll(-dir);
+
+ for (int x = 0; !shouldQuit() && x < _gdi->_numStrips; ++x) {
+ _scrollDestOffset = (_scrollDestOffset - (dir << 3)) % layerW;
+ uint32 nextFrame = _system->getMillis() + 1000 / 60;
+ // Same as in requestScroll(): This prevents glitches from graphics layer wrapping.
+ towns_waitForScroll(dir, threshold);
+ _townsScreen->scrollLayers(0, dir << 3);
+ towns_drawStripToScreen(vs, destX << 3, vs->topline, (srcX + (-dir * x)) << 3, 0, stripWidth, vs->h);
+ waitForTimer(nextFrame - _system->getMillis());
+ }
+
+ // Wait for all scrolls to finish.
+ towns_waitForScroll(0);
+}
+
#ifdef USE_RGB_COLOR
void ScummEngine::towns_setPaletteFromPtr(const byte *ptr, int numcolor) {
setPaletteFromPtr(ptr, numcolor);
@@ -199,13 +319,13 @@ const uint8 ScummEngine::_townsLayer2Mask[] = {
#define FULL_REDRAW (DIRTY_RECTS_MAX + 1)
TownsScreen::TownsScreen(OSystem *system, int width, int height, Graphics::PixelFormat &format) :
- _system(system), _width(width), _height(height), _pixelFormat(format), _pitch(width * format.bytesPerPixel) {
+ _system(system), _width(width), _height(height), _pixelFormat(format), _pitch(width * format.bytesPerPixel), _scrollOffset(0), _scrollRemainder(0) {
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);
+ setupLayer(0, width, height, 1, 1, 256);
_numDirtyRects = 0;
}
@@ -223,7 +343,7 @@ TownsScreen::~TownsScreen() {
_dirtyRects.clear();
}
-void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void *pal) {
+void TownsScreen::setupLayer(int layer, int width, int height, int scaleW, int scaleH, int numCol, void *pal) {
if (layer < 0 || layer > 1)
return;
@@ -232,23 +352,19 @@ void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void
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->width = width;
l->height = height;
+ l->scaleW = scaleW;
+ l->scaleH = scaleH;
+ l->modW = width * scaleW;
l->numCol = numCol;
l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1;
l->pitch = width * l->bpp;
l->palette = (uint8 *)pal;
+ l->hScroll = 0;
if (l->palette && _pixelFormat.bytesPerPixel == 1)
warning("TownsScreen::setupLayer(): Layer palette usage requires 16 bit graphics setting.\nLayer palette will be ignored.");
@@ -260,13 +376,15 @@ void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void
// build offset tables to speed up merging/scaling layers
delete[] l->bltInternX;
- l->bltInternX = new uint16[_width];
- for (int i = 0; i < _width; ++i)
+ width = MAX<int>(_width, l->modW);
+ 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)
+ height = MAX<int>(_height, l->modW);
+ l->bltInternY = new uint8*[height];
+ for (int i = 0; i < height; ++i)
l->bltInternY[i] = l->pixels + (i / l->scaleH) * l->pitch;
delete[] l->bltTmpPal;
@@ -319,45 +437,15 @@ 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);
}
-uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) {
+uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) const {
if (layer < 0 || layer > 1)
return 0;
- TownsScreenLayer *l = &_layers[layer];
+ const 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;
+ return l->pixels + y * l->pitch + (x % l->width) * l->bpp;
}
void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
@@ -420,13 +508,13 @@ void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
}
}
-void TownsScreen::toggleLayers(int flag) {
- if (flag < 0 || flag > 3)
+void TownsScreen::toggleLayers(int flags) {
+ if (flags < 0 || flags > 3)
return;
- _layers[0].enabled = (flag & 1) ? true : false;
+ _layers[0].enabled = (flags & 1) ? true : false;
_layers[0].onBottom = true;
- _layers[1].enabled = (flag & 2) ? true : false;
+ _layers[1].enabled = (flags & 2) ? true : false;
_layers[1].onBottom = !_layers[0].enabled;
_dirtyRects.clear();
@@ -439,6 +527,29 @@ void TownsScreen::toggleLayers(int flag) {
_system->updateScreen();
}
+void TownsScreen::scrollLayers(int flags, int offset) {
+ // This actually supports layer 0 only, since this is all we need.
+ _scrollRemainder += offset;
+ if (!_scrollRemainder)
+ return;
+
+ int step = (_scrollRemainder > 0) ? -1 : 1;
+ _scrollRemainder += step;
+ _scrollOffset = (_scrollOffset + step) % _layers[0].width;
+
+ _dirtyRects.clear();
+ _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+ _numDirtyRects = FULL_REDRAW;
+
+ for (int i = 0; i < 2; ++i) {
+ if (!(flags & (1 << i)))
+ continue;
+ TownsScreenLayer *l = &_layers[i];
+ if (l->ready)
+ l->hScroll = (_scrollOffset * l->scaleH) % l->modW;
+ }
+}
+
void TownsScreen::update() {
updateOutputBuffer();
outputToScreen();
@@ -447,13 +558,12 @@ void TownsScreen::update() {
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 * _pixelFormat.bytesPerPixel;
- int ptch = _pitch - (r->right - r->left + 1) * _pixelFormat.bytesPerPixel;
+ int pitch2 = _pitch - (r->right - r->left + 1) * _pixelFormat.bytesPerPixel;
if (_pixelFormat.bytesPerPixel == 2 && l->bpp == 1) {
if (!l->palette)
@@ -463,29 +573,35 @@ void TownsScreen::updateOutputBuffer() {
}
for (int y = r->top; y <= r->bottom; ++y) {
- if (l->bpp == _pixelFormat.bytesPerPixel && l->scaleW == 1 && l->onBottom && l->numCol & 0xff00) {
+ if (l->bpp == _pixelFormat.bytesPerPixel && l->scaleW == 1 && l->onBottom && l->numCol & 0xff00 && + !l->hScroll) {
memcpy(dst, &l->bltInternY[y][l->bltInternX[r->left]], (r->right + 1 - r->left) * _pixelFormat.bytesPerPixel);
dst += _pitch;
} else if (_pixelFormat.bytesPerPixel == 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;
+ uint16 *dst2 = (uint16*)dst;
+ int x = (r->left + l->hScroll) % l->modW;
+ if (l->bpp == 1) {
+ for (int w = r->right - r->left; w >= 0; --w) {
+ uint8 col = l->bltInternY[y][l->bltInternX[x]];
if (col || l->onBottom) {
if (l->numCol == 16)
col = (col >> 4) & (col & 0x0f);
- *(uint16 *)dst = l->bltTmpPal[col];
+ *dst2 = l->bltTmpPal[col];
}
- } else {
- *(uint16 *)dst = *(uint16 *)src;
+ dst2++;
+ x = (x + 1) % l->modW;
+ }
+ } else {
+ for (int w = r->right - r->left; w >= 0; --w) {
+ *dst2++ = *(uint16*)&l->bltInternY[y][l->bltInternX[x]];
+ x = (x + 1) % l->modW;
}
- dst += 2;
}
- dst += ptch;
+ dst += _pitch;
} else {
- for (int x = r->left; x <= r->right; ++x) {
+ int x = (r->left + l->hScroll) % l->modW;
+ for (int w = r->right - r->left; w >= 0; --w) {
uint8 col = l->bltInternY[y][l->bltInternX[x]];
if (col || l->onBottom) {
if (l->numCol == 16)
@@ -493,8 +609,9 @@ void TownsScreen::updateOutputBuffer() {
*dst = col;
}
dst++;
+ x = (x + 1) % l->modW;
}
- dst += ptch;
+ dst += pitch2;
}
}
}
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 8b716c6a80..98068b62ab 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -395,7 +395,7 @@ void ScummEngine_v7::processKeyboard(Common::KeyState lastKeyHit) {
// WORKAROUND bug #12022: For some reason, skipping the cutscene in which Ben fires up
// his bike (after retrieving the keys from the bartender), will outright skip the first
// bike fight sequence. Because of this, the script which handles playing ambient and wind SFX
- // outside the bar is never stopped, so those SFX are unintentionally played throughout the
+ // outside the bar is never stopped, so those SFX are unintentionally played throughout the
// rest of the game.
// This fix produces the intended behaviour from the original interpreter.
if (_game.id == GID_FT && _currentRoom == 6
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 6b60875b2b..cdb58bf8da 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -292,6 +292,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_16BitPalette = NULL;
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
_townsScreen = 0;
+ _scrollRequest = _scrollDeltaAdjust = 0;
+ _scrollDestOffset = _scrollTimer = 0;
#ifdef USE_RGB_COLOR
_cjkFont = 0;
#endif
@@ -360,6 +362,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
}
_numCyclRects = 0;
+ memset(_scrollFeedStrips, 0, sizeof(_scrollFeedStrips));
#endif
//
@@ -1569,9 +1572,11 @@ void ScummEngine::resetScumm() {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
delete _townsScreen;
+ _scrollRequest = _scrollDeltaAdjust = 0;
+ _scrollDestOffset = _scrollTimer = 0;
_townsScreen = new TownsScreen(_system, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, _outputPixelFormat);
- _townsScreen->setupLayer(0, _screenWidth, _screenHeight, (_outputPixelFormat.bytesPerPixel == 2) ? 32767 : 256);
- _townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 16, _textPalette);
+ _townsScreen->setupLayer(0, 512, _screenHeight, _textSurfaceMultiplier, _textSurfaceMultiplier, (_outputPixelFormat.bytesPerPixel == 2) ? 32767 : 256);
+ _townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 1, 1, 16, _textPalette);
}
#endif
@@ -2164,6 +2169,12 @@ Common::Error ScummEngine::go() {
// Determine how long to wait before the next loop iteration should start
int delta = (VAR_TIMER_NEXT != 0xFF) ? VAR(VAR_TIMER_NEXT) : 4;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ // FM-Towns only. The original does this to make the engine wait for the scrolling to catch up.
+ if (_scrollDeltaAdjust)
+ delta = delta * 4 / (4 - _scrollDeltaAdjust);
+ _scrollDeltaAdjust = 0;
+#endif
if (delta < 1) // Ensure we don't get into an endless loop
delta = 1; // by not decreasing sleepers.
@@ -2222,12 +2233,9 @@ void ScummEngine::waitForTimer(int msec_delay) {
_sound->updateCD(); // Loop CD Audio if needed
parseEvents();
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_townsScreen)
- _townsScreen->update();
-#endif
-
+ towns_updateGfx();
_system->updateScreen();
+
if (_system->getMillis() >= start_time + msec_delay)
break;
_system->delayMillis(10);
@@ -2806,22 +2814,23 @@ bool ScummEngine::startManiac() {
void ScummEngine::pauseEngineIntern(bool pause) {
if (pause) {
// Pause sound & video
- _oldSoundsPaused = _sound->_soundsPaused;
- _sound->pauseSounds(true);
-
+ if (_sound) {
+ _oldSoundsPaused = _sound->_soundsPaused;
+ _sound->pauseSounds(true);
+ }
} else {
- // Update the screen to make it less likely that the player will see a
- // brief cursor palette glitch when the GUI is disabled.
-
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_townsScreen)
- _townsScreen->update();
+ // Restore FM-Towns graphics
+ _scrollTimer = 0;
+ towns_updateGfx();
#endif
-
+ // Update the screen to make it less likely that the player will see a
+ // brief cursor palette glitch when the GUI is disabled.
_system->updateScreen();
// Resume sound & video
- _sound->pauseSounds(_oldSoundsPaused);
+ if (_sound)
+ _sound->pauseSounds(_oldSoundsPaused);
}
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index d8f4351d59..e790841f3a 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1348,6 +1348,7 @@ public:
protected:
void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
+ void towns_clearStrip(int strip);
#ifdef USE_RGB_COLOR
void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1);
void towns_setTextPaletteFromPtr(const byte *ptr);
@@ -1356,9 +1357,21 @@ protected:
void towns_processPalCycleField();
void towns_resetPalCycleFields();
void towns_restoreCharsetBg();
+ void towns_scriptScrollEffect(int dir);
+
+ void requestScroll(int dir);
+ void scrollLeft() { requestScroll(-1); }
+ void scrollRight() { requestScroll(1); }
+ void towns_waitForScroll(int waitForDirection, int threshold = 0);
+ void towns_updateGfx();
Common::Rect _cyclRects[16];
int _numCyclRects;
+ int _scrollRequest;
+ int _scrollDeltaAdjust;
+ uint32 _scrollTimer;
+ uint32 _scrollDestOffset;
+ uint16 _scrollFeedStrips[3];
Common::Rect _curStringRect;
@@ -1369,6 +1382,11 @@ protected:
static const uint8 _townsLayer2Mask[];
TownsScreen *_townsScreen;
+#else
+ void scrollLeft() { redrawBGStrip(_gdi->_numStrips - 1, 1); }
+ void scrollRight() { redrawBGStrip(0, 1); }
+ void towns_updateGfx() {}
+ void towns_waitForScroll(int waitForDirection, int threshold = 0) {}
#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
};
More information about the Scummvm-git-logs
mailing list