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

mikrosk noreply at scummvm.org
Wed Jul 19 20:21:06 UTC 2023


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

Summary:
f85fde7707 ENGINES: ALL: Use new CursorMan API where applicable
d9a5de0fe6 BACKEND: ATARI: Screen shaking and some fixes


Commit: f85fde77077e2798c3f7d053233e88f261bfb42a
    https://github.com/scummvm/scummvm/commit/f85fde77077e2798c3f7d053233e88f261bfb42a
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-07-19T22:22:11+02:00

Commit Message:
ENGINES: ALL: Use new CursorMan API where applicable

This makes code not only cleaner but also friendly to 8-bit surfaces
where pitch != width.

Changed paths:
    engines/access/events.cpp
    engines/avalanche/graphics.cpp
    engines/glk/events.cpp
    engines/hypno/cursors.cpp
    engines/mm/mm1/utils/mouse.cpp
    engines/mm/xeen/events.cpp
    engines/mohawk/cursors.cpp
    engines/nancy/cursor.cpp
    engines/neverhood/mouse.cpp
    engines/pegasus/cursor.cpp
    engines/sherlock/events.cpp
    engines/titanic/support/mouse_cursor.cpp
    engines/ultima/shared/core/mouse_cursor.cpp
    engines/ultima/ultima4/gfx/screen.cpp


diff --git a/engines/access/events.cpp b/engines/access/events.cpp
index 1d9731f8edb..323a78dbdb3 100644
--- a/engines/access/events.cpp
+++ b/engines/access/events.cpp
@@ -69,8 +69,7 @@ void EventsManager::setCursor(CursorType cursorId) {
 
 	if (cursorId == CURSOR_INVENTORY) {
 		// Set the cursor
-		CursorMan.replaceCursor(_invCursor.getPixels(), _invCursor.w, _invCursor.h,
-			_invCursor.w / 2, _invCursor.h / 2, 0);
+		CursorMan.replaceCursor(_invCursor, _invCursor.w / 2, _invCursor.h / 2, 0);
 	} else {
 		// Get a pointer to the mouse data to use, and get the cursor hotspot
 		const byte *srcP = &_vm->_res->CURSORS[cursorId][0];
@@ -106,8 +105,7 @@ void EventsManager::setCursor(CursorType cursorId) {
 		}
 
 		// Set the cursor
-		CursorMan.replaceCursor(cursorSurface.getPixels(), CURSOR_WIDTH, CURSOR_HEIGHT,
-			hotspotX, hotspotY, 0);
+		CursorMan.replaceCursor(cursorSurface, hotspotX, hotspotY, 0);
 
 		// Free the cursor surface
 		cursorSurface.free();
diff --git a/engines/avalanche/graphics.cpp b/engines/avalanche/graphics.cpp
index 62e9ffefb48..f8e1ec3540b 100644
--- a/engines/avalanche/graphics.cpp
+++ b/engines/avalanche/graphics.cpp
@@ -163,7 +163,7 @@ void GraphicManager::loadMouse(byte which) {
 	mask.free();
 	f.close();
 
-	CursorMan.replaceCursor(cursor.getPixels(), 16, 32, kMouseHotSpots[which]._horizontal, kMouseHotSpots[which]._vertical * 2, 255, false);
+	CursorMan.replaceCursor(cursor, kMouseHotSpots[which]._horizontal, kMouseHotSpots[which]._vertical * 2, 255, false);
 	cursor.free();
 }
 
diff --git a/engines/glk/events.cpp b/engines/glk/events.cpp
index cf0a3c772b4..5d6c6aa9c22 100644
--- a/engines/glk/events.cpp
+++ b/engines/glk/events.cpp
@@ -410,7 +410,7 @@ void Events::setCursor(CursorId cursorId) {
 			const Surface &s = _cursors[cursorId];
 			const int TRANSPARENT = s.format.RGBToColor(TRANSPARENT_RGB, TRANSPARENT_RGB, TRANSPARENT_RGB);
 
-			CursorMan.replaceCursor(s.getPixels(), s.w, s.h, s._hotspot.x, s._hotspot.y, TRANSPARENT, true, &s.format);
+			CursorMan.replaceCursor(s, s._hotspot.x, s._hotspot.y, TRANSPARENT, true);
 		}
 
 		_cursorId = cursorId;
diff --git a/engines/hypno/cursors.cpp b/engines/hypno/cursors.cpp
index 6da21b888dd..6291beec966 100644
--- a/engines/hypno/cursors.cpp
+++ b/engines/hypno/cursors.cpp
@@ -153,7 +153,7 @@ void HypnoEngine::changeCursor(const Common::String &cursor, uint32 n, bool cent
 	Graphics::Surface *entry = decodeFrame(cursor, n, &palette);
 	uint32 hotspotX = centerCursor ? entry->w / 2 : 0;
 	uint32 hotspotY = centerCursor ? entry->h / 2 : 0;
-	CursorMan.replaceCursor(entry->getPixels(), entry->w, entry->h, hotspotX, hotspotY, 0, false, &_pixelFormat);
+	CursorMan.replaceCursor(*entry, hotspotX, hotspotY, 0, false);
 	CursorMan.replaceCursorPalette(palette, 0, 256);
 	entry->free();
 	delete entry;
@@ -163,7 +163,7 @@ void HypnoEngine::changeCursor(const Common::String &cursor, uint32 n, bool cent
 void HypnoEngine::changeCursor(const Graphics::Surface &entry, byte *palette, bool centerCursor) {
 	uint32 hotspotX = centerCursor ? entry.w / 2 : 0;
 	uint32 hotspotY = centerCursor ? entry.h / 2 : 0;
-	CursorMan.replaceCursor(entry.getPixels(), entry.w, entry.h, hotspotX, hotspotY, 0, false, &_pixelFormat);
+	CursorMan.replaceCursor(entry, hotspotX, hotspotY, 0, false);
 	CursorMan.replaceCursorPalette(palette, 0, 256);
 	CursorMan.showMouse(true);
 }
diff --git a/engines/mm/mm1/utils/mouse.cpp b/engines/mm/mm1/utils/mouse.cpp
index b99dad2fe40..85d0b2e4b06 100644
--- a/engines/mm/mm1/utils/mouse.cpp
+++ b/engines/mm/mm1/utils/mouse.cpp
@@ -33,7 +33,7 @@ void Mouse::setCursor(int cursorId) {
 	Shared::Xeen::XSurface cursor;
 	_sprites.draw(cursor, cursorId, Common::Point(0, 0), Shared::Xeen::SPRFLAG_RESIZE);
 
-	CursorMan.replaceCursor(cursor.getPixels(), cursor.w, cursor.h, 0, 0, 0);
+	CursorMan.replaceCursor(cursor, 0, 0, 0);
 	showCursor();
 }
 
diff --git a/engines/mm/xeen/events.cpp b/engines/mm/xeen/events.cpp
index ad130361694..160edc45cb0 100644
--- a/engines/mm/xeen/events.cpp
+++ b/engines/mm/xeen/events.cpp
@@ -44,7 +44,7 @@ void EventsManager::setCursor(int cursorId) {
 	XSurface cursor;
 	_sprites.draw(cursor, cursorId, Common::Point(0, 0), SPRFLAG_RESIZE);
 
-	CursorMan.replaceCursor(cursor.getPixels(), cursor.w, cursor.h, 0, 0, 0);
+	CursorMan.replaceCursor(cursor, 0, 0, 0);
 	showCursor();
 }
 
diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp
index b1c7f6a9c7d..d37edc82ba2 100644
--- a/engines/mohawk/cursors.cpp
+++ b/engines/mohawk/cursors.cpp
@@ -122,7 +122,7 @@ void MystCursorManager::setCursor(uint16 id) {
 		} else {
 			transparentColor = 255;
 		}
-		CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, transparentColor);
+		CursorMan.replaceCursor(*surface, hotspotX, hotspotY, transparentColor);
 
 		// We're using the screen palette for the original game, but we need
 		// to use this for any 8bpp cursor in ME.
@@ -130,7 +130,7 @@ void MystCursorManager::setCursor(uint16 id) {
 			CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
 	} else {
 		Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
-		CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false, &pixelFormat);
+		CursorMan.replaceCursor(*surface, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false);
 	}
 }
 
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 7bcfeefc91e..51c1fb25356 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -202,7 +202,7 @@ void CursorManager::applyCursor() {
 		transColor = temp.format.RGBToColor(r, g, b);
 	}
 
-	CursorMan.replaceCursor(temp.getPixels(), temp.w, temp.h, hotspot.x, hotspot.y, transColor, false, &temp.format);
+	CursorMan.replaceCursor(temp, hotspot.x, hotspot.y, transColor, false);
 }
 
 void CursorManager::showCursor(bool shouldShow) {
diff --git a/engines/neverhood/mouse.cpp b/engines/neverhood/mouse.cpp
index fc7eb43b221..1d8678398ad 100644
--- a/engines/neverhood/mouse.cpp
+++ b/engines/neverhood/mouse.cpp
@@ -184,8 +184,7 @@ void Mouse::updateCursor() {
 		_drawOffset = _mouseCursorResource.getRect();
 		_surface->drawMouseCursorResource(_mouseCursorResource, _frameNum / 2);
 		Graphics::Surface *cursorSurface = _surface->getSurface();
-		CursorMan.replaceCursor((const byte*)cursorSurface->getPixels(),
-			cursorSurface->w, cursorSurface->h, -_drawOffset.x, -_drawOffset.y, 0);
+		CursorMan.replaceCursor(*cursorSurface, -_drawOffset.x, -_drawOffset.y, 0);
 	}
 
 }
diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp
index 25bf2583757..b4c790ff39b 100644
--- a/engines/pegasus/cursor.cpp
+++ b/engines/pegasus/cursor.cpp
@@ -83,9 +83,9 @@ void Cursor::setCurrentFrameIndex(int32 index) {
 
 			if (_info[index].surface->format.bytesPerPixel == 1) {
 				CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount);
-				CursorMan.replaceCursor(_info[index].surface->getPixels(), _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0);
+				CursorMan.replaceCursor(*_info[index].surface, _info[index].hotspot.x, _info[index].hotspot.y, 0);
 			} else {
-				CursorMan.replaceCursor(_info[index].surface->getPixels(), _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, _info[index].surface->format.RGBToColor(0xFF, 0xFF, 0xFF), false, &_info[index].surface->format);
+				CursorMan.replaceCursor(*_info[index].surface, _info[index].hotspot.x, _info[index].hotspot.y, _info[index].surface->format.RGBToColor(0xFF, 0xFF, 0xFF), false);
 			}
 		}
 	}
diff --git a/engines/sherlock/events.cpp b/engines/sherlock/events.cpp
index 459dd4a785b..8360066b3d2 100644
--- a/engines/sherlock/events.cpp
+++ b/engines/sherlock/events.cpp
@@ -96,9 +96,9 @@ void Events::setCursor(const Graphics::Surface &src, int hotspotX, int hotspotY)
 
 	if (!IS_3DO) {
 		// PC 8-bit palettized
-		CursorMan.replaceCursor(src.getPixels(), src.w, src.h, hotspotX, hotspotY, 0xff);
+		CursorMan.replaceCursor(src, hotspotX, hotspotY, 0xff);
 	} else if (!_vm->_isScreenDoubled) {
-		CursorMan.replaceCursor(src.getPixels(), src.w, src.h, hotspotX, hotspotY, 0x0000, false, &src.format);
+		CursorMan.replaceCursor(src, hotspotX, hotspotY, 0x0000, false);
 	} else {
 		Graphics::Surface tempSurface;
 		tempSurface.create(2 * src.w, 2 * src.h, src.format);
@@ -115,7 +115,7 @@ void Events::setCursor(const Graphics::Surface &src, int hotspotX, int hotspotY)
 		}
 
 		// 3DO RGB565
-		CursorMan.replaceCursor(tempSurface.getPixels(), tempSurface.w, tempSurface.h, 2 * hotspotX, 2 * hotspotY, 0x0000, false, &src.format);
+		CursorMan.replaceCursor(tempSurface, 2 * hotspotX, 2 * hotspotY, 0x0000, false);
 
 		tempSurface.free();
 	}
diff --git a/engines/titanic/support/mouse_cursor.cpp b/engines/titanic/support/mouse_cursor.cpp
index bb88ea279f9..bf987d8924e 100644
--- a/engines/titanic/support/mouse_cursor.cpp
+++ b/engines/titanic/support/mouse_cursor.cpp
@@ -151,8 +151,7 @@ void CMouseCursor::setCursor(CursorId cursorId) {
 		_cursorId = cursorId;
 
 		// Set the cursor
-		CursorMan.replaceCursor(ce._surface->getPixels(), CURSOR_SIZE, CURSOR_SIZE,
-			ce._centroid.x, ce._centroid.y, 0, false, &ce._surface->format);
+		CursorMan.replaceCursor(*ce._surface, ce._centroid.x, ce._centroid.y, 0, false);
 	}
 }
 
diff --git a/engines/ultima/shared/core/mouse_cursor.cpp b/engines/ultima/shared/core/mouse_cursor.cpp
index 3215aa45713..41fb3605f35 100644
--- a/engines/ultima/shared/core/mouse_cursor.cpp
+++ b/engines/ultima/shared/core/mouse_cursor.cpp
@@ -92,8 +92,7 @@ void MouseCursor::setCursor(int cursorId) {
 	}
 
 	// Pass the generated surface onto the ScummVM cursor manager
-	CursorMan.replaceCursor(s.getPixels(), CURSOR_WIDTH, CURSOR_HEIGHT,
-		data._hotspot.x, data._hotspot.y, 0xff);
+	CursorMan.replaceCursor(s, data._hotspot.x, data._hotspot.y, 0xff);
 }
 
 void MouseCursor::show() {
diff --git a/engines/ultima/ultima4/gfx/screen.cpp b/engines/ultima/ultima4/gfx/screen.cpp
index c14b6f840a1..ece26fe20eb 100644
--- a/engines/ultima/ultima4/gfx/screen.cpp
+++ b/engines/ultima/ultima4/gfx/screen.cpp
@@ -158,9 +158,7 @@ void Screen::loadMouseCursors() {
 		// Set the default initial cursor
 		const uint TRANSPARENT = format.RGBToColor(0x80, 0x80, 0x80);
 		MouseCursorSurface *c = _mouseCursors[MC_DEFAULT];
-		CursorMan.pushCursor(c->getPixels(),
-			MOUSE_CURSOR_SIZE, MOUSE_CURSOR_SIZE,
-			c->_hotspot.x, c->_hotspot.y, TRANSPARENT, false, &format);
+		CursorMan.pushCursor(*c, c->_hotspot.x, c->_hotspot.y, TRANSPARENT, false);
 		CursorMan.showMouse(true);
 
 	} else {
@@ -179,8 +177,7 @@ void Screen::setMouseCursor(MouseCursor cursor) {
 		_currentMouseCursor = cursor;
 
 		const uint TRANSPARENT = format.RGBToColor(0x80, 0x80, 0x80);
-		CursorMan.replaceCursor(c->getPixels(), MOUSE_CURSOR_SIZE, MOUSE_CURSOR_SIZE,
-		                        c->_hotspot.x, c->_hotspot.y, TRANSPARENT, false, &format);
+		CursorMan.replaceCursor(*c, c->_hotspot.x, c->_hotspot.y, TRANSPARENT, false);
 	}
 }
 


Commit: d9a5de0fe6c156e4bc92799a2e4f9d67a23694cc
    https://github.com/scummvm/scummvm/commit/d9a5de0fe6c156e4bc92799a2e4f9d67a23694cc
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2023-07-19T22:22:11+02:00

Commit Message:
BACKEND: ATARI: Screen shaking and some fixes

- surface setup for optimized 4-bit C2P routine wasn't properly detected

- STFA pretends to support Falcon sampling frequencies on TT leading to
  suboptimal sample mixing

- delayMillis() should check also for other events (fixes Future Wars)
  but avoid doing it for SCI as its MIDI timer would call itself in a
  recursive loop

- SuperVidel doesn't need to use VsetScreen() in VBL anymore

- Wetlands, Teen Agent, Shivers and Private Eye need non-aligned
  surface  widths

- However Wetlands and Private Eye use setCursorPalette, see
  https://bugs.scummvm.org/ticket/14524

- Added warning for Phantasmagoria's 630x450, nothing can be done there
  as the game also requires non-aligned surfaces and at the same time
  the buffer has to be aligned on 16 bytes.

- BDF scaling disabled by default

Changed paths:
    backends/graphics/atari/atari-graphics-supervidel.h
    backends/graphics/atari/atari-graphics-videl.h
    backends/graphics/atari/atari-graphics.cpp
    backends/graphics/atari/atari-graphics.h
    backends/mixer/atari/atari-mixer.cpp
    backends/platform/atari/osystem_atari.cpp
    backends/platform/atari/osystem_atari.h
    backends/platform/atari/readme.txt


diff --git a/backends/graphics/atari/atari-graphics-supervidel.h b/backends/graphics/atari/atari-graphics-supervidel.h
index 25bcc01a179..afa1026b725 100644
--- a/backends/graphics/atari/atari-graphics-supervidel.h
+++ b/backends/graphics/atari/atari-graphics-supervidel.h
@@ -80,6 +80,10 @@ private:
 		return [](void *ptr) { Mfree((uintptr)ptr & 0x00FFFFFF); };
 	}
 
+	Common::Rect alignRect(int x, int y, int w, int h) const override {
+		return Common::Rect(x, y, x + w, y + h);
+	}
+
 	static long hasSvRamBoosted() {
 		register long ret __asm__ ("d0") = 0;
 
diff --git a/backends/graphics/atari/atari-graphics-videl.h b/backends/graphics/atari/atari-graphics-videl.h
index 72832d8746a..3da10048576 100644
--- a/backends/graphics/atari/atari-graphics-videl.h
+++ b/backends/graphics/atari/atari-graphics-videl.h
@@ -64,28 +64,25 @@ public:
 	}
 
 private:
-	void copyRectToSurface(Graphics::Surface &dstSurface,
-						   const Graphics::Surface &srcSurface, int destX, int destY,
+	void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+						   int destX, int destY,
 						   const Common::Rect &subRect) const override {
 		// 'pChunkyEnd' is a delicate parameter: the c2p routine compares it to the address register
 		// used for pixel reading; two common mistakes:
 		// 1. (subRect.left, subRect.bottom) = beginning of the next line *including the offset*
 		// 2. (subRect.right, subRect.bottom) = even worse, end of the *next* line, not current one
-		const byte *pChunky       = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
-		const byte *pChunkyEnd    = (const byte *)srcSurface.getBasePtr(subRect.right, subRect.bottom-1);
+		const byte *pChunky    = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
+		const byte *pChunkyEnd = (const byte *)srcSurface.getBasePtr(subRect.right, subRect.bottom-1);
 
-		const uint32 bitsPerPixel = dstSurface.format.isCLUT8() || dstSurface.format == PIXELFORMAT_RGB332 ? 8 : 4;
-		const uint32 screenPitch  = dstSurface.pitch * bitsPerPixel/8;
+        byte *pScreen = (byte *)dstSurface.getPixels() + destY * dstSurface.pitch + destX * dstBitsPerPixel/8;
 
-		byte *pScreen = (byte *)dstSurface.getPixels() + destY * screenPitch + destX * bitsPerPixel/8;
-
-		if (bitsPerPixel == 8) {
+		if (dstBitsPerPixel == 8) {
 			if (srcSurface.pitch == subRect.width()) {
 				if (srcSurface.pitch == dstSurface.pitch) {
 					asm_c2p1x1_8(pChunky, pChunkyEnd, pScreen);
 					return;
 				} else if (srcSurface.pitch == dstSurface.pitch/2) {
-					asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, screenPitch);
+                    asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, dstSurface.pitch);
 					return;
 				}
 			}
@@ -95,10 +92,9 @@ private:
 				subRect.width(),
 				srcSurface.pitch,
 				pScreen,
-				screenPitch);
+                dstSurface.pitch);
 		} else {
-			// compare unmodified dst pitch
-			if (srcSurface.pitch == subRect.width() && srcSurface.pitch == dstSurface.pitch) {
+            if (srcSurface.pitch == subRect.width() && srcSurface.pitch/2 == dstSurface.pitch) {
 				asm_c2p1x1_4(pChunky, pChunkyEnd, pScreen);
 				return;
 			}
@@ -108,23 +104,15 @@ private:
 				subRect.width(),
 				srcSurface.pitch,
 				pScreen,
-				screenPitch);
+                dstSurface.pitch);
 		}
 	}
 
-	void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
-								  int destX, int destY, const Common::Rect &subRect, uint32 key,
+	void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+								  int destX, int destY,
+								  const Common::Rect &subRect, uint32 key,
 								  const Graphics::Surface &bgSurface, const byte srcPalette[256*3]) const override {
-		Common::Rect backgroundRect(destX, destY, destX + subRect.width(), destY + subRect.height());
-
-		// ensure that background's left and right lie on a 16px boundary and double the width if needed
-		backgroundRect.moveTo(backgroundRect.left & 0xfff0, backgroundRect.top);
-
-		const int deltaX = destX - backgroundRect.left;
-
-		backgroundRect.right = (backgroundRect.right + deltaX + 15) & 0xfff0;
-		if (backgroundRect.right > bgSurface.w)
-			backgroundRect.right = bgSurface.w;
+		const Common::Rect backgroundRect = alignRect(destX, destY, subRect.width(), subRect.height());
 
 		static Graphics::Surface cachedSurface;
 
@@ -141,14 +129,17 @@ private:
 		cachedSurface.copyRectToSurface(bgSurface, 0, 0, backgroundRect);
 
 		// copy cursor
-		convertRectToSurfaceWithKey(cachedSurface, srcSurface, deltaX, 0, subRect, key, srcPalette);
+		convertRectToSurfaceWithKey(cachedSurface, srcSurface, destX - backgroundRect.left, 0, subRect, key, srcPalette);
 
 		copyRectToSurface(
-			dstSurface,
-			cachedSurface,
+			dstSurface, dstBitsPerPixel, cachedSurface,
 			backgroundRect.left, backgroundRect.top,
 			Common::Rect(cachedSurface.w, cachedSurface.h));
 	}
+
+	Common::Rect alignRect(int x, int y, int w, int h) const override {
+		return Common::Rect(x & 0xfff0, y, (x + w + 15) & 0xfff0, y + h);
+	}
 };
 
 #endif
diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp
index 9778277f714..14722fc7d7b 100644
--- a/backends/graphics/atari/atari-graphics.cpp
+++ b/backends/graphics/atari/atari-graphics.cpp
@@ -42,27 +42,51 @@
 
 #define SCREEN_ACTIVE
 
+#define MAX_HZ_SHAKE 16 // Falcon only
+#define MAX_V_SHAKE  16
+
 bool g_unalignedPitch = false;
 
-// this is how screenptr should have been handled in TOS...
-#undef screenptr
-static volatile uintptr screenptr;
+static const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
+static const Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
+static const Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0);
+
+static bool s_tt;
+static int s_shakeXOffset;
+static int s_shakeYOffset;
+
+static Graphics::Surface *s_screenSurf;
 static void VblHandler() {
-	if (screenptr) {
+	if (s_screenSurf) {
 #ifdef SCREEN_ACTIVE
-		if (hasSuperVidel()) {
-			// SuperVidel's XBIOS seems to switch to Super mode with BS8C...
-			VsetScreen(SCR_NOCHANGE, screenptr, SCR_NOCHANGE, SCR_NOCHANGE);
-		} else {
-			union { byte c[4]; uintptr p; } sptr;
-			sptr.p = screenptr;
+		const int bitsPerPixel = (s_screenSurf->format == PIXELFORMAT_RGB121 ? 4 : 8);
+		uintptr p = (uintptr)s_screenSurf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset);
+
+		if (!s_tt) {
+			s_shakeXOffset = -s_shakeXOffset;
 
-			*((volatile byte *)0xFFFF8201) = sptr.c[1];
-			*((volatile byte *)0xFFFF8203) = sptr.c[2];
-			*((volatile byte *)0xFFFF820D) = sptr.c[3];
+			if (s_shakeXOffset >= 0) {
+				p += MAX_HZ_SHAKE;
+				*((volatile char *)0xFFFF8265) = s_shakeXOffset;
+			} else {
+				*((volatile char *)0xFFFF8265) = MAX_HZ_SHAKE + s_shakeXOffset;
+			}
+
+			// subtract 4 or 8 words if scrolling
+			*((volatile short *)0xFFFF820E) = s_shakeXOffset == 0
+			   ? (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2
+			   : (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2 - bitsPerPixel;
 		}
+
+
+		union { byte c[4]; uintptr p; } sptr;
+		sptr.p = p;
+
+		*((volatile byte *)0xFFFF8201) = sptr.c[1];
+		*((volatile byte *)0xFFFF8203) = sptr.c[2];
+		*((volatile byte *)0xFFFF820D) = sptr.c[3];
 #endif
-		screenptr = 0;
+		s_screenSurf = nullptr;
 	}
 }
 
@@ -132,10 +156,14 @@ AtariGraphicsManager::AtariGraphicsManager() {
 	vdo >>= 16;
 
 	_tt = (vdo == VDO_TT);
+	s_tt = _tt;
 
 	if (!_tt)
 		_vgaMonitor = VgetMonitor() == MON_VGA;
 
+	// no BDF scaling please
+	ConfMan.registerDefault("gui_disable_fixed_font_scaling", true);
+
 	// make the standard GUI renderer default (!DISABLE_FANCY_THEMES implies anti-aliased rendering in ThemeEngine.cpp)
 	// (and without DISABLE_FANCY_THEMES we can't use 640x480 themes)
 	const char *standardThemeEngineName = GUI::ThemeEngine::findModeConfigName(GUI::ThemeEngine::kGfxStandard);
@@ -148,7 +176,6 @@ AtariGraphicsManager::AtariGraphicsManager() {
 
 #ifndef DISABLE_FANCY_THEMES
 	// make "themes" the default theme path
-	ConfMan.registerDefault("themepath", "themes");
 	if (!ConfMan.hasKey("themepath"))
 		ConfMan.set("themepath", "themes");
 #endif
@@ -276,6 +303,11 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight())
 		error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
 
+	if (_pendingState.width % 16 != 0 && !hasSuperVidel()) {
+		warning("Requested width not divisible by 16, please report");
+		error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
+	}
+
 	if (error != OSystem::TransactionError::kTransactionSuccess) {
 		warning("endGFXTransaction failed: %02x", (int)error);
 		// all our errors are fatal but engine.cpp takes only this one seriously
@@ -289,10 +321,12 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
 	_screen[FRONT_BUFFER]->reset(_pendingState.width, _pendingState.height, 8);
 	_screen[BACK_BUFFER1]->reset(_pendingState.width, _pendingState.height, 8);
 	_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8);
-	screenptr = 0;
-
 	_workScreen = _screen[_pendingState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1];
 
+	s_screenSurf = nullptr;
+	s_shakeXOffset = 0;
+	s_shakeYOffset = 0;
+
 	// in case of resolution change from GUI
 	if (_oldWorkScreen)
 		_oldWorkScreen = _workScreen;
@@ -364,17 +398,21 @@ void AtariGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, i
 	//debug("copyRectToScreen: %d, %d, %d(%d), %d", x, y, w, pitch, h);
 
 	Graphics::Surface &dstSurface = *lockScreen();
-	const Common::Rect rect = Common::Rect(x, y, x + w, y + h);
 
-	dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
+	const Common::Rect rect = alignRect(x, y, w, h);
 	_workScreen->addDirtyRect(dstSurface, rect);
 
+	if (_currentState.mode == GraphicsMode::TripleBuffering) {
+		_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
+		_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
+	}
+
+	// no need to align so far...
+	dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
+
 	if (_currentState.mode == GraphicsMode::DirectRendering) {
 		// TODO: c2p with 16pix align
 		updateScreen();
-	} else if (_currentState.mode == GraphicsMode::TripleBuffering) {
-		_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
-		_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
 	}
 }
 
@@ -395,16 +433,16 @@ void AtariGraphicsManager::unlockScreen() {
 	//debug("unlockScreen: %d x %d", _workScreen->surf.w, _workScreen->surf.h);
 
 	const Graphics::Surface &dstSurface = *lockScreen();
-	const Common::Rect rect = Common::Rect(dstSurface.w, dstSurface.h);
 
+	const Common::Rect rect = alignRect(0, 0, dstSurface.w, dstSurface.h);
 	_workScreen->addDirtyRect(dstSurface, rect);
 
-	if (_currentState.mode == GraphicsMode::DirectRendering)
-		updateScreen();
-	else if (_currentState.mode == GraphicsMode::TripleBuffering) {
+	if (_currentState.mode == GraphicsMode::TripleBuffering) {
 		_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
 		_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
 	}
+
+	updateScreen();
 }
 
 void AtariGraphicsManager::fillScreen(uint32 col) {
@@ -427,10 +465,16 @@ void AtariGraphicsManager::updateScreen() {
 			// FIXME: Some engines are too bound to linear surfaces that it is very
 			// hard to repair them. So instead of polluting the engine with
 			// Surface::init() & delete[] Surface::getPixels() just use this hack.
-			Common::String engineId = activeDomain->getValOrDefault("engineid");
-			if (engineId == "parallaction"
+			const Common::String engineId = activeDomain->getValOrDefault("engineid");
+			const Common::String gameId = activeDomain->getValOrDefault("gameid");
+			if (engineId == "hypno"
 				|| engineId == "mohawk"
+				|| engineId == "parallaction"
+				|| engineId == "private"
+				|| (engineId == "sci"
+					&& (gameId == "phantasmagoria" || gameId == "shivers"))
 				|| engineId == "sherlock"
+				|| engineId == "teenagent"
 				|| engineId == "tsage") {
 				g_unalignedPitch = true;
 			}
@@ -446,29 +490,40 @@ void AtariGraphicsManager::updateScreen() {
 
 	if (isOverlayVisible()) {
 		assert(_workScreen == _screen[OVERLAY_BUFFER]);
-		screenUpdated = updateScreenInternal(_overlaySurface);
+		screenUpdated = updateScreenInternal<false>(_overlaySurface);
 	} else {
 		switch (_currentState.mode) {
 		case GraphicsMode::DirectRendering:
 			assert(_workScreen == _screen[FRONT_BUFFER]);
-			screenUpdated = updateScreenInternal(Graphics::Surface());
+			screenUpdated = updateScreenInternal<true>(Graphics::Surface());
 			break;
 		case GraphicsMode::SingleBuffering:
 			assert(_workScreen == _screen[FRONT_BUFFER]);
-			screenUpdated = updateScreenInternal(_chunkySurface);
+			screenUpdated = updateScreenInternal<false>(_chunkySurface);
 			break;
 		case GraphicsMode::TripleBuffering:
 			assert(_workScreen == _screen[BACK_BUFFER1]);
-			screenUpdated = updateScreenInternal(_chunkySurface);
+			screenUpdated = updateScreenInternal<false>(_chunkySurface);
 			break;
 		}
 	}
 
 	_workScreen->clearDirtyRects();
 
+#ifdef SCREEN_ACTIVE
+	// first change video mode so we can modify video regs later
+	if (_pendingScreenChange & kPendingScreenChangeMode) {
+		if (_workScreen->rez != -1) {
+			// unfortunately this reinitializes VDI, too
+			Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
+		} else if (_workScreen->mode != -1) {
+			VsetMode(_workScreen->mode);
+		}
+	}
+
 	if (_pendingScreenChange & kPendingScreenChangeScreen) {
-		// can't call (V)SetScreen without Vsync()
-		screenptr = (uintptr)(isOverlayVisible() ? _workScreen->surf.getPixels() : _screen[FRONT_BUFFER]->surf.getPixels());
+		// calling (V)SetScreen without Vsync() is dangerous (at least on Falcon)
+		s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
 	} else if (screenUpdated && !isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
 		// Triple buffer:
 		// - alternate BACK_BUFFER1 and BACK_BUFFER2
@@ -498,7 +553,7 @@ void AtariGraphicsManager::updateScreen() {
 		_screen[BACK_BUFFER2] = tmp;
 
 		// queue BACK_BUFFER2 with the most recent frame content
-		screenptr = (uintptr)_screen[BACK_BUFFER2]->surf.getPixels();
+		s_screenSurf = &_screen[BACK_BUFFER2]->surf;
 
 		set_sysvar_to_short(vblsem, 1);  // unlock vbl
 
@@ -507,19 +562,6 @@ void AtariGraphicsManager::updateScreen() {
 		// FRONT_BUFFER is displayed and still contains previously finished frame
 	}
 
-#ifdef SCREEN_ACTIVE
-	if (_pendingScreenChange & kPendingScreenChangeMode) {
-		// Avoid changing video registers in the middle of rendering...
-		Vsync();
-
-		if (_workScreen->rez != -1) {
-			// unfortunately this reinitializes VDI, too
-			Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
-		} else if (_workScreen->mode != -1) {
-			VsetMode(_workScreen->mode);
-		}
-	}
-
 	if (_pendingScreenChange & kPendingScreenChangePalette) {
 		if (_tt)
 			EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt);
@@ -562,7 +604,18 @@ void AtariGraphicsManager::updateScreen() {
 }
 
 void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) {
-	debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
+	//debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
+
+	if (_tt) {
+		// as TT can't horizontally shake anything, do it at least vertically
+		s_shakeYOffset = (shakeYOffset == 0 && shakeXOffset != 0) ? shakeXOffset : shakeYOffset;
+	} else {
+		s_shakeXOffset = shakeXOffset;
+		s_shakeYOffset = shakeYOffset;
+	}
+
+	_pendingScreenChange |= kPendingScreenChangeScreen;
+	updateScreen();
 }
 
 void AtariGraphicsManager::showOverlay(bool inGUI) {
@@ -585,7 +638,7 @@ void AtariGraphicsManager::showOverlay(bool inGUI) {
 	_workScreen = _screen[OVERLAY_BUFFER];
 
 	// do not cache dirtyRects and oldCursorRect
-	int bitsPerPixel = getOverlayFormat().isCLUT8() || getOverlayFormat() == PIXELFORMAT_RGB332 ? 8 : 4;
+	const int bitsPerPixel = getBitsPerPixel(getOverlayFormat());
 	_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel);
 
 	_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
@@ -615,6 +668,14 @@ void AtariGraphicsManager::hideOverlay() {
 	updateScreen();
 }
 
+Graphics::PixelFormat AtariGraphicsManager::getOverlayFormat() const {
+#ifndef DISABLE_FANCY_THEMES
+	return _tt ? PIXELFORMAT_RGB121 : PIXELFORMAT_RGB332;
+#else
+	return PIXELFORMAT_RGB121;
+#endif
+}
+
 void AtariGraphicsManager::clearOverlay() {
 	debug("clearOverlay");
 
@@ -625,15 +686,15 @@ void AtariGraphicsManager::clearOverlay() {
 	const Graphics::Surface &sourceSurface =
 		_currentState.mode == GraphicsMode::DirectRendering ? *_screen[FRONT_BUFFER]->offsettedSurf : _chunkySurface;
 
-	bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
+	const bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
 
-	int w = upscale ? sourceSurface.w * 2 : sourceSurface.w;
-	int h = upscale ? sourceSurface.h * 2 : sourceSurface.h;
+	const int w = upscale ? sourceSurface.w * 2 : sourceSurface.w;
+	const int h = upscale ? sourceSurface.h * 2 : sourceSurface.h;
 
-	int hzOffset = (_overlaySurface.w - w) / 2;
-	int vOffset  = (_overlaySurface.h - h) / 2;
+	const int hzOffset = (_overlaySurface.w - w) / 2;
+	const int vOffset  = (_overlaySurface.h - h) / 2;
 
-	int pitch = hzOffset * 2 + (upscale ? _overlaySurface.pitch : 0);
+	const int pitch = hzOffset * 2 + (upscale ? _overlaySurface.pitch : 0);
 
 	// Transpose from game palette to RGB332/RGB121 (overlay palette)
 	const byte *src = (const byte*)sourceSurface.getPixels();
@@ -641,11 +702,11 @@ void AtariGraphicsManager::clearOverlay() {
 
 	// for TT: 8/4/0 + (xLoss - 4) + xShift
 	static const int rShift = (_tt ? (8 - 4) : 0)
-					   + _overlaySurface.format.rLoss - _overlaySurface.format.rShift;
+		+ _overlaySurface.format.rLoss - _overlaySurface.format.rShift;
 	static const int gShift = (_tt ? (4 - 4) : 0)
-					   + _overlaySurface.format.gLoss - _overlaySurface.format.gShift;
+		+ _overlaySurface.format.gLoss - _overlaySurface.format.gShift;
 	static const int bShift = (_tt ? (0 - 4) : 0)
-					   + _overlaySurface.format.bLoss - _overlaySurface.format.bShift;
+		+ _overlaySurface.format.bLoss - _overlaySurface.format.bShift;
 
 	static const int rMask = _overlaySurface.format.rMax() << _overlaySurface.format.rShift;
 	static const int gMask = _overlaySurface.format.gMax() << _overlaySurface.format.gShift;
@@ -717,8 +778,12 @@ void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
 void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
 	//debug("copyRectToOverlay: %d, %d, %d(%d), %d", x, y, w, pitch, h);
 
-	_overlaySurface.copyRectToSurface(buf, pitch, x, y, w, h);
-	_screen[OVERLAY_BUFFER]->addDirtyRect(_overlaySurface, Common::Rect(x, y, x + w, y + h));
+	Graphics::Surface *dstSurface = lockScreen();
+
+	const Common::Rect rect = alignRect(x, y, w, h);
+	_workScreen->addDirtyRect(*dstSurface, rect);
+
+	dstSurface->copyRectToSurface(buf, pitch, x, y, w, h);
 }
 
 bool AtariGraphicsManager::showMouse(bool visible) {
@@ -868,8 +933,13 @@ void AtariGraphicsManager::convertRectToSurfaceWithKey(Graphics::Surface &dstSur
 	}
 }
 
+int AtariGraphicsManager::getBitsPerPixel(const Graphics::PixelFormat &format) const {
+	return format == PIXELFORMAT_RGB121 ? 4 : 8;
+}
+
+template <bool directRendering>	// hopefully compiler optimizes all the branching out
 bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurface) {
-	//debug("updateScreenInternal: %d", (int)dirtyRects.size());
+	//debug("updateScreenInternal");
 
 	const DirtyRects &dirtyRects  = _workScreen->dirtyRects;
 	Graphics::Surface *dstSurface = _workScreen->offsettedSurf;
@@ -878,12 +948,8 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
 	bool &cursorVisibilityChanged = _workScreen->cursorVisibilityChanged;
 	Common::Rect &oldCursorRect   = _workScreen->oldCursorRect;
 	const bool &fullRedraw        = _workScreen->fullRedraw;
-#ifndef DISABLE_FANCY_THEMES
-	const bool directRendering    = !isOverlayVisible() && _currentState.mode == GraphicsMode::DirectRendering;
-#else
-	// hopefully compiler optimizes all the branching out
-	const bool directRendering    = false;
-#endif
+
+	const int dstBitsPerPixel     = getBitsPerPixel(dstSurface->format);
 
 	bool updated = false;
 
@@ -906,7 +972,7 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
 			restoreCursor = !it->contains(oldCursorRect);
 
 		if (!directRendering) {
-			copyRectToSurface(*dstSurface, srcSurface, it->left, it->top, *it);
+			copyRectToSurface(*dstSurface, dstBitsPerPixel, srcSurface, it->left, it->top, *it);
 			updated = true;
 		} else if (!oldCursorRect.isEmpty()) {
 			const Common::Rect intersectingRect = it->findIntersectingRect(oldCursorRect);
@@ -932,12 +998,12 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
 			oldCursorRect.right = (oldCursorRect.right + 15) & 0xfff0;
 
 			copyRectToSurface(
-				*dstSurface, srcSurface,
+				*dstSurface, dstBitsPerPixel, srcSurface,
 				oldCursorRect.left, oldCursorRect.top,
 				oldCursorRect);
 		} else {
 			copyRectToSurface(
-				*dstSurface, cachedCursorSurface,
+				*dstSurface, dstBitsPerPixel, cachedCursorSurface,
 				oldCursorRect.left, oldCursorRect.top,
 				Common::Rect(oldCursorRect.width(), oldCursorRect.height()));
 		}
@@ -962,7 +1028,7 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
 		}
 
 		copyRectToSurfaceWithKey(
-			*dstSurface, _cursor.surface,
+			*dstSurface, dstBitsPerPixel, _cursor.surface,
 			_cursor.dstRect.left, _cursor.dstRect.top,
 			_cursor.srcRect,
 			_cursor.keycolor,
@@ -985,7 +1051,12 @@ AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, i
 
 	palette = palette_;
 
-	surf.init(width, height, width * format.bytesPerPixel, nullptr, format);
+	width += (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE);
+	height += 2 * MAX_V_SHAKE;
+
+	const int bitsPerPixel = _manager->getBitsPerPixel(format);
+
+	surf.init(width, height, width * bitsPerPixel / 8, nullptr, format);
 
 	void *pixelsUnaligned = allocFunc(sizeof(uintptr) + (surf.h * surf.pitch) + ALIGN - 1);
 	if (!pixelsUnaligned) {
@@ -999,7 +1070,7 @@ AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, i
 
 	memset(surf.getPixels(), 0, surf.h * surf.pitch);
 
-	_offsettedSurf.init(surf.w, surf.h, surf.pitch, surf.getPixels(), surf.format);
+	_offsettedSurf.init(surf.w, surf.h, surf.pitch, surf.getBasePtr(_manager->_tt ? 0 : MAX_HZ_SHAKE, MAX_V_SHAKE), surf.format);
 }
 
 AtariGraphicsManager::Screen::~Screen() {
@@ -1018,18 +1089,18 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
 	mode = -1;
 
 	// erase old screen
-	surf.fillRect(Common::Rect(surf.w, surf.h), 0);
+	_offsettedSurf.fillRect(Common::Rect(_offsettedSurf.w, _offsettedSurf.h), 0);
 
 	if (_manager->_tt) {
 		if (width <= 320 && height <= 240) {
 			surf.w = 320;
-			surf.h = 240;
-			surf.pitch = 2*surf.w;
+			surf.h = 240 + 2 * MAX_V_SHAKE;
+			surf.pitch = 2 * surf.w * bitsPerPixel / 8;
 			rez = kRezValueTTLow;
 		} else {
 			surf.w = 640;
-			surf.h = 480;
-			surf.pitch = surf.w;
+			surf.h = 480 + 2 * MAX_V_SHAKE;
+			surf.pitch = surf.w * bitsPerPixel / 8;
 			rez = kRezValueTTMid;
 		}
 	} else {
@@ -1048,7 +1119,7 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
 				mode |= COL80;
 			}
 		} else {
-			mode |= TV | BPS8;
+			mode |= TV | (bitsPerPixel == 4 ? BPS4 : BPS8);
 
 			if (width <= 320 && height <= 200) {
 				surf.w = 320;
@@ -1069,20 +1140,21 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
 			}
 		}
 
-		surf.pitch = surf.w;
+		surf.w += 2 * MAX_HZ_SHAKE;
+		surf.h += 2 * MAX_V_SHAKE;
+		surf.pitch = surf.w * bitsPerPixel / 8;
 	}
 
-	_offsettedSurf.init(width, height, surf.pitch, surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2), surf.format);
+	_offsettedSurf.init(
+		width, height, surf.pitch,
+		surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
+		surf.format);
 }
 
-void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, Common::Rect rect) {
+void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect) {
 	if (fullRedraw)
 		return;
 
-	// align on 16px (i.e. 16 bytes -> optimize for C2P, MOVE16 or just 16-byte cache lines)
-	rect.left &= 0xfff0;
-	rect.right = (rect.right + 15) & 0xfff0;
-
 	if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
 		|| dirtyRects.size() == 128) {	// 320x200 can hold at most 250 16x16 rectangles
 		//debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
@@ -1094,7 +1166,7 @@ void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurf
 		return;
 	}
 
-	dirtyRects.emplace(std::move(rect));
+	dirtyRects.insert(rect);
 }
 
 void AtariGraphicsManager::Cursor::update(const Graphics::Surface &screen, bool isModified) {
diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h
index 17e465f08cf..5f9d56d8783 100644
--- a/backends/graphics/atari/atari-graphics.h
+++ b/backends/graphics/atari/atari-graphics.h
@@ -79,13 +79,7 @@ public:
 	void showOverlay(bool inGUI) override;
 	void hideOverlay() override;
 	bool isOverlayVisible() const override { return _overlayVisible; }
-	Graphics::PixelFormat getOverlayFormat() const override {
-#ifndef DISABLE_FANCY_THEMES
-		return _tt ? PIXELFORMAT_RGB121 : PIXELFORMAT_RGB332;
-#else
-		return PIXELFORMAT_RGB121;
-#endif
-	}
+	Graphics::PixelFormat getOverlayFormat() const override;
 	void clearOverlay() override;
 	void grabOverlay(Graphics::Surface &surface) const override;
 	void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
@@ -105,10 +99,6 @@ public:
 	Common::Keymap *getKeymap() const;
 
 protected:
-	const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
-	const Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
-	const Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0);
-
 	typedef void* (*AtariMemAlloc)(size_t bytes);
 	typedef void (*AtariMemFree)(void *ptr);
 
@@ -157,8 +147,11 @@ private:
 	int16 getMaximumScreenHeight() const { return 480; }
 	int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
 
+	template <bool directRendering>
 	bool updateScreenInternal(const Graphics::Surface &srcSurface);
 
+	inline int getBitsPerPixel(const Graphics::PixelFormat &format) const;
+
 	virtual AtariMemAlloc getStRamAllocFunc() const {
 		return [](size_t bytes) { return (void*)Mxalloc(bytes, MX_STRAM); };
 	}
@@ -166,17 +159,20 @@ private:
 		return [](void *ptr) { Mfree(ptr); };
 	}
 
-	virtual void copyRectToSurface(Graphics::Surface &dstSurface,
-								   const Graphics::Surface &srcSurface, int destX, int destY,
+	virtual void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+								   int destX, int destY,
 								   const Common::Rect &subRect) const {
 		dstSurface.copyRectToSurface(srcSurface, destX, destY, subRect);
 	}
-	virtual void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
-										  int destX, int destY, const Common::Rect &subRect, uint32 key,
+	virtual void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
+										  int destX, int destY,
+										  const Common::Rect &subRect, uint32 key,
 										  const Graphics::Surface &bgSurface, const byte srcPalette[256*3]) const {
 		convertRectToSurfaceWithKey(dstSurface, srcSurface, destX, destY, subRect, key, srcPalette);
 	}
 
+	virtual Common::Rect alignRect(int x, int y, int w, int h) const = 0;
+
 	void cursorPositionChanged() {
 		if (_overlayVisible) {
 			_screen[OVERLAY_BUFFER]->cursorPositionChanged = true;
@@ -259,7 +255,8 @@ private:
 		~Screen();
 
 		void reset(int width, int height, int bitsPerPixel);
-		void addDirtyRect(const Graphics::Surface &srcSurface, Common::Rect rect);
+		// must be called before any rectangle drawing
+		void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect);
 
 		void clearDirtyRects() {
 			dirtyRects.clear();
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index e6a07cc127a..b7009e5547c 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -66,7 +66,7 @@ AtariMixerManager::~AtariMixerManager() {
 }
 
 void AtariMixerManager::init() {
-	long cookie;
+    long cookie, stfa = 0;
 	bool useDevconnectReturnValue = Getcookie(C__SND, &cookie) == C_FOUND && (cookie & SND_EXT) != 0;
 
 	int clk;
@@ -75,9 +75,62 @@ void AtariMixerManager::init() {
 		error("Sound system is locked");
 
 	// try XBIOS APIs which do not set SND_EXT in _SND
-	useDevconnectReturnValue |= (Getcookie(C_STFA, &cookie) == C_FOUND);	// STFA
+    useDevconnectReturnValue |= (Getcookie(C_STFA, &stfa) == C_FOUND);	// STFA
 	useDevconnectReturnValue |= (Getcookie(C_McSn, &cookie) == C_FOUND);	// X-SOUND, MacSound
 
+    bool forceSoundCmd = false;
+    if (stfa) {
+        // see http://removers.free.fr/softs/stfa.php#STFA
+        struct STFA_control {
+            uint16 sound_enable;
+            uint16 sound_control;
+            uint16 sound_output;
+            uint32 sound_start;
+            uint32 sound_current;
+            uint32 sound_end;
+            uint16 version;
+            uint32 old_vbl;
+            uint32 old_timerA;
+            uint32 old_mfp_status;
+            uint32 stfa_vbl;
+            uint32 drivers_list;
+            uint32 play_stop;
+            uint16 timer_a_setting;
+            uint32 set_frequency;
+            uint16 frequency_treshold;
+            uint32 custom_freq_table;
+            int16 stfa_on_off;
+            uint32 new_drivers_list;
+            uint32 old_bit_2_of_cookie_snd;
+            uint32 it;
+        } __attribute__((packed));
+
+        STFA_control *stfaControl = (STFA_control *)stfa;
+        if (stfaControl->version < 0x0200) {
+            error("Your STFA version is too old, please upgrade to at least 2.00");
+        }
+        if (stfaControl->stfa_on_off == -1) {
+            // emulating 16-bit playback, force TT frequencies
+            enum {
+                MCH_ST = 0,
+                MCH_STE,
+                MCH_TT,
+                MCH_FALCON,
+                MCH_CLONE,
+                MCH_ARANYM
+            };
+
+            long mch = MCH_ST<<16;
+            Getcookie(C__MCH, &mch);
+            mch >>= 16;
+
+            if (mch == MCH_TT) {
+                debug("Forcing STE/TT compatible frequency");
+                forceSoundCmd = true;
+            }
+        }
+    }
+
 	// reset connection matrix (and other settings)
 	Sndstatus(SND_RESET);
 
@@ -118,7 +171,7 @@ void AtariMixerManager::init() {
 	}
 
 	// first try to use Devconnect() with a Falcon prescaler
-	if (Devconnect(DMAPLAY, DAC, CLK25M, clk, NO_SHAKE) != 0) {
+    if (forceSoundCmd || Devconnect(DMAPLAY, DAC, CLK25M, clk, NO_SHAKE) != 0) {
 		// the return value is broken on Falcon
 		if (useDevconnectReturnValue) {
 			if (Devconnect(DMAPLAY, DAC, CLK25M, CLKOLD, NO_SHAKE) == 0) {
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 8d5f1b2110d..06a5575da5d 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -291,7 +291,9 @@ uint32 OSystem_Atari::getMillis(bool skipRecord) {
 
 void OSystem_Atari::delayMillis(uint msecs) {
 	const uint32 threshold = getMillis() + msecs;
-	while (getMillis() < threshold);
+	while (getMillis() < threshold) {
+		update();
+	}
 }
 
 void OSystem_Atari::getTimeAndDate(TimeDate &td, bool skipRecord) const {
@@ -391,7 +393,14 @@ Common::String OSystem_Atari::getDefaultConfigFileName() {
 }
 
 void OSystem_Atari::update() {
-	((DefaultTimerManager *)_timerManager)->checkTimers();
+	// FIXME: SCI MIDI calls delayMillis() from a timer leading to an infitite recursion loop here
+	const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
+	if (!activeDomain || activeDomain->getValOrDefault("engineid") != "sci" || !_inTimer) {
+		_inTimer = true;
+		((DefaultTimerManager *)_timerManager)->checkTimers();
+		_inTimer = false;
+	}
+
 	if (_useNullMixer)
 		((NullMixerManager *)_mixerManager)->update();
 	else
diff --git a/backends/platform/atari/osystem_atari.h b/backends/platform/atari/osystem_atari.h
index 898115b7379..f6d3708da44 100644
--- a/backends/platform/atari/osystem_atari.h
+++ b/backends/platform/atari/osystem_atari.h
@@ -54,6 +54,7 @@ private:
 	bool _videoInitialized = false;
 	bool _timerInitialized = false;
 	bool _useNullMixer = false;
+	bool _inTimer = false;
 };
 
 #endif
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index a2c30d8a7c2..bf2e6120743 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -262,10 +262,10 @@ or "FM_medium_quality=true" into scummvm.ini if you want to experiment with a
 better quality synthesis, otherwise the lowest quality will be used (applies
 for MAME OPL only).
 
-On the TT, in 95% of cases it makes sense to use ScummVM only if you own a
+On the TT, in most cases it makes sense to use ScummVM only if you own a
 native MIDI synthesizer (like mt32-pi: https://github.com/dwhinham/mt32-pi).
-MIDI emulation is out of question and STFA is usually slow to mix samples, too
-=> stick with games with MIDI sounds or at least don't install/enable STFA.
+MIDI emulation is out of question and STFA takes a good chunk of CPU time for
+downsampling to 8-bit resolution which could be utilized elsewhere.
 
 CD music slows everything down
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -343,6 +343,9 @@ Known issues
   fact that TT offers only 320x480 in 256 colours. Possibly fixable by a Timer
   B interrupt.
 
+- horizontal screen shaking doesn't work on TT because TT Shifter doesn't
+  support fine scrolling.
+
 - tooltips in overlay are sometimes drawn with corrupted background.
 
 - the talkie version of MI1 needs to be merged from two sources: first generate




More information about the Scummvm-git-logs mailing list