[Scummvm-git-logs] scummvm master -> 047f3fbfea170cf8b713b09fa6cd6a86b364272d

bluegr noreply at scummvm.org
Tue Dec 24 11:15:40 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:
047f3fbfea SCI: cleanup and reorganize gfx drivers


Commit: 047f3fbfea170cf8b713b09fa6cd6a86b364272d
    https://github.com/scummvm/scummvm/commit/047f3fbfea170cf8b713b09fa6cd6a86b364272d
Author: athrxx (athrxx at scummvm.org)
Date: 2024-12-24T13:15:37+02:00

Commit Message:
SCI: cleanup and reorganize gfx drivers

gfxdrivers.cpp has become quite large. So I've now made a
separate file for each driver. I also separated the mode
validation and driver init code from the enigine and put
it into an extra file.

Changed paths:
  A engines/sci/graphics/drivers/cga.cpp
  A engines/sci/graphics/drivers/cgabw.cpp
  A engines/sci/graphics/drivers/common.cpp
  A engines/sci/graphics/drivers/default.cpp
  A engines/sci/graphics/drivers/ega.cpp
  A engines/sci/graphics/drivers/gfxdriver.h
  A engines/sci/graphics/drivers/gfxdriver_intern.h
  A engines/sci/graphics/drivers/hercules.cpp
  A engines/sci/graphics/drivers/init.cpp
  A engines/sci/graphics/drivers/pc98_16col.cpp
  A engines/sci/graphics/drivers/pc98_8col_sci0.cpp
  A engines/sci/graphics/drivers/pc98_8col_sci1.cpp
  A engines/sci/graphics/drivers/upscaled.cpp
  A engines/sci/graphics/drivers/vgagrey.cpp
  A engines/sci/graphics/drivers/win16col.cpp
  A engines/sci/graphics/drivers/win256col.cpp
  R engines/sci/graphics/gfxdrivers.cpp
  R engines/sci/graphics/gfxdrivers.h
    engines/sci/engine/kgraphics.cpp
    engines/sci/engine/kmisc.cpp
    engines/sci/engine/kvideo.cpp
    engines/sci/event.cpp
    engines/sci/graphics/controls16.cpp
    engines/sci/graphics/cursor.cpp
    engines/sci/graphics/fontsjis.cpp
    engines/sci/graphics/maciconbar.cpp
    engines/sci/graphics/paint16.cpp
    engines/sci/graphics/palette.cpp
    engines/sci/graphics/portrait.cpp
    engines/sci/graphics/screen.cpp
    engines/sci/graphics/transitions.cpp
    engines/sci/graphics/view.cpp
    engines/sci/module.mk
    engines/sci/sci.cpp


diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 488317e8831..538a562f7f9 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -43,7 +43,7 @@
 #include "sci/graphics/compare.h"
 #include "sci/graphics/controls16.h"
 #include "sci/graphics/cursor.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/paint16.h"
 #include "sci/graphics/picture.h"
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 2af7dc587d1..0db69581e9e 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -35,7 +35,7 @@
 #endif
 #include "sci/engine/savegame.h"
 #include "sci/graphics/cursor.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/screen.h"
 #ifdef ENABLE_SCI32
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 8beb1e6a5ea..c427ed446d3 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -23,7 +23,7 @@
 #include "sci/engine/state.h"
 #include "sci/graphics/helpers.h"
 #include "sci/graphics/cursor.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/screen.h"
 #include "sci/util.h"
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 87ba1fddfba..c32175fb8e4 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -28,7 +28,7 @@
 #include "sci/console.h"
 #include "sci/engine/state.h"
 #include "sci/engine/kernel.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #ifdef ENABLE_SCI32
 #include "sci/graphics/cursor32.h"
 #include "sci/graphics/frameout.h"
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index 53a8845b8ca..ac9c96f2771 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -32,7 +32,7 @@
 #include "sci/engine/selector.h"
 #include "sci/engine/tts.h"
 #include "sci/graphics/compare.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/ports.h"
 #include "sci/graphics/paint16.h"
 #include "sci/graphics/scifont.h"
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 90fadeaf186..28a9f2e8c31 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -30,7 +30,7 @@
 #include "sci/sci.h"
 #include "sci/event.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/coordadjuster.h"
diff --git a/engines/sci/graphics/drivers/cga.cpp b/engines/sci/graphics/drivers/cga.cpp
new file mode 100644
index 00000000000..b3de66a7b72
--- /dev/null
+++ b/engines/sci/graphics/drivers/cga.cpp
@@ -0,0 +1,224 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI0_CGADriver final : public SCI0_DOSPreVGADriver {
+public:
+	SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering);
+	~SCI0_CGADriver() override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
+private:
+	void setupRenderProc() override;
+	uint16 *_cgaPatterns;
+	byte _palette[12];
+	const bool _disableMode5;
+	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const uint16*, const byte*);
+	LineProc _renderLine;
+	static const char *_driverFile;
+};
+
+SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering) : SCI0_DOSPreVGADriver(4, 320, 200, rgbRendering), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard), _renderLine(nullptr) {
+	static const byte cgaColors[48] = {
+		/*
+		// Canonical CGA palette
+		0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+		0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+		0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+		0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+		*/
+		// Improved palette model taken from https://int10h.org/blog/2022/06/ibm-5153-color-true-cga-palette/
+		0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0x00, 0x00, 0xC4, 0xC4,
+		0xC4, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0xC4, 0x7E, 0x00, 0xC4, 0xC4, 0xC4,
+		0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0xDC, 0x4E, 0xDC, 0x4E, 0x4E, 0xF3, 0xF3,
+		0xDC, 0x4E, 0x4E, 0xF3, 0x4E, 0xF3, 0xF3, 0xF3, 0x4E, 0xFF, 0xFF, 0xFF
+	};
+
+	static const byte modeColorMap[3][4] = {
+		{ 0, 2, 4, 6 },
+		{ 0, 3, 5, 7 },
+		{ 0, 3, 4, 7 }
+	};
+
+	Common::File drv;
+	if (!drv.open(_driverFile))
+		GFXDRV_ERR_OPEN(_driverFile);
+
+	byte palIndex = 1;
+	byte palIntensity = 1;
+	byte mode = 4;
+
+	byte colMap[4];
+	memset(colMap, 0, sizeof(colMap));
+
+	uint16 eprcOffs = 0;
+
+	uint32 cmd = drv.readUint32LE();
+	if ((cmd & 0xFF) == 0xE9)
+		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
+
+	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
+		GFXDRV_ERR_VERSION(_driverFile);
+
+	drv.skip(drv.readByte() == 0x90 ? 2 : 1);
+
+	uint16 op1st = drv.readUint16LE();
+	int op1len = drv.readUint16LE() - op1st;
+
+	// sanity check
+	assert(op1len > 0 && op1len < 0x100);
+
+	drv.seek(op1st, SEEK_SET);
+	byte *buf = new byte[op1len]();
+	drv.read(buf, op1len);
+
+	// Try figuring out the correct settings...
+	for (int i = 0; i < op1len - 7; ++i) {
+		uint32 cfg = READ_BE_UINT32(buf + i);
+		cmd = READ_BE_UINT32(buf + 4 + i);
+		if ((cmd >> 16) == 0xCD10 && (cfg & 0xff00ff) == 0xB80000) {
+			mode = (cfg >> 8) & 0xff;
+		} else if (cmd == 0xB40BCD10) {
+			if (cfg >> 8 == 0x00B701B3) {
+				palIndex = cfg & 1;
+				palIntensity = (cfg >> 4) & 1;
+			} else if (cfg >> 8 == 0x00B700B3) {
+				colMap[0] = (cfg & 0x0f) + ((cfg & 0x10) >> 1);
+			}
+		}
+	}
+
+	delete[] buf;
+
+	assert(palIndex <= 1);
+	assert(palIntensity <= 1);
+
+	for (int i = 1; i < 4; ++i)
+		colMap[i] = modeColorMap[(!_disableMode5 && mode == 5) ? 2 : palIndex][i] + (palIntensity << 3);
+
+	memset (_palette, 0, sizeof(_palette));
+	for (int i = 0; i < 4; ++i) {
+		for (int ii = 0; ii < 3; ++ii)
+			_palette[i * 3 + ii] = cgaColors[colMap[i] * 3 + ii];
+	}
+
+	assignPalette(_palette);
+
+	_cgaPatterns = new uint16[256]();
+	// The pattern map is always located right before the driver entry point proc.
+	drv.seek(eprcOffs - 512, SEEK_SET);
+	for (int i = 0; i < 256; ++i)
+		_cgaPatterns[i] = drv.readUint16LE();
+
+	drv.close();
+}
+
+SCI0_CGADriver::~SCI0_CGADriver() {
+	delete[] _cgaPatterns;
+}
+
+void SCI0_CGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
+	GFXDRV_ASSERT_READY;
+
+	byte diff = srcX & 1;
+	srcX &= ~1;
+	destX &= ~1;
+	w = (w + diff + 1) & ~1;
+
+	src += (srcY * pitch + srcX);
+
+	byte *dst = _compositeBuffer;
+	int ty = destY;
+
+	for (int i = 0; i < h; ++i) {
+		_renderLine(dst, src, w, srcX & 3, ++ty, _cgaPatterns, _internalPalette);
+		src += pitch;
+	}
+
+	g_system->copyRectToScreen(_compositeBuffer, w * _pixelSize, destX, destY, w, h);
+}
+
+void SCI0_CGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor
+	// that has already been generated by the engine. We simply convert the colors as needed...
+	assert(keycolor == 1);
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d = _compositeBuffer;
+	for (uint i = w * h; i; --i)
+		*d++ = *s++ & 3;
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
+}
+
+
+template <typename T> void cgaRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const uint16 *patterns, const byte *pal) {
+	T *d = reinterpret_cast<T*>(dst);
+	const T *p = reinterpret_cast<const T*>(pal);
+	w >>= 1;
+
+	for (int i = 0; i < w; ++i) {
+		uint16 pattern = patterns[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
+		src += 2;
+		byte sh = (ty & 3) << 1;
+		byte lo = ((pattern & 0xff) >> sh) | ((pattern & 0xff) << (8 - sh));
+		byte hi = (pattern >> (8 + sh)) | ((pattern >> 8) << (8 - sh));
+		if (sizeof(T) == 1) {
+			*d++ = (lo >> (6 - (tx << 1))) & 3;
+			*d++ = (hi >> (4 - (tx << 1))) & 3;
+		} else {
+			*d++ = p[(lo >> (6 - (tx << 1))) & 3];
+			*d++ = p[(hi >> (4 - (tx << 1))) & 3];
+		}
+		tx ^= 2;
+	}
+
+	dst = reinterpret_cast<byte*>(d);
+}
+
+void SCI0_CGADriver::setupRenderProc() {
+	static const LineProc lineProcs[] = {
+		&cgaRenderLine<byte>,
+		&cgaRenderLine<uint16>,
+		&cgaRenderLine<uint32>
+	};
+
+	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+	_renderLine = lineProcs[_pixelSize >> 1];
+}
+
+const char *SCI0_CGADriver::_driverFile = "CGA320C.DRV";
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI0_CGA)
+
+GfxDriver *SCI0_CGADriver_create(int rgbRendering, ...) {
+	return new SCI0_CGADriver(false, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/cgabw.cpp b/engines/sci/graphics/drivers/cgabw.cpp
new file mode 100644
index 00000000000..cab0c390eb5
--- /dev/null
+++ b/engines/sci/graphics/drivers/cgabw.cpp
@@ -0,0 +1,222 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI0_CGABWDriver final : public SCI0_DOSPreVGADriver {
+public:
+	SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering);
+	~SCI0_CGABWDriver() override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	Common::Point getMousePos() const override;
+	void setMousePos(const Common::Point &pos) const override;
+	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
+	void clearRect(const Common::Rect &r) const override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(_driverFiles, 2); }
+private:
+	void setupRenderProc() override;
+	byte _monochromePalette[6];
+	const byte *_monochromePatterns;
+	bool _earlyVersion;
+	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
+	LineProc _renderLine;
+	static const char *_driverFiles[2];
+};
+
+SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering) : SCI0_DOSPreVGADriver(2, 640, 400, rgbRendering), _monochromePatterns(nullptr), _earlyVersion(false), _renderLine(nullptr) {
+	_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
+	_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
+	_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
+	_monochromePalette[5] = monochromeColor & 0xff;
+	assignPalette(_monochromePalette);
+
+	if (!(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFiles[0], _earlyVersion)) && !(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFiles[1], _earlyVersion)))
+		error("Failed to open '%s' or '%s'", _driverFiles[0], _driverFiles[1]);
+}
+
+SCI0_CGABWDriver::~SCI0_CGABWDriver() {
+	delete[] _monochromePatterns;
+}
+
+void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
+	GFXDRV_ASSERT_READY;
+
+	byte *dst = _compositeBuffer;
+	int ty = destY & 7;
+
+	if (_earlyVersion) {
+		++ty;
+		byte diff = srcX & 1;
+		srcX &= ~1;
+		destX &= ~1;
+		w = (w + diff + 1) & ~1;
+	}
+
+	src += (srcY * pitch + srcX);
+
+	for (int i = 0; i < h; ++i) {
+		_renderLine(dst, src, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
+		ty = (ty + 1) & 7;
+		src += pitch;
+	}
+
+	g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, destX << 1, destY << 1, w << 1, h << 1);
+}
+
+void SCI0_CGABWDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
+	// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
+	assert(keycolor == 1);
+	keycolor = 0x0f;
+	w <<= 1;
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d0 = _compositeBuffer;
+	byte *d1 = _compositeBuffer + w;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < w; ++ii) {
+			*d0++ = *d1++ = *s ? (*s ^ 0x0e) : 0;
+			if (ii & 1)
+				++s;
+		}
+		d0 += w;
+		d1 += w;
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
+}
+
+Common::Point SCI0_CGABWDriver::getMousePos() const {
+	Common::Point res = GfxDriver::getMousePos();
+	res.x >>= 1;
+	res.y >>= 1;
+	return res;
+}
+
+void SCI0_CGABWDriver::setMousePos(const Common::Point &pos) const {
+	g_system->warpMouse(pos.x << 1, pos.y << 1);
+}
+
+void SCI0_CGABWDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
+	g_system->setShakePos(shakeXOffset << 1, shakeYOffset << 1);
+}
+
+void SCI0_CGABWDriver::clearRect(const Common::Rect &r) const {
+	Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
+	GfxDriver::clearRect(r2);
+}
+
+Common::Point SCI0_CGABWDriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point(pos.x << 1, pos.y << 1);
+}
+
+template <typename T> void cgabwRenderLine_v1(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+	const T *p = reinterpret_cast<const T*>(pal);
+	const uint16 *patterns16 = reinterpret_cast<const uint16*>(patterns);
+	T *d1 = reinterpret_cast<T*>(dst);
+	T *d2 = d1 + (w << 1);
+	w >>= 1;
+
+	for (int i = 0; i < w; ++i) {
+		uint16 pt = patterns16[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
+		src += 2;
+		byte sh = (ty & 3) << 1;
+		byte lo = ((pt & 0xff) >> sh) | ((pt & 0xff) << (8 - sh));
+		byte hi = (pt >> (8 + sh)) | ((pt >> 8) << (8 - sh));
+		if (sizeof(T) == 1) {
+			*d1++ = *d2++ = ((lo >> (6 - (tx << 1))) >> 1) & 1;
+			*d1++ = *d2++ = (lo >> (6 - (tx << 1))) & 1;
+			*d1++ = *d2++ = ((hi >> (4 - (tx << 1))) >> 1) & 1;
+			*d1++ = *d2++ = (hi >> (4 - (tx << 1))) & 1;
+		} else {
+			*d1++ = *d2++ = p[((lo >> (6 - (tx << 1))) >> 1) & 1];
+			*d1++ = *d2++ = p[(lo >> (6 - (tx << 1))) & 1];
+			*d1++ = *d2++ = p[((hi >> (4 - (tx << 1))) >> 1) & 1];
+			*d1++ = *d2++ = p[(hi >> (4 - (tx << 1))) & 1];
+		}
+		tx ^= 2;
+	}
+
+	dst = reinterpret_cast<byte*>(d2);
+}
+
+template <typename T> void cgabwRenderLine_v2(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+	const T *p = reinterpret_cast<const T*>(pal);
+	T *d1 = reinterpret_cast<T*>(dst);
+	T *d2 = d1 + (w << 1);
+
+	for (int i = 0; i < w; ++i) {
+		byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
+		if (sizeof(T) == 1) {
+			*d1++ = *d2++ = (pt >> 1) & 1;
+			*d1++ = *d2++ = pt & 1;
+		} else {
+			*d1++ = *d2++ = p[(pt >> 1) & 1];
+			*d1++ = *d2++ = p[pt & 1];
+		}
+		tx = (tx + 1) & 3;
+	}
+
+	dst = reinterpret_cast<byte*>(d2);
+}
+
+void SCI0_CGABWDriver::setupRenderProc() {
+	static const LineProc lineProcs[] = {
+		&cgabwRenderLine_v1<byte>,
+		&cgabwRenderLine_v1<uint16>,
+		&cgabwRenderLine_v1<uint32>,
+		&cgabwRenderLine_v2<byte>,
+		&cgabwRenderLine_v2<uint16>,
+		&cgabwRenderLine_v2<uint32>
+	};
+
+	int t = _pixelSize >> 1;
+	if (!_earlyVersion)
+		t += 3;
+
+	assert(t < ARRAYSIZE(lineProcs));
+	_renderLine = lineProcs[t];
+}
+
+const char *SCI0_CGABWDriver::_driverFiles[2] = { "CGA320BW.DRV", "CGA320M.DRV" };
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI0_CGABW)
+
+GfxDriver *SCI0_CGABWDriver_create(int rgbRendering, ...) {
+	static const uint32 monochromeColors[] = { 0xffbf66, 0x66ff66, 0xffffff };
+	va_list args;
+	va_start(args, rgbRendering);
+	int config = CLIP<int>(va_arg(args, int), 0, ARRAYSIZE(monochromeColors));
+	va_end(args);
+
+	return new SCI0_CGABWDriver(monochromeColors[config], rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/common.cpp b/engines/sci/graphics/drivers/common.cpp
new file mode 100644
index 00000000000..b70f263a949
--- /dev/null
+++ b/engines/sci/graphics/drivers/common.cpp
@@ -0,0 +1,368 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/events.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/paletteman.h"
+
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+#include "sci/sci.h"
+
+namespace Sci {
+
+Common::Point GfxDriver::getMousePos() const {
+	return g_system->getEventManager()->getMousePos();
+}
+
+void GfxDriver::setMousePos(const Common::Point &pos) const {
+	g_system->warpMouse(pos.x, pos.y);
+}
+
+void GfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
+	g_system->setShakePos(shakeXOffset, shakeYOffset);
+}
+
+void GfxDriver::clearRect(const Common::Rect &r) const {
+	GFXDRV_ASSERT_READY;
+	g_system->fillScreen(r, 0);
+}
+
+void GfxDriver::copyCurrentPalette(byte *dest, int start, int num) const {
+	assert(dest);
+	assert(start + num <= 256);
+	g_system->getPaletteManager()->grabPalette(dest, start, num);
+}
+
+bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
+	Common::String missing;
+	while (listSize-- && *driverNames) {
+		if (Common::File::exists(*driverNames))
+			return true;
+		if (!missing.empty())
+			missing += " or ";
+		missing += "'" + Common::String(*driverNames) + "'";
+		++driverNames;
+	}
+	warning("Driver file %s not found. Starting game in default mode", missing.c_str());
+	return false;
+}
+
+SCI0_DOSPreVGADriver::SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering) :
+	GfxDriver(screenW, screenH, numColors), _requestRGBMode(rgbRendering), _colors(nullptr), _compositeBuffer(nullptr), _internalPalette(nullptr) {
+}
+
+SCI0_DOSPreVGADriver::~SCI0_DOSPreVGADriver() {
+	delete[] _compositeBuffer;
+	delete[] _internalPalette;
+}
+
+void SCI0_DOSPreVGADriver::assignPalette(const byte *colors) {
+	_colors = colors;
+}
+
+void SCI0_DOSPreVGADriver::initScreen(const Graphics::PixelFormat*) {
+	Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
+	initGraphics(_screenW, _screenH, _requestRGBMode ? nullptr : &format);
+	format = g_system->getScreenFormat();
+	_pixelSize = format.bytesPerPixel;
+
+	if (_requestRGBMode && _pixelSize == 1)
+		warning("SCI0_DOSPreVGADriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+	delete[] _compositeBuffer;
+	delete[] _internalPalette;
+	_internalPalette = nullptr;
+	_compositeBuffer = nullptr;
+
+	assert(_colors);
+	if (_pixelSize == 1) {
+		g_system->getPaletteManager()->setPalette(_colors, 0, _numColors);
+	} else {
+		byte *rgbpal = new byte[_numColors * _pixelSize]();
+		assert(rgbpal);
+
+		if (_pixelSize == 2)
+			SciGfxDrvInternal::updateRGBPalette<uint16>(rgbpal, _colors, 0, _numColors, format);
+		else if (_pixelSize == 4)
+			SciGfxDrvInternal::updateRGBPalette<uint32>(rgbpal, _colors, 0, _numColors, format);
+		else
+			error("SCI0_DOSPreVGADriver::initScreen(): Unsupported screen format");
+		_internalPalette = rgbpal;
+		CursorMan.replaceCursorPalette(_colors, 0, _numColors);
+	}
+
+	_compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
+	assert(_compositeBuffer);
+
+	setupRenderProc();
+
+	_ready = true;
+}
+
+void SCI0_DOSPreVGADriver::replaceMacCursor(const Graphics::Cursor*) {
+	// This is not needed for SCI0 (and not for any PC version of games at all)
+	error("SCI0_DOSPreVGADriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
+}
+
+void SCI0_DOSPreVGADriver::copyCurrentBitmap(byte*, uint32) const {
+	// This is not needed for SCI0
+	error("SCI0_DOSPreVGADriver::copyCurrentBitmap(): Not implemented");
+}
+
+void SCI0_DOSPreVGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
+	GFXDRV_ASSERT_READY;
+
+	if (_pixelSize == 1) {
+		GfxDriver::copyCurrentPalette(dest, start, num);
+		return;
+	}
+
+	assert(dest);
+	memcpy(dest + start * 3, _colors + start * 3, MIN<int>(num, _numColors) * 3);
+}
+
+void SCI0_DOSPreVGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
+	// This is only needed for scaling drivers with unscaled hires fonts.
+	error("SCI0_DOSPreVGADriver::drawTextFontGlyph(): Not implemented");
+}
+
+} // End of namespace Sci
+
+namespace SciGfxDrvInternal {
+
+void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h) {
+	if (!dst)
+		return;
+
+	if (w == srcPitch && w == dstPitch) {
+		memcpy(dst + y * w, src, w * h);
+	} else {
+		const byte *s = src;
+		byte *d = dst + y * dstPitch + x;
+		for (int i = 0; i < h; ++i) {
+			memcpy(d, s, w);
+			s += srcPitch;
+			d += dstPitch;
+		}
+	}
+}
+
+template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f) {
+	T *dst = &reinterpret_cast<T*>(dest)[start];
+	for (uint i = 0; i < num; ++i) {
+		*dst++ = f.RGBToColor(src[0], src[1], src[2]);
+		src += 3;
+	}
+}
+
+
+template void updateRGBPalette<byte>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
+template void updateRGBPalette<uint16>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
+template void updateRGBPalette<uint32>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
+
+template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h) {
+	const T *s = reinterpret_cast<const T*>(src);
+	int dstPitch = pitch << 1;
+	T *d1 = reinterpret_cast<T*>(dst);
+	T *d2 = d1 + dstPitch;
+	pitch -= w;
+	dstPitch += (pitch << 1);
+
+	while (h--) {
+		for (int i = 0; i < w; ++i) {
+			d1[0] = d1[1] = d2[0] = d2[1] = *s++;
+			d1 += 2;
+			d2 += 2;
+		}
+		s += pitch;
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+}
+
+template void scale2x<byte>(byte *dst, const byte *src, int pitch, int w, int h);
+template void scale2x<uint16>(byte *dst, const byte *src, int pitch, int w, int h);
+template void scale2x<uint32>(byte *dst, const byte *src, int pitch, int w, int h);
+
+byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors) {
+	byte color[3];
+	for (int i = 2; i >= 0; --i) {
+		color[i] = rgbTriplet & 0xFF;
+		rgbTriplet >>= 8;
+	}
+	int min = 65025;
+	byte match = 0;
+	for (int i = 0; i < numColors && min; ++i) {
+		const byte *rgb = &palette[i * 3];
+		int t = (color[0] - rgb[0]) * (color[0] - rgb[0]) + (color[1] - rgb[1]) * (color[1] - rgb[1]) + (color[2] - rgb[2]) * (color[2] - rgb[2]);
+		if (t < min) {
+			min = t;
+			match = i;
+		}
+	}
+	return match;
+}
+
+void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor, bool noScale) {
+	const byte *s = reinterpret_cast<const byte*>(src);
+	uint16 min = 65025;
+	uint16 max = 0;
+
+	byte newKeyColor = 0;
+	while (newKeyColor == blackColor || newKeyColor == whiteColor)
+		++newKeyColor;
+
+	for (uint i = 0; i < w * h; ++i) {
+		byte col = *s++;
+		if (col == keycolor)
+			continue;
+		const byte *rgb = &palette[col * 3];
+		uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+		if (t > max)
+			max = t;
+		if (t < min)
+			min = t;
+	}
+
+#if 0
+	// The original interpreter will accidentally let the value overflow like this,
+	// making most cursors completely white. I have fixed it.
+	uint16 med = (uint16)(min + max) >> 1;
+#else
+	uint16 med = (min + max) >> 1;
+#endif
+	uint16 lim1 = max - (max - min) / 3;
+	uint16 lim2 = min + max - lim1;
+	s = reinterpret_cast<const byte*>(src);
+
+	if (w < 17 && h < 17 && !noScale) {
+		// Small cursors (like the insignia ring in KQ6) get scaled and dithered.
+		byte *dst2 = dst + (w << 1);
+		for (uint i = 0; i < h; ++i) {
+			for (uint ii = 0; ii < w; ++ii) {
+				byte col = *s++;
+				if (col == keycolor) {
+					*dst++ = *dst2++ = newKeyColor;
+					*dst++ = *dst2++ = newKeyColor;
+					continue;
+				}
+				const byte *rgb = &palette[col * 3];
+				uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+
+				dst[0] = dst2[1] = t > lim2 ? whiteColor : blackColor;
+				dst2[0] = dst[1] = t > lim1 ? whiteColor : blackColor;
+				dst += 2;
+				dst2 += 2;
+			};
+			dst	+= (w << 1);
+			dst2 += (w << 1);
+		}
+		w <<= 1;
+		h <<= 1;
+		hotX <<= 1;
+		hotY <<= 1;
+	} else {
+		for (uint i = 0; i < w * h; ++i) {
+			byte col = *s++;
+			if (col == keycolor) {
+				*dst++ = newKeyColor;
+				continue;
+			}
+			const byte *rgb = &palette[col * 3];
+			uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+			*dst++ = t > med ? whiteColor : blackColor;
+		}
+	}
+	keycolor = newKeyColor;
+}
+
+void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
+	dstPitch -= w;
+	srcPitch -= w;
+
+	while (h--) {
+		for (int i = 0; i < w - 1; ++i) {
+			uint8 a = *src++;
+			uint8 b = *src;
+			if (a != transpCol)
+				*dst = a;
+			else if (b != transpCol)
+				*dst = b;
+			++dst;
+		}
+		byte l = *src++;
+		if (l != transpCol)
+			*dst = l;
+		++dst;
+		src += srcPitch;
+		dst += dstPitch;
+	}
+}
+
+const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
+	Common::File drv;
+	if (!drv.open(drvFile))
+		return nullptr;
+
+	uint16 eprcOffs = 0;
+
+	uint32 cmd = drv.readUint32LE();
+	if ((cmd & 0xFF) == 0xE9)
+		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
+
+	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
+		GFXDRV_ERR_VERSION(drv.getName());
+
+	// This is a safe assumption, as the early version pattern map is 4 times the size of the later one.
+	earlyVersion = (eprcOffs > 0x500);
+	uint16 size = earlyVersion ? 512 : 128;
+
+	byte *result = new byte[size];
+	// For CGA, the pattern map is always located before the entry point dispatcher proc.
+	drv.seek(eprcOffs - size, SEEK_SET);
+	drv.read(result, size);
+
+	// For Hercules there are some extra vars in between, all with initial values
+	// of zero. The last entry of the pattern map is definitely never zero...
+	int xtraOffs = 0;
+	while (result[size - 1 - xtraOffs] == 0)
+		++xtraOffs;
+	if (xtraOffs != 0) {
+		drv.seek(eprcOffs - size - xtraOffs, SEEK_SET);
+		drv.read(result, size);
+	}
+
+	drv.close();
+
+	if (earlyVersion) {
+		uint16 *r = reinterpret_cast<uint16*>(result);
+		for (int i = 0; i < 256; ++i)
+			r[i] = FROM_LE_16(r[i]);
+	}
+
+	return result;
+}
+
+} // End of namespace SciGfxDrvInternal
diff --git a/engines/sci/graphics/drivers/default.cpp b/engines/sci/graphics/drivers/default.cpp
new file mode 100644
index 00000000000..190aa833701
--- /dev/null
+++ b/engines/sci/graphics/drivers/default.cpp
@@ -0,0 +1,270 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/paletteman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+#include "sci/resource/resource.h"
+
+namespace Sci {
+
+GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering) : GfxDriver(screenWidth, screenHeight, 0), _cursorUsesScreenPalette(true),  _colorConv(nullptr), _colorConvMod(nullptr),
+	_srcPixelSize(1), _requestRGBMode(rgbRendering), _compositeBuffer(nullptr), _currentBitmap(nullptr), _internalPalette(nullptr), _currentPalette(nullptr), _virtualW(screenWidth), _virtualH(screenHeight), _alwaysCreateBmpBuffer(!isSCI0) {
+	switch (g_sci->getResMan()->getViewType()) {
+	case kViewEga:
+		_numColors = 16;	// QFG PC-98 with 8 colors also reports 16 here
+		break;
+	case kViewAmiga:
+		_numColors = 32;
+		break;
+	case kViewAmiga64:
+		_numColors = 64;
+		break;
+	case kViewVga:
+	case kViewVga11:
+		_numColors = 256;
+		break;
+	default:
+		break;
+	}
+
+	if (_numColors == 0)
+		error("GfxDefaultDriver: Unknown view type");
+}
+
+GfxDefaultDriver::~GfxDefaultDriver() {
+	delete[] _compositeBuffer;
+	delete[] _currentBitmap;
+	delete[] _internalPalette;
+	delete[] _currentPalette;
+}
+
+template <typename T> void colorConvert(byte *dst, const byte *src, int pitch, int w, int h, const byte *pal) {
+	T *d = reinterpret_cast<T*>(dst);
+	const T *p = reinterpret_cast<const T*>(pal);
+	const byte *s = src;
+	pitch -= w;
+
+	while (h--) {
+		for (int i = 0; i < w; ++i)
+			*d++ = p[*s++];
+		s += pitch;
+	}
+}
+
+#define applyMod(a, b) MIN<uint>(a * (128 + b) / 128, 255)
+template <typename T> void colorConvertMod(byte *dst, const byte *src, int pitch, int w, int h, const byte *srcPal, const byte *internalPal, Graphics::PixelFormat &f, const PaletteMod *mods, const byte *modMapping) {
+	T *d = reinterpret_cast<T*>(dst);
+	const T *p = reinterpret_cast<const T*>(internalPal);
+	const byte *s1 = src;
+	const byte *s2 = modMapping;
+	pitch -= w;
+
+	while (h--) {
+		for (int i = 0; i < w; ++i) {
+			byte m = *s2++;
+			if (m) {
+				const byte *col = &srcPal[*s1++ * 3];
+				*d++ = f.RGBToColor(applyMod(col[0], mods[m].r), applyMod(col[1], mods[m].g), applyMod(col[2], mods[m].b));
+			} else {
+				*d++ = p[*s1++];
+			}
+		}
+		s1 += pitch;
+		s2 += pitch;
+	}
+}
+#undef applyMod
+
+void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
+	Graphics::PixelFormat format8bt(Graphics::PixelFormat::createFormatCLUT8());
+	initGraphics(_screenW, _screenH, srcRGBFormat ? srcRGBFormat : (_requestRGBMode ? nullptr : &format8bt));
+	_format = g_system->getScreenFormat();
+
+	int srcPixelSize = srcRGBFormat ? _format.bytesPerPixel : 1;
+	if (srcPixelSize != _srcPixelSize || _pixelSize != _format.bytesPerPixel) {
+		delete[] _compositeBuffer;
+		delete[] _currentBitmap;
+		delete[] _internalPalette;
+		delete[] _currentPalette;
+		_compositeBuffer = _currentBitmap = _internalPalette = _currentPalette = nullptr;
+	}
+
+	_pixelSize = _format.bytesPerPixel;
+	_srcPixelSize = srcPixelSize;
+
+	if (_requestRGBMode && _pixelSize == 1)
+		warning("GfxDefaultDriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+	if (_pixelSize != _srcPixelSize) {
+		uint32 bufferSize = _screenW * _screenH * _pixelSize;
+		_compositeBuffer = new byte[bufferSize]();
+		assert(_compositeBuffer);
+	}
+
+	// Not needed for SCI0, except for rgb rendering. Unfortunately, SCI_VERSION_01
+	// does need it and we can't tell the version from the number of colors there.
+	// That's why we have the _alwaysCreateBmpBuffer flag...
+	if (_alwaysCreateBmpBuffer || _numColors > 16 || _pixelSize > 1) {
+		_currentBitmap = new byte[_virtualW * _virtualH * _srcPixelSize]();
+		assert(_currentBitmap);
+	}
+
+	if (_numColors > 16 || _pixelSize > 1) {
+		// Not needed for SCI0, except for rgb rendering
+		_currentPalette = new byte[256 * 3]();
+		assert(_currentPalette);
+		if (_pixelSize != _srcPixelSize) {
+			_internalPalette = new byte[256 * _pixelSize]();
+			assert(_internalPalette);
+		}
+	}
+
+	static const ColorConvProc colorConvProcs[] = {
+		&colorConvert<byte>,
+		&colorConvert<uint16>,
+		&colorConvert<uint32>
+	};
+	assert((_pixelSize >> 1) < ARRAYSIZE(colorConvProcs));
+	_colorConv = colorConvProcs[_pixelSize >> 1];
+
+	static const ColorConvModProc colorConvModProcs[] = {
+		&colorConvertMod<byte>,
+		&colorConvertMod<uint16>,
+		&colorConvertMod<uint32>
+	};
+	assert((_pixelSize >> 1) < ARRAYSIZE(colorConvModProcs));
+	_colorConvMod = colorConvModProcs[_pixelSize >> 1];
+
+	_ready = true;
+}
+
+void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	if (_pixelSize > 1) {
+		updatePalette(colors, start, num);
+		if (update)
+			copyRectToScreen(_currentBitmap, 0, 0, _virtualW, 0, 0, _virtualW, _virtualH, palMods, palModMapping);
+		if (_cursorUsesScreenPalette)
+			CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
+	} else {
+		g_system->getPaletteManager()->setPalette(colors, start, num);
+	}
+}
+
+void GfxDefaultDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	assert (h >= 0 && w >= 0);
+
+	src += (srcY * pitch + srcX * _srcPixelSize);
+	if (src != _currentBitmap)
+		SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _screenW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
+
+	if (_pixelSize != _srcPixelSize) {
+		generateOutput(_compositeBuffer, src, pitch, w, h, palMods, palModMapping + destY * pitch + destX);
+		src = _compositeBuffer;
+		pitch = w * _pixelSize;
+	}
+
+	g_system->copyRectToScreen(src, pitch, destX, destY, w, h);
+}
+
+void GfxDefaultDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
+	if (_pixelSize > 1 && _currentPalette != nullptr)
+		CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
+}
+
+void GfxDefaultDriver::replaceMacCursor(const Graphics::Cursor*) {
+	// This is not needed for any non-Mac version of games at all)
+	error("GfxDefaultDriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
+}
+
+void GfxDefaultDriver::copyCurrentBitmap(byte *dest, uint32 size) const {
+	GFXDRV_ASSERT_READY;
+	assert(dest);
+	assert(size <= (uint32)(_screenW * _screenH));
+
+	// SCI 0 should not make calls to this method (except when using palette mods), but we have to know if it does...
+	if (!_currentBitmap)
+		error("GfxDefaultDriver::copyCurrentBitmap(): unexpected call");
+
+	// I have changed the implementation a bit from what the engine did before. For non-rgb rendering
+	// it would call OSystem::lockScreen() and then memcpy the data from there (which avoided the need
+	// for the extra bitmap buffer). However, OSystem::lockScreen() is meant more as an update method
+	// for the screen, the call to OSystem::unlockScreen() will turn the whole screen dirty (to be
+	// updated on the next OSysten::updateScreen() call. This is not what we need here, so I rather use
+	// the extra bitmap buffer (which is required for rgb rendering anyway).
+	memcpy(dest, _currentBitmap, size);
+}
+
+void GfxDefaultDriver::copyCurrentPalette(byte *dest, int start, int num) const {
+	GFXDRV_ASSERT_READY;
+
+	if (_pixelSize == 1) {
+		GfxDriver::copyCurrentPalette(dest, start, num);
+		return;
+	}
+
+	assert(dest);
+	assert(_currentPalette);
+	assert(start + num <= 256);
+	memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
+}
+
+void GfxDefaultDriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
+	// This is only needed for scaling drivers with unscaled hires fonts.
+	error("GfxDefaultDriver::drawTextFontGlyph(): Not implemented");
+}
+
+void GfxDefaultDriver::updatePalette(const byte *colors, uint start, uint num) {
+	memcpy(_currentPalette + start * 3, colors, num * 3);
+	if (_pixelSize == 4)
+		SciGfxDrvInternal::updateRGBPalette<uint32>(_internalPalette, colors, start, num, _format);
+	else if (_pixelSize == 2)
+		SciGfxDrvInternal::updateRGBPalette<uint16>(_internalPalette, colors, start, num, _format);
+	else
+		error("GfxDefaultDriver::updatePalette(): Unsupported pixel size %d", _pixelSize);
+}
+
+void GfxDefaultDriver::generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	if (palMods && palModMapping)
+		_colorConvMod(dst, src, pitch, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
+	else
+		_colorConv(dst, src, pitch, w, h, _internalPalette);
+}
+
+GfxDriver *GfxDefaultDriver_create(int rgbRendering, ...) {
+	va_list args;
+	va_start(args, rgbRendering);
+	int config = va_arg(args, int);
+	int width = va_arg(args, int);
+	int height = va_arg(args, int);
+	va_end(args);
+
+	return new GfxDefaultDriver(width, height, config == 0, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/ega.cpp b/engines/sci/graphics/drivers/ega.cpp
new file mode 100644
index 00000000000..8decef6c8e7
--- /dev/null
+++ b/engines/sci/graphics/drivers/ega.cpp
@@ -0,0 +1,304 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/paletteman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+SCI1_EGADriver::SCI1_EGADriver(bool rgbRendering) : GfxDriver(320, 200, 256), _requestRGBMode(rgbRendering), _egaColorPatterns(nullptr), _egaMatchTable(nullptr),
+	_currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _renderLine(nullptr), _vScaleMult(2), _vScaleDiv(1) {
+	static const byte egaColors[48] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+		0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+		0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+		0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+	};
+	_convPalette = egaColors;
+}
+
+SCI1_EGADriver::~SCI1_EGADriver() {
+	delete[] _egaMatchTable;
+	delete[] _egaColorPatterns;
+	delete[] _compositeBuffer;
+	delete[] _currentBitmap;
+	delete[] _currentPalette;
+	delete[] _internalPalette;
+}
+
+template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool) {
+	const T *p = reinterpret_cast<const T*>(pal);
+	T *d1 = reinterpret_cast<T*>(dst);
+	T *d2 = d1 + (w << 1);
+
+	for (int i = 0; i < w; ++i) {
+		byte pt = patterns[*src++];
+		if (sizeof(T) == 1) {
+			*d1++ = *d2++ = pt >> 4;
+			*d1++ = *d2++ = pt & 0x0f;
+		} else {
+			*d1++ = *d2++ = p[pt >> 4];
+			*d1++ = *d2++ = p[pt & 0x0f];
+		}
+	}
+	dst = reinterpret_cast<byte*>(d2);
+}
+
+void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
+	if (!_ready)
+		loadData();
+
+	Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
+	initGraphics(_screenW << 1, _screenH * _vScaleMult / _vScaleDiv, _requestRGBMode ? nullptr : &format);
+	format = g_system->getScreenFormat();
+	_pixelSize = format.bytesPerPixel;
+
+	if (_requestRGBMode && _pixelSize == 1)
+		warning("SCI1_EGADriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+	delete[] _egaColorPatterns;
+	delete[] _compositeBuffer;
+	delete[] _currentBitmap;
+	delete[] _currentPalette;
+	delete[] _internalPalette;
+	_internalPalette = nullptr;
+	_egaColorPatterns = _compositeBuffer = _currentBitmap = _currentPalette = nullptr;
+
+	if (_pixelSize == 1) {
+		g_system->getPaletteManager()->setPalette(_convPalette, 0, 16);
+	} else {
+		byte *rgbpal = new byte[_numColors * _pixelSize]();
+		assert(rgbpal);
+
+		if (_pixelSize == 2)
+			SciGfxDrvInternal::updateRGBPalette<uint16>(rgbpal, _convPalette, 0, 16, format);
+		else if (_pixelSize == 4)
+			SciGfxDrvInternal::updateRGBPalette<uint32>(rgbpal, _convPalette, 0, 16, format);
+		else
+			error("SCI1_EGADriver::initScreen(): Unsupported screen format");
+		_internalPalette = rgbpal;
+		CursorMan.replaceCursorPalette(_convPalette, 0, 16);
+	}
+
+	_compositeBuffer = new byte[(_screenW << 1) * (_screenH * _vScaleMult / _vScaleDiv) * _pixelSize]();
+	assert(_compositeBuffer);
+	_currentBitmap = new byte[_screenW * _screenH]();
+	assert(_currentBitmap);
+	_currentPalette = new byte[256 * 3]();
+	assert(_currentPalette);
+	_egaColorPatterns = new byte[256]();
+	assert(_egaColorPatterns);
+
+	static const LineProc lineProcs[] = {
+		&ega640RenderLine<byte>,
+		&ega640RenderLine<uint16>,
+		&ega640RenderLine<uint32>
+	};
+
+	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+	_renderLine = lineProcs[_pixelSize >> 1];
+
+	_ready = true;
+}
+
+void SCI1_EGADriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) {
+	GFXDRV_ASSERT_READY;
+	memcpy(_currentPalette + start * 3, colors, num * 3);
+	byte *d = &_egaColorPatterns[start];
+	for (uint i = 0; i < num; ++i) {
+		*d++ = _egaMatchTable[((MIN<byte>((colors[0] >> 2) + _colAdjust, 63) & 0x38) << 3) | (MIN<byte>((colors[1] >> 2) + _colAdjust, 63) & 0x38) | (MIN<byte>((colors[2] >> 2) + _colAdjust, 63) >> 3)];
+		colors += 3;
+	}
+	if (update)
+		copyRectToScreen(_currentBitmap, 0, 0, _screenW, 0, 0, _screenW, _screenH, nullptr, nullptr);
+}
+
+void SCI1_EGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
+	GFXDRV_ASSERT_READY;
+	assert (h >= 0 && w >= 0);
+
+	src += (srcY * pitch + srcX);
+
+	if (src != _currentBitmap)
+		SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _screenW, src, pitch, destX, destY, w, h);
+
+	uint16 realWidth, realHeight;
+	renderBitmap(_compositeBuffer, src, pitch, destY, w, h, _egaColorPatterns, _internalPalette, realWidth, realHeight);
+
+	Common::Point pos(destX, destY);
+	pos = getRealCoords(pos);
+
+	g_system->copyRectToScreen(_compositeBuffer, realWidth * _pixelSize, pos.x, pos.y, realWidth, realHeight);
+}
+
+void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	int dstPitch = (w << 1);
+	byte *d1 = _compositeBuffer;
+	byte *d2 = _compositeBuffer + dstPitch;
+	uint32 newKeyColor = 0xFF;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < w; ++ii) {
+			byte col = *s++;
+			if (col == keycolor) {
+				*d1++ = *d2++ = newKeyColor;
+				*d1++ = *d2++ = newKeyColor;
+			} else {
+				byte pt = _egaColorPatterns[col];
+				*d1++ = *d2++ = pt >> 4;
+				*d1++ = *d2++ = pt & 0x0f;
+			}
+		}
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
+}
+
+void SCI1_EGADriver::copyCurrentBitmap(byte *dest, uint32 size) const {
+	GFXDRV_ASSERT_READY;
+	assert(dest);
+	assert(size <= (uint32)(_screenW * _screenH));
+	memcpy(dest, _currentBitmap, size);
+}
+
+void SCI1_EGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
+	GFXDRV_ASSERT_READY;
+	assert(dest);
+	assert(start + num <= 256);
+	memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
+}
+
+void SCI1_EGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
+	// This is only needed for scaling drivers with unscaled hires fonts.
+	error("SCI1_EGADriver::drawTextFontGlyph(): Not implemented");
+}
+
+Common::Point SCI1_EGADriver::getMousePos() const {
+	Common::Point res = GfxDriver::getMousePos();
+	res.x >>= 1;
+	res.y = res.y * _vScaleDiv / _vScaleMult;
+	return res;
+}
+
+void SCI1_EGADriver::setMousePos(const Common::Point &pos) const {
+	g_system->warpMouse(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
+}
+
+void SCI1_EGADriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
+	g_system->setShakePos(shakeXOffset << 1, shakeYOffset * _vScaleMult / _vScaleDiv);
+}
+
+void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
+	Common::Rect r2(r.left << 1, r.top * _vScaleMult / _vScaleDiv, r.right << 1, r.bottom * _vScaleMult / _vScaleDiv);
+	GfxDriver::clearRect(r2);
+}
+
+Common::Point SCI1_EGADriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
+}
+
+void SCI1_EGADriver::loadData() {
+	Common::File drv;
+	if (!drv.open(_driverFile))
+		GFXDRV_ERR_OPEN(_driverFile);
+
+	uint16 eprcOffs = 0;
+
+	uint32 cmd = drv.readUint32LE();
+	if ((cmd & 0xFF) == 0xE9)
+		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
+
+	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR) || drv.readUint32LE() != 0xFEDCBA98 || !drv.skip(4))
+		GFXDRV_ERR_VERSION(_driverFile);
+
+	uint32 pos = (drv.pos() + 1) & ~1;
+
+	drv.seek(pos);
+	drv.seek(drv.readUint16LE());
+	uint32 colResponse = drv.readUint32LE();
+	_numColors = (colResponse >> 8) & 0xffff;
+	if (_numColors < 16 || _numColors > 256 || (colResponse & 0xff0000ff) != 0xC30000B8)
+		error("SCI1_EGADriver: Failed to retrieve color info from '%s'", _driverFile);
+
+	drv.seek(pos + 20);
+	drv.seek(drv.readUint16LE());
+	byte *buff = new byte[128];
+	drv.read(buff, 128);
+
+	uint16 tableOffs = 0;
+	for (int i = 0; i < 120 && !tableOffs; ++i) {
+		uint32 c = READ_BE_UINT32(buff + i);
+		if (c == 0x8BD82E8A) {
+			if (buff[i + 4] == 0x87)
+				tableOffs = READ_LE_UINT16(buff + i + 5);
+		} else if (c == 0xD0E8D0E8) {
+			for (int ii = 4; ii < 14; ++ii) {
+				if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
+					c = READ_BE_UINT32(buff + i + ii + 2);
+					if ((c & 0xFFFFFF) == 0x83F83F)
+						_colAdjust = c >> 24;
+				}
+			}
+		}
+	}
+	delete[] buff;
+
+	if (!tableOffs)
+		error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
+
+	drv.seek(tableOffs);
+	byte *table = new byte[512]();
+	drv.read(table, 512);
+	_egaMatchTable = table;
+
+	if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
+		GFXDRV_ERR_VERSION(_driverFile);
+
+	drv.close();
+}
+
+void SCI1_EGADriver::renderBitmap(byte *dst, const byte *src, int pitch, int, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
+	for (int i = 0; i < h; ++i) {
+		_renderLine(dst, src, w, patterns, palette, 0);
+		src += pitch;
+	}
+	realWidth = w << 1;
+	realHeight = h << 1;
+}
+
+const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI1_EGA)
+
+GfxDriver *SCI1_EGADriver_create(int rgbRendering, ...) {
+	return new SCI1_EGADriver(rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/gfxdriver.h b/engines/sci/graphics/drivers/gfxdriver.h
new file mode 100644
index 00000000000..7328af5c37a
--- /dev/null
+++ b/engines/sci/graphics/drivers/gfxdriver.h
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef SCI_GRAPHICS_DRIVERS_GFXDRIVER_H
+#define SCI_GRAPHICS_DRIVERS_GFXDRIVER_H
+
+#include "common/rendermode.h"
+#include "common/rect.h"
+#include "graphics/pixelformat.h"
+#include "sci/detection.h"
+
+namespace Graphics {
+	class Cursor;
+}
+
+namespace Sci {
+
+struct PaletteMod;
+
+class GfxDriver {
+public:
+	enum DrawFlags : uint32 {
+		kHiResMode		=	1 << 0,
+		kMovieMode		=	1 << 1
+	};
+
+	GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _ready(false), _pixelSize(1) {}
+	virtual ~GfxDriver() {}
+	virtual void initScreen(const Graphics::PixelFormat *srcRGBFormat = nullptr) = 0; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
+	virtual void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) = 0;
+	virtual void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) = 0;
+	virtual void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) = 0;
+	virtual void replaceMacCursor(const Graphics::Cursor *cursor) = 0;
+	virtual Common::Point getMousePos() const;
+	virtual void setMousePos(const Common::Point &pos) const;
+	virtual void setShakePos(int shakeXOffset, int shakeYOffset) const;
+	virtual void clearRect(const Common::Rect &r) const;
+	virtual void copyCurrentBitmap(byte *dest, uint32 size) const = 0;
+	virtual void copyCurrentPalette(byte *dest, int start, int num) const;
+	virtual void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) = 0;
+	virtual byte remapTextColor(byte color) const { return color; }
+	virtual void setColorMap(const byte *colorMap) {}
+	virtual Common::Point getRealCoords(Common::Point &pos) const { return pos; }
+	virtual void setFlags(uint32 flags) {}
+	virtual void clearFlags(uint32 flags) {}
+	virtual bool supportsPalIntensity() const = 0;
+	virtual bool supportsHiResGraphics() const = 0;
+	virtual bool driverBasedTextRendering() const = 0;
+	uint16 numColors() const { return _numColors; }
+	byte pixelSize() const { return _pixelSize; }
+
+protected:
+	bool _ready;
+	static bool checkDriver(const char *const *driverNames, int listSize);
+	const uint16 _screenW;
+	const uint16 _screenH;
+	uint16 _numColors;
+	byte _pixelSize;
+};
+
+} // End of namespace Sci
+
+namespace SciGfxDriver {
+
+Common::RenderMode getRenderMode();
+Sci::GfxDriver *create(Common::RenderMode renderMode, int width, int height);
+
+} // End of namespace SciGfxDriver
+
+#endif // SCI_GRAPHICS_DRIVERS_GFXDRIVER_H
diff --git a/engines/sci/graphics/drivers/gfxdriver_intern.h b/engines/sci/graphics/drivers/gfxdriver_intern.h
new file mode 100644
index 00000000000..e12fb4a7776
--- /dev/null
+++ b/engines/sci/graphics/drivers/gfxdriver_intern.h
@@ -0,0 +1,194 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H
+#define SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H
+
+#include "common/platform.h"
+#include "sci/graphics/drivers/gfxdriver.h"
+
+namespace Sci {
+
+class GfxDefaultDriver : public GfxDriver {
+public:
+	GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering);
+	~GfxDefaultDriver() override;
+	void initScreen(const Graphics::PixelFormat *srcRGBFormat) override; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
+	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	void replaceMacCursor(const Graphics::Cursor*) override;
+	void copyCurrentBitmap(byte *dest, uint32 size) const override;
+	void copyCurrentPalette(byte *dest, int start, int num) const override;
+	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
+	bool supportsPalIntensity() const override { return true; }
+	bool supportsHiResGraphics() const override { return false; }
+	bool driverBasedTextRendering() const override { return false; }
+protected:
+	void updatePalette(const byte *colors, uint start, uint num);
+	byte *_compositeBuffer;
+	byte *_currentBitmap;
+	byte *_currentPalette;
+	byte *_internalPalette;
+	uint16 _virtualW;
+	uint16 _virtualH;
+	Graphics::PixelFormat _format;
+	byte _srcPixelSize;
+	bool _cursorUsesScreenPalette;
+	const bool _alwaysCreateBmpBuffer;
+	const bool _requestRGBMode;
+	typedef void (*ColorConvProc)(byte*, const byte*, int, int, int, const byte*);
+	ColorConvProc _colorConv;
+	typedef void (*ColorConvModProc)(byte*, const byte*, int, int, int, const byte*, const byte*, Graphics::PixelFormat&, const PaletteMod*, const byte*);
+	ColorConvModProc _colorConvMod;
+private:
+	void generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
+};
+
+class SCI0_DOSPreVGADriver : public GfxDriver {
+public:
+	SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering);
+	~SCI0_DOSPreVGADriver() override;
+	void initScreen(const Graphics::PixelFormat*) override;
+	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
+	void replaceMacCursor(const Graphics::Cursor*) override;
+	void copyCurrentBitmap(byte*, uint32) const override;
+	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
+	void copyCurrentPalette(byte *dest, int start, int num) const override;
+	bool supportsPalIntensity() const override { return false; }
+	bool supportsHiResGraphics() const override { return false; }
+	bool driverBasedTextRendering() const override { return false; }
+protected:
+	void assignPalette(const byte *colors);
+	byte *_compositeBuffer;
+	const byte *_internalPalette;
+private:
+	virtual void setupRenderProc() = 0;
+	const bool _requestRGBMode;
+	const byte *_colors;
+};
+
+class UpscaledGfxDriver : public GfxDefaultDriver {
+public:
+	UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering);
+	~UpscaledGfxDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	Common::Point getMousePos() const override;
+	void setMousePos(const Common::Point &pos) const override;
+	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
+	void clearRect(const Common::Rect &r) const override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) override; // For HiRes fonts.
+	bool driverBasedTextRendering() const override { return true; }
+protected:
+	UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering);
+	void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
+	void adjustCursorBuffer(uint16 newWidth, uint16 newHeight);
+	typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int);
+	GlyphRenderProc _renderGlyph;
+	typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
+	ScaledRenderProc _renderScaled;
+	uint16 _textAlignX;
+	uint16 _hScaleMult;
+	uint16 _vScaleMult;
+	uint16 _vScaleDiv;
+	byte *_scaledBitmap;
+private:
+	virtual void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight);
+	const bool _scaleCursor;
+	uint16 _cursorWidth;
+	uint16 _cursorHeight;
+	bool _needCursorBuffer;
+};
+
+class SCI1_EGADriver : public GfxDriver {
+public:
+	SCI1_EGADriver(bool rgbRendering);
+	~SCI1_EGADriver() override;
+	void initScreen(const Graphics::PixelFormat*) override;
+	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	void replaceMacCursor(const Graphics::Cursor *cursor) override {}
+	void copyCurrentBitmap(byte *dest, uint32 size) const override;
+	void copyCurrentPalette(byte *dest, int start, int num) const override;
+	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
+	Common::Point getMousePos() const override;
+	void setMousePos(const Common::Point &pos) const override;
+	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
+	void clearRect(const Common::Rect &r) const override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	bool supportsPalIntensity() const override { return false; }
+	bool supportsHiResGraphics() const override { return false; }
+	bool driverBasedTextRendering() const override { return false; }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
+protected:
+	typedef void (*LineProc)(byte*&, const byte*, int, const byte*, const byte*, bool);
+	LineProc _renderLine;
+	const byte *_convPalette;
+	uint16 _vScaleMult, _vScaleDiv;
+	const byte *_egaMatchTable;
+	byte *_egaColorPatterns;
+	uint8 _colAdjust;
+	byte *_compositeBuffer;
+	byte *_currentPalette;
+private:
+	virtual void loadData();
+	virtual void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight);
+	byte *_currentBitmap;
+	const byte *_internalPalette;
+	const bool _requestRGBMode;
+	static const char *_driverFile;
+};
+
+#define GFXDRV_ASSERT_READY \
+	if (!_ready) \
+		error("%s(): initScreen() must be called before using this method", __FUNCTION__)
+
+#define GFXDRV_ERR_OPEN(x) \
+	error("%s(): Failed to open '%s'", __FUNCTION__, x)
+
+#define GFXDRV_ERR_VERSION(x) \
+	error("%s(): Driver file '%s' unknown version", __FUNCTION__, x)
+
+#define SCI_GFXDRV_VALIDATE_IMPL(name) \
+	bool name##Driver_validateMode(Common::Platform platform) { \
+		return name##Driver::validateMode(platform); \
+	}
+
+} // End of namespace Sci
+
+namespace SciGfxDrvInternal {
+	template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
+	template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h);
+	void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h);
+	byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors);
+	void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor, bool noScale);
+	void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol);
+	const byte *monochrInit(const char *drvFile, bool &earlyVersion);
+
+} // End of namespace SciGfxDrvInternal
+
+#endif // SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H
diff --git a/engines/sci/graphics/drivers/hercules.cpp b/engines/sci/graphics/drivers/hercules.cpp
new file mode 100644
index 00000000000..116b5a95ee9
--- /dev/null
+++ b/engines/sci/graphics/drivers/hercules.cpp
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI0_HerculesDriver final : public SCI0_DOSPreVGADriver {
+public:
+	SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage);
+	~SCI0_HerculesDriver() override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	Common::Point getMousePos() const override;
+	void setMousePos(const Common::Point &pos) const override;
+	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
+	void clearRect(const Common::Rect &r) const override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
+private:
+	void setupRenderProc() override;
+	const uint16 _centerX;
+	const uint16 _centerY;
+	byte _monochromePalette[6];
+	const byte *_monochromePatterns;
+	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
+	LineProc _renderLine;
+	static const char *_driverFile;
+};
+
+SCI0_HerculesDriver::SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage) : SCI0_DOSPreVGADriver(2, cropImage ? 640 : 720, cropImage ? 300 : 350, rgbRendering),
+	_centerX(cropImage ? 0 : 40), _centerY(cropImage ? 0 : 25), _monochromePatterns(nullptr), _renderLine(nullptr) {
+	_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
+	_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
+	_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
+	_monochromePalette[5] = monochromeColor & 0xff;
+	assignPalette(_monochromePalette);
+	bool unused = false;
+
+	if (!(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFile, unused)))
+		GFXDRV_ERR_OPEN(_driverFile);
+}
+
+SCI0_HerculesDriver::~SCI0_HerculesDriver() {
+	delete[] _monochromePatterns;
+}
+
+void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
+	GFXDRV_ASSERT_READY;
+
+	byte *dst = _compositeBuffer;
+	byte sw = destY & 1;
+	src += (srcY * pitch + srcX);
+	destY = (destY & ~1) * 3 / 2 + (destY & 1);
+	int ty = destY & 7;
+	int rh = 0;
+
+	for (int i = 0; i < h; ++i) {
+		const byte *src2 = src;
+		_renderLine(dst, src2, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
+		ty = (ty + 1) & 7;
+		++rh;
+
+		if (sw & 1)
+			sw ^= 2;
+
+		if (sw != 3) {
+			src += pitch;
+			sw ^= 1;
+		} else {
+			--i;
+		}
+	}
+
+	g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, (destX << 1) + _centerX, destY + _centerY, w << 1, rh);
+}
+
+void SCI0_HerculesDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
+	// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
+	assert(keycolor == 1);
+	keycolor = 0x0f;
+	int alt = 0;
+	const byte *s = reinterpret_cast<const byte *>(cursor);
+	byte *d = _compositeBuffer;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < (w << 1); ++ii) {
+			*d++ = *s ? (*s ^ 0x0e) : 0;
+			if (ii & 1)
+				++s;
+		}
+		if (i & 1) {
+			alt ^= 1;
+			if (alt) {
+				s -= w;
+				--i;
+			}
+		}
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w << 1, (h & ~1) * 3 / 2 + (h & 1), hotspotX << 1, (hotspotY & ~1) * 3 / 2 + (hotspotY & 1), keycolor);
+}
+
+Common::Point SCI0_HerculesDriver::getMousePos() const {
+	Common::Point res = GfxDriver::getMousePos();
+	res.x = CLIP<int>(res.x - _centerX, 0, 639) >> 1;
+	res.y = (CLIP<int>(res.y - _centerY, 0, 299) * 2 + 1) / 3;
+	return res;
+}
+
+void SCI0_HerculesDriver::setMousePos(const Common::Point &pos) const {
+	g_system->warpMouse((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
+}
+
+void SCI0_HerculesDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
+	g_system->setShakePos(shakeXOffset << 1, (shakeYOffset & ~1) * 3 / 2 + (shakeYOffset & 1));
+}
+
+void SCI0_HerculesDriver::clearRect(const Common::Rect &r) const {
+	Common::Rect r2((r.left << 1) + _centerX, (r.top & ~1) * 3 / 2 + (r.top & 1) + _centerY, (r.right << 1) + 40, (r.bottom & ~1) * 3 / 2 + (r.bottom & 1) + _centerY);
+	GfxDriver::clearRect(r2);
+}
+
+Common::Point SCI0_HerculesDriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
+}
+
+template <typename T> void herculesRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+	T *d = reinterpret_cast<T*>(dst);
+	const T *p = reinterpret_cast<const T*>(pal);
+
+	for (int i = 0; i < w; ++i) {
+		byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
+		if (sizeof(T) == 1) {
+			*d++ = (pt >> 1) & 1;
+			*d++ = pt & 1;
+		} else {
+			*d++ = p[(pt >> 1) & 1];
+			*d++ = p[pt & 1];
+		}
+		tx = (tx + 1) & 3;
+	}
+
+	dst = reinterpret_cast<byte*>(d);
+}
+
+void SCI0_HerculesDriver::setupRenderProc() {
+	static const LineProc lineProcs[] = {
+		&herculesRenderLine<byte>,
+		&herculesRenderLine<uint16>,
+		&herculesRenderLine<uint32>
+	};
+
+	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+	_renderLine = lineProcs[_pixelSize >> 1];
+}
+
+const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI0_Hercules)
+
+GfxDriver *SCI0_HerculesDriver_create(int rgbRendering, ...) {
+	static const uint32 monochromeColors[] = { 0xffbf66, 0x66ff66, 0xffffff };
+	va_list args;
+	va_start(args, rgbRendering);
+	int config = CLIP<int>(va_arg(args, int), 0, ARRAYSIZE(monochromeColors));
+	va_end(args);
+
+	return new SCI0_HerculesDriver(monochromeColors[config], rgbRendering != 0, false);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/init.cpp b/engines/sci/graphics/drivers/init.cpp
new file mode 100644
index 00000000000..431cad229d9
--- /dev/null
+++ b/engines/sci/graphics/drivers/init.cpp
@@ -0,0 +1,167 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "sci/graphics/drivers/gfxdriver.h"
+#include "sci/sci.h"
+#include "common/config-manager.h"
+#include "common/language.h"
+#include "common/platform.h"
+
+namespace Sci {
+
+#define SCI_GFXDRV_DCL1(name) \
+	extern GfxDriver *name##Driver_create(int, ...)
+
+#define SCI_GFXDRV_DCL2(name) \
+	extern bool name##Driver_validateMode(Common::Platform); \
+	extern GfxDriver *name##Driver_create(int, ...)
+
+SCI_GFXDRV_DCL1(GfxDefault);
+SCI_GFXDRV_DCL1(UpscaledGfx);
+SCI_GFXDRV_DCL1(PC98Gfx16Colors);
+SCI_GFXDRV_DCL1(WindowsGfx256Colors);
+SCI_GFXDRV_DCL2(SCI1_EGA);
+SCI_GFXDRV_DCL2(SCI1_VGAGreyScale);
+SCI_GFXDRV_DCL2(SCI0_CGA);
+SCI_GFXDRV_DCL2(SCI0_CGABW);
+SCI_GFXDRV_DCL2(SCI0_Hercules);
+SCI_GFXDRV_DCL2(SCI0_PC98Gfx8Colors);
+SCI_GFXDRV_DCL2(SCI1_PC98Gfx8Colors);
+SCI_GFXDRV_DCL2(WindowsGfx16Colors);
+
+#undef SCI_GFXDRV_DCL1
+#undef SCI_GFXDRV_DCL2
+
+enum HiresSetting {
+	kUnused = -1,
+	kDisable = 0,
+	kEnable = 1
+};
+
+struct GfxDriverInfo {
+	Common::RenderMode renderMode;
+	Common::Platform platform;
+	SciVersion versionMin;
+	SciVersion versionMax;
+	SciGameId gameId;
+	Common::Language language;
+	HiresSetting hires;
+	bool (*validateMode)(Common::Platform);
+	GfxDriver *(*createDriver)(int, ...);
+	int config;
+};
+
+#define INITPROCS1(x) nullptr, x##Driver_create
+#define INITPROCS2(x) x##Driver_validateMode, x##Driver_create
+
+static const GfxDriverInfo _gfxDriverInfos[] = {
+	// Selected modes
+	{ Common::kRenderEGA, Common::kPlatformUnknown, SCI_VERSION_1_EARLY, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_EGA), 0 },
+	{ Common::kRenderVGAGrey, Common::kPlatformUnknown, SCI_VERSION_1_EARLY, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_VGAGreyScale), 0 },
+	{ Common::kRenderCGA, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_CGA), 0 },
+	{ Common::kRenderCGA_BW, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_CGABW), 2 },
+	{ Common::kRenderHercA, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_Hercules), 0 },
+	{ Common::kRenderHercG, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_Hercules), 1 },
+	{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_0_LATE, SCI_VERSION_0_LATE, GID_PQ2, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_PC98Gfx8Colors), 1 },
+	{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_01, SCI_VERSION_01, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_PC98Gfx8Colors), 0 },
+	{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_PC98Gfx8Colors), 0 },
+	{ Common::kRenderWin16c, Common::kPlatformUnknown, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(WindowsGfx16Colors), 0 },
+	// Default modes
+	{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_0_LATE, SCI_VERSION_0_LATE, GID_PQ2, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 2 },
+	{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_01, SCI_VERSION_01, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 0 },
+	{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 1 },
+	{ Common::kRenderDefault, Common::kPlatformWindows, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kDisable, INITPROCS1(WindowsGfx256Colors), 0 },
+	{ Common::kRenderDefault, Common::kPlatformWindows, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_KQ6, Common::UNK_LANG, kEnable, INITPROCS1(WindowsGfx256Colors), 1 },
+	{ Common::kRenderDefault, Common::kPlatformDOS, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_KQ6, Common::UNK_LANG, kEnable, INITPROCS1(WindowsGfx256Colors), 1 },
+	{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_1, GID_ALL, Common::KO_KOR, kUnused, INITPROCS1(UpscaledGfx), 0 },
+	{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(GfxDefault), 0 },
+	{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_1_EARLY, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(GfxDefault), 1 }
+};
+
+#undef INITPROCS1
+#undef INITPROCS2
+
+} // End of namespace Sci
+
+namespace SciGfxDriver {
+using namespace Sci;
+
+Common::RenderMode getRenderMode() {
+	// Check if the selected render mode is available for the game. This is quite specific for each game. Sometime it
+	// is only EGA, sometimes only CGA b/w without CGA 4 colors, etc. Also set default mode if undithering is enabled.
+	bool undither = ConfMan.getBool("disable_dithering");
+	Common::RenderMode selectedMode = ConfMan.hasKey("render_mode") ? Common::parseRenderMode(ConfMan.get("render_mode")) : Common::kRenderDefault;
+	Common::RenderMode result = selectedMode;
+	Common::Language lang = g_sci->getLanguage();
+	Common::Platform platform = g_sci->getPlatform();
+	SciVersion version = getSciVersion();
+
+	// No extra modes supported for the Korean fan-patched games.
+	// Also set default mode if undithering is enabled for a SCI0 game and the render mode is either set to kRenderEGA
+	// or kRenderPC98_16c (probably redundant nowadays, but why not make sure everything works as intended).
+	if (lang == Common::KO_KOR || (undither && ((selectedMode == Common::kRenderEGA && (version <= SCI_VERSION_0_LATE || version == SCI_VERSION_1_EGA_ONLY)) || selectedMode == Common::kRenderPC98_16c)))
+		result = Common::kRenderDefault;
+
+	if (result == Common::kRenderDefault)
+		return result;
+
+	// Now we just go over the first part of the table and if we find a config for the selected render mode and check
+	// if the mode is usable. Otherwise we return the default mode.
+	for (const GfxDriverInfo *info = _gfxDriverInfos; info->renderMode != Common::kRenderDefault; ++info) {
+		if (info->renderMode == selectedMode && info->versionMin <= version && version <= info->versionMax && (info->gameId == GID_ALL || info->gameId == g_sci->getGameId())
+			&& (info->language == Common::UNK_LANG || info->language == lang) && (info->hires == kUnused || (info->hires == kEnable) == g_sci->useHiresGraphics())) {
+				result = (info->validateMode == nullptr || info->validateMode(platform)) ? selectedMode : Common::kRenderDefault;
+				break;
+		}
+	}
+
+	return result;
+}
+
+GfxDriver *create(Common::RenderMode renderMode, int width, int height) {
+	GfxDriver *result = nullptr;
+
+	int requestRGB = (int)(ConfMan.hasKey("palette_mods") && ConfMan.getBool("palette_mods")) || (ConfMan.hasKey("rgb_rendering") && ConfMan.getBool("rgb_rendering"));
+	SciVersion version = getSciVersion();
+	SciGameId gameId = g_sci->getGameId();
+	Common::Language lang = g_sci->getLanguage();
+	Common::Platform platform = g_sci->getPlatform();
+	bool hires = g_sci->useHiresGraphics();
+
+	bool undither = ConfMan.hasKey("disable_dithering") ? ConfMan.getBool("disable_dithering") : false;
+	bool winCursors = ConfMan.hasKey("windows_cursors") ? ConfMan.getBool("windows_cursors") : false;
+
+	// If a specific render mode is requested, we try to find a driver that supports it. Otherwise, we try to find a
+	// driver that supports the current platform, game and version.
+	for (const GfxDriverInfo *info = _gfxDriverInfos; info < &_gfxDriverInfos[ARRAYSIZE(_gfxDriverInfos)]; ++info)  {
+		if (((info->renderMode == Common::kRenderDefault && (info->platform == Common::kPlatformUnknown || info->platform == platform)) || (renderMode != Common::kRenderDefault && info->renderMode == renderMode))
+			&& version >= info->versionMin && version <= info->versionMax && (info->gameId == GID_ALL || info->gameId == gameId) && (info->language == Common::UNK_LANG || info->language == lang) && (info->hires == kUnused || (info->hires == kEnable) == hires)) {
+				result = info->createDriver(requestRGB, info->config, width, height, undither, winCursors, hires);
+				break;
+		}
+	}
+
+	return result;
+}
+
+} // End of namespace SciGfxDriver
+
diff --git a/engines/sci/graphics/drivers/pc98_16col.cpp b/engines/sci/graphics/drivers/pc98_16col.cpp
new file mode 100644
index 00000000000..6415aa30775
--- /dev/null
+++ b/engines/sci/graphics/drivers/pc98_16col.cpp
@@ -0,0 +1,244 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
+public:
+	enum SjisFontStyle {
+		kFontStyleNone,
+		kFontStyleTextMode,
+		kFontStyleSpecialSCI1
+	};
+
+	PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette);
+	~PC98Gfx16ColorsDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	byte remapTextColor(byte color) const override;
+private:
+	const byte *_convPalette;
+	const byte *_textModePalette;
+	const bool _cursorScaleHeightOnly;
+	SjisFontStyle _fontStyle;
+};
+
+PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette) :
+	UpscaledGfxDriver(textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
+		_cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
+	// Palette taken from driver file (identical for all versions of the
+	// driver I have seen so far, also same for SCI0 and SCI1)
+	static const byte pc98colorsV16[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x07, 0x07,
+		0x07, 0x00, 0x00, 0x07, 0x00, 0x07, 0x05, 0x07, 0x00, 0x09, 0x09, 0x09,
+		0x06, 0x06, 0x06, 0x00, 0x00, 0x0f, 0x07, 0x0f, 0x06, 0x00, 0x0f, 0x0f,
+		0x0f, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f
+	};
+
+	byte *col = new byte[768]();
+	const byte *s = pc98colorsV16;
+
+	for (uint i = 0; i < sizeof(pc98colorsV16) / 3; ++i) {
+		int a = ((i & 6) == 4 || (i & 6) == 2 ? i ^ 6 : i) * 3;
+		col[a + 0] = (s[1] * 0x11);
+		col[a + 1] = (s[0] * 0x11);
+		col[a + 2] = (s[2] * 0x11);
+		s += 3;
+	}
+
+	if (_fontStyle == kFontStyleTextMode) {
+		byte *d = &col[48];
+		for (uint8 i = 0; i < 8; ++i) {
+			*d++ = (i & 4) ? 0xff : 0;
+			*d++ = (i & 2) ? 0xff : 0;
+			*d++ = (i & 1) ? 0xff : 0;
+		}
+	}
+
+	if (needsUnditheringPalette) {
+		// We store the text mode color separately, since we need the slots for the undithering.
+		if (_fontStyle == kFontStyleTextMode) {
+			byte *tpal = new byte[24]();
+			memcpy(tpal, &col[48], 24);
+			_textModePalette = tpal;
+		}
+		// For the undithered mode, we generate the missing colors using the same formula as for EGA.
+		byte *d = &col[48];
+		for (int i = 16; i < 256; i++) {
+			const byte *s1 = &col[(i & 0x0f) * 3];
+			const byte *s2 = &col[(i >> 4) * 3];
+			for (int ii = 0; ii < 3; ++ii)
+				*d++ = (byte)(0.5 + (pow(0.5 * ((pow(*s1++ / 255.0, 2.2 / 1.0) * 255.0) + (pow(*s2++ / 255.0, 2.2 / 1.0) * 255.0)) / 255.0, 1.0 / 2.2) * 255.0));
+		}
+	}
+
+	_convPalette = col;
+}
+
+PC98Gfx16ColorsDriver::~PC98Gfx16ColorsDriver() {
+	delete[] _convPalette;
+	delete[] _textModePalette;
+}
+
+void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
+	assert(h == 16); // This is really not suitable for anything but the special SCI1 PC98 glyph drawing
+	dstPitch -= w;
+	srcPitch -= w;
+
+	while (h--) {
+		if (h > 10 || h < 5) {
+			for (int i = 0; i < w - 1; ++i) {
+				uint8 a = *src++;
+				uint8 b = *src;
+				if (a != transpCol)
+					*dst = a;
+				else if (b != transpCol)
+					*dst = b;
+				++dst;
+			}
+			byte l = *src++;
+			if (l != transpCol)
+				*dst = l;
+			++dst;
+		} else {
+			for (int i = 0; i < w; ++i) {
+				byte in = *src++;
+				if (in != transpCol)
+					*dst = in;
+				++dst;
+			}
+		}
+		src += srcPitch;
+		dst += dstPitch;
+	}
+}
+
+void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	UpscaledGfxDriver::initScreen(format);
+
+	assert(_convPalette);
+	GfxDefaultDriver::setPalette(_convPalette, 0, 256, true, nullptr, nullptr);
+
+	if (_fontStyle == kFontStyleTextMode)
+		_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
+
+	if (_fontStyle != kFontStyleSpecialSCI1)
+		return;
+
+	_renderGlyph = &renderPC98GlyphSpecial;
+}
+
+void PC98Gfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	if (!_cursorScaleHeightOnly) {
+		UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
+		return;
+	}
+
+	// Special case for PQ2 which scales the cursor height (but not the width)
+	adjustCursorBuffer(w, h << 1);
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d = _compositeBuffer;
+
+	for (uint i = 0; i < h; ++i) {
+		memcpy(d, s, w);
+		d += w;
+		memcpy(d, s, w);
+		d += w;
+		s += w;
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX, hotspotY << 1, keycolor);
+}
+
+byte PC98Gfx16ColorsDriver::remapTextColor(byte color) const {
+	// Always black for QFG and SCI1. For QFG, this is on purpose. The code just copies the inverted glyph data
+	// into all 4 vmem planes. For SCI1 the driver opcode actually has a pen color argument, but it isn't put
+	// to any use. Not sure if it is intended.
+	if (_fontStyle != kFontStyleTextMode)
+		return 0;
+
+	color &= 7;
+	// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
+	// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
+	// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
+	// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
+	// and cyan colors are turned into white.
+	if (color & 2)
+		color |= 4;
+	if (color & 4)
+		color |= 2;
+	// This is the blue color that PQ2 uses basically for all Japanese text...
+	if (color == 0)
+		color = 1;
+
+	byte textCol = color;
+	color += 0x10;
+
+	if (_textModePalette) {
+		// If we have used up the whole space of the CLUT8 for the undithering, we try
+		// to relocate the color which will work for all text mode colors with the default
+		// palette that is used by the PC-98 ports...
+		for (int i = 0; i < 256; ++i) {
+			if (_convPalette[i * 3] != _textModePalette[textCol * 3] || _convPalette[i * 3 + 1] != _textModePalette[textCol * 3 + 1] || _convPalette[i * 3 + 2] != _textModePalette[textCol * 3 + 2])
+				continue;
+			color = i;
+			break;
+		}
+		if (color >= 16)
+			color = 0;
+	}
+	return color;
+}
+
+GfxDriver *PC98Gfx16ColorsDriver_create(int rgbRendering, ...) {
+	va_list args;
+	va_start(args, rgbRendering);
+	int config = va_arg(args, int);
+	va_arg(args, int);
+	va_arg(args, int);
+	bool undither = (va_arg(args, int) != 0);
+	va_end(args);
+
+	GfxDriver *result = nullptr;
+	if (config == 0)
+		result = new PC98Gfx16ColorsDriver(8, false, false, PC98Gfx16ColorsDriver::kFontStyleNone, rgbRendering != 0, true);
+	else if (config == 1)
+		result = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, rgbRendering != 0, true);
+	else if (config == 2) {
+		// PQ2 is a bit special, probably the oldest of the PC-98 ports. Unlike all the others, it uses text mode print,
+		// so the text color is a system color outside the normal 16 colors palette. The original does not even have a
+		// 16 colors mode driver. Only the 8 colors mode, where the colors are identical for text and graphics mode.
+		// But we do want to provide the 16 colors mode, since it is not a big deal (i.e., it does not require data
+		// from a driver file and the fat print is also already there for the 8 colors mode). So we just make the
+		// necessary adjustments.
+		result = new PC98Gfx16ColorsDriver(8, false, true, PC98Gfx16ColorsDriver::kFontStyleTextMode, rgbRendering != 0, undither);
+	}
+	return result;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/pc98_8col_sci0.cpp b/engines/sci/graphics/drivers/pc98_8col_sci0.cpp
new file mode 100644
index 00000000000..20e5654a188
--- /dev/null
+++ b/engines/sci/graphics/drivers/pc98_8col_sci0.cpp
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI0_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
+public:
+	SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering);
+	~SCI0_PC98Gfx8ColorsDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	byte remapTextColor(byte color) const override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(_driverFiles, 2); }
+private:
+	const byte *_convPalette;
+	const bool _cursorScaleHeightOnly;
+	const bool _useTextMode;
+	static const char *_driverFiles[2];
+};
+
+SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering) :
+	UpscaledGfxDriver(8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _useTextMode(useTextModeForSJISChars), _convPalette(nullptr) {
+	byte *col = new byte[8 * 3]();
+	_convPalette = col;
+
+	for (uint8 i = 0; i < 8; ++i) {
+		*col++ = (i & 4) ? 0xff : 0;
+		*col++ = (i & 2) ? 0xff : 0;
+		*col++ = (i & 1) ? 0xff : 0;
+	}
+}
+
+SCI0_PC98Gfx8ColorsDriver::~SCI0_PC98Gfx8ColorsDriver() {
+	delete[] _convPalette;
+}
+
+void pc98SimpleDither(byte *dst, const byte *src, int pitch, int w, int h) {
+	int dstPitch = pitch << 1;
+	byte *d1 = dst;
+	byte *d2 = d1 + dstPitch;
+	pitch -= w;
+	dstPitch += (pitch << 1);
+
+	while (h--) {
+		for (int i = 0; i < w; ++i) {
+			uint8 v = *src++;
+			d1[0] = d2[0] = (v & 7);
+			d1[1] = d2[1] = (v & 8) ? (v & 7) : 0;
+			d1 += 2;
+			d2 += 2;
+		}
+		src += pitch;
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+}
+
+void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	UpscaledGfxDriver::initScreen(format);
+	_renderScaled = &pc98SimpleDither;
+	if (_useTextMode)
+		_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
+	assert(_convPalette);
+	GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
+}
+
+void SCI0_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	adjustCursorBuffer(w, _cursorScaleHeightOnly ? h << 1 : h);
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d = _compositeBuffer;
+	uint32 newKeyColor = 0xFF;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < w; ++ii) {
+			byte col = *s++;
+			*d++ = (col == keycolor) ? newKeyColor : (col & 7);
+		}
+	}
+
+	// Special case for PQ2 which 2x scales the cursor height
+	if (_cursorScaleHeightOnly) {
+		s = _compositeBuffer + h * w - w;
+		d = _compositeBuffer + h * w * 2 - w;
+
+		for (uint i = 0; i < h; ++i) {
+			memcpy(d, s, w);
+			d -= w;
+			memcpy(d, s, w);
+			d -= w;
+			s -= w;
+		}
+		h <<= 1;
+		hotspotX <<= 1;
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, newKeyColor);
+}
+
+byte SCI0_PC98Gfx8ColorsDriver::remapTextColor(byte color) const {
+	// Always black. For QFG, this is on purpose. The code just copies the inverted glyph data into all 4 vmem planes.
+	if (!_useTextMode)
+		return 0;
+
+	color &= 7;
+	// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
+	// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
+	// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
+	// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
+	// and cyan colors are turned into white.
+	if (color & 2)
+		color |= 4;
+	if (color & 4)
+		color |= 2;
+	// This is the blue color that PQ2 uses basically for all Japanese text...
+	if (color == 0)
+		color = 1;
+
+	return color;
+}
+
+const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI0_PC98Gfx8Colors)
+
+GfxDriver *SCI0_PC98Gfx8ColorsDriver_create(int rgbRendering, ...) {
+	va_list args;
+	va_start(args, rgbRendering);
+	bool conf = (va_arg(args, int) != 0);
+	va_end(args);
+
+	return new SCI0_PC98Gfx8ColorsDriver(conf, conf, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/pc98_8col_sci1.cpp b/engines/sci/graphics/drivers/pc98_8col_sci1.cpp
new file mode 100644
index 00000000000..738707c558a
--- /dev/null
+++ b/engines/sci/graphics/drivers/pc98_8col_sci1.cpp
@@ -0,0 +1,221 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI1_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
+public:
+	SCI1_PC98Gfx8ColorsDriver(bool rgbRendering);
+	~SCI1_PC98Gfx8ColorsDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	byte remapTextColor(byte) const override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(&_driverFile, 1); }
+private:
+	const byte *_ditheringTable;
+	const byte *_convPalette;
+	static const char *_driverFile;
+};
+
+SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
+	Common::File drv;
+	if (!drv.open(_driverFile))
+		GFXDRV_ERR_OPEN(_driverFile);
+
+	uint16 eprcOffs = 0;
+
+	uint32 cmd = drv.readUint32LE();
+	if ((cmd & 0xFF) == 0xE9)
+		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
+
+	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
+		GFXDRV_ERR_VERSION(_driverFile);
+
+	uint32 pos = (drv.pos() + 1) & ~1;
+
+	drv.seek(pos + 2);
+	drv.seek(drv.readUint16LE());
+	byte *buff = new byte[190];
+	drv.read(buff, 190);
+
+	uint16 tableOffs = 0;
+	int step = 0;
+	for (int i = 0; i < 182 && !tableOffs; ++i) {
+		uint32 c = READ_BE_UINT32(buff + i);
+		if (step == 0 && ((c & 0xFFF0FFF0) != 0xD1E0D1E0 || (READ_BE_UINT32(buff + i + 4) ^ c)))
+			continue;
+
+		if (step == 0) {
+			step = 1;
+			i += 7;
+			continue;
+		}
+
+		if (c >> 20 != 0x81C || ((READ_BE_UINT32(buff + i + 4) >> 20) ^ (c >> 20)))
+			continue;
+
+		if ((c & 0xFFFF) == READ_BE_UINT16(buff + i + 6))
+			tableOffs = FROM_BE_16(c);
+	}
+	delete[] buff;
+
+	if (!tableOffs)
+		error("SCI1_PC98Gfx8ColorsDriver: Failed to load dithering data from '%s'", _driverFile);
+
+	drv.seek(tableOffs);
+	byte *dmx = new byte[96]();
+	drv.read(dmx, 96);
+
+	if (drv.readUint16LE() != 0xA800 || drv.readUint16LE() != 0xB000)
+		GFXDRV_ERR_VERSION(_driverFile);
+
+	drv.close();
+
+	byte *dt = new byte[1536]();
+	_ditheringTable = dt;
+
+	for (uint16 i = 0; i < 256; ++i) {
+		for (int ii = 0; ii < 6; ++ii)
+			*dt++ = (dmx[(i >> 4) * 6 + ii] & 0xCC) | (dmx[(i & 0x0f) * 6 + ii] & 0x33);
+	}
+
+	delete[] dmx;
+
+	_textAlignX = 1;
+
+	byte *col = new byte[8 * 3]();
+	_convPalette = col;
+
+	for (uint8 i = 0; i < 8; ++i) {
+		*col++ = (i & 2) ? 0xff : 0;
+		*col++ = (i & 1) ? 0xff : 0;
+		*col++ = (i & 4) ? 0xff : 0;
+	}
+}
+
+SCI1_PC98Gfx8ColorsDriver::~SCI1_PC98Gfx8ColorsDriver() {
+	delete[] _ditheringTable;
+	delete[] _convPalette;
+}
+
+void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, const byte *tbl) {
+	int dstPitch = pitch << 1;
+	byte *d1 = dst;
+	byte *d2 = d1 + dstPitch;
+	pitch -= w;
+	dstPitch += (pitch << 1);
+
+	while (h--) {
+		byte sh = 0;
+		for (int i = 0; i < (w >> 1); ++i) {
+			const byte *c = &tbl[(src[0] << 4 | src[1]) * 6];
+			for (int ii = sh; ii < sh + 4; ++ii) {
+				*d1++ = (((c[0] >> (7 - ii)) & 1) << 2) | (((c[1] >> (7 - ii)) & 1) << 1) | ((c[2] >> (7 - ii)) & 1);
+				*d2++ = (((c[3] >> (7 - ii)) & 1) << 2) | (((c[4] >> (7 - ii)) & 1) << 1) | ((c[5] >> (7 - ii)) & 1);
+			}
+			src += 2;
+			sh ^= 4;
+		}
+
+		src += pitch;
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+}
+
+void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	UpscaledGfxDriver::initScreen(format);
+
+	_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
+
+	assert(_convPalette);
+	GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
+}
+
+void SCI1_PC98Gfx8ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	assert (h >= 0 && w >= 0);
+
+	byte diff = srcX & 7;
+	srcX &= ~7;
+	destX &= ~7;
+	w = (w + diff + 7) & ~7;
+
+	src += (srcY * pitch + srcX * _srcPixelSize);
+	if (src != _currentBitmap)
+		SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
+
+	byte *scb = _scaledBitmap + (destY << 1) * _screenW * _srcPixelSize + (destX << 1) * _srcPixelSize;
+	renderPlanarMatrix(scb, src, pitch, w, h, _ditheringTable);
+
+	updateScreen(destX << 1, destY << 1,  w << 1, h << 1, palMods, palModMapping);
+}
+
+void SCI1_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	adjustCursorBuffer(w << 1, h << 1);
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d1 = _compositeBuffer;
+	uint32 newKeyColor = 0xFF;
+
+	int dstPitch = (w << 1);
+	byte *d2 = _compositeBuffer + dstPitch;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < w; ++ii) {
+			byte col = *s++;
+			if (col == keycolor) {
+				*d1++ = *d2++ = newKeyColor;
+				*d1++ = *d2++ = newKeyColor;
+			} else {
+				*d1++ = *d2++ = (col & 7);
+				*d1++ = *d2++ = (col & 8) ? (col & 7) : 0;
+			}
+		}
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
+}
+
+byte SCI1_PC98Gfx8ColorsDriver::remapTextColor(byte) const {
+	// Always black. The driver opcode actually has a pen color argument, but it isn't put to any use. Not sure if it is intended.
+	return 0;
+}
+
+const char *SCI1_PC98Gfx8ColorsDriver::_driverFile = "9801V8.DRV";
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI1_PC98Gfx8Colors)
+
+GfxDriver *SCI1_PC98Gfx8ColorsDriver_create(int rgbRendering, ...) {
+	return new SCI1_PC98Gfx8ColorsDriver(rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/upscaled.cpp b/engines/sci/graphics/drivers/upscaled.cpp
new file mode 100644
index 00000000000..b25ed10cab8
--- /dev/null
+++ b/engines/sci/graphics/drivers/upscaled.cpp
@@ -0,0 +1,193 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+UpscaledGfxDriver::UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering) :
+	UpscaledGfxDriver(640, 400, textAlignX, scaleCursor, rgbRendering) {
+}
+
+UpscaledGfxDriver::UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering) :
+	GfxDefaultDriver(scaledW, scaledH, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
+	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _cursorWidth(0), _cursorHeight(0), _hScaleMult(2), _vScaleMult(2), _vScaleDiv(1) {
+	_virtualW = 320;
+	_virtualH = 200;
+}
+
+UpscaledGfxDriver::~UpscaledGfxDriver() {
+	delete[] _scaledBitmap;
+}
+
+void renderGlyph(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
+	dstPitch -= w;
+	srcPitch -= w;
+
+	while (h--) {
+		for (int i = 0; i < w; ++i) {
+			byte in = *src++;
+			if (in != transpCol)
+				*dst = in;
+			++dst;
+		}
+		src += srcPitch;
+		dst += dstPitch;
+	}
+}
+
+void UpscaledGfxDriver::initScreen(const Graphics::PixelFormat *format) {
+	GfxDefaultDriver::initScreen(format);
+	_scaledBitmap = new byte[_screenW * _screenH * _srcPixelSize]();
+
+	static const ScaledRenderProc scaledRenderProcs[] = {
+		&SciGfxDrvInternal::scale2x<byte>,
+		&SciGfxDrvInternal::scale2x<uint16>,
+		&SciGfxDrvInternal::scale2x<uint32>
+	};
+	assert((_srcPixelSize >> 1) < ARRAYSIZE(scaledRenderProcs));
+	_renderScaled = scaledRenderProcs[_srcPixelSize >> 1];
+	_renderGlyph = &renderGlyph;
+}
+
+void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	if (_pixelSize == 1) {
+		GfxDefaultDriver::setPalette(colors, start, num, update, palMods, palModMapping);
+		return;
+	}
+	updatePalette(colors, start, num);
+	if (update)
+		updateScreen(0, 0, _screenW, _screenH, palMods, palModMapping);
+	if (_cursorUsesScreenPalette)
+		CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
+}
+
+void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	assert (h >= 0 && w >= 0);
+
+	src += (srcY * pitch + srcX * _srcPixelSize);
+	if (src != _currentBitmap)
+		SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
+
+	int realWidth = 0;
+	int realHeight = 0;
+
+	// We need to scale and color convert the bitmap in separate functions, because we want
+	// to keep the scaled non-color-modified bitmap for palette updates in rgb rendering mode.
+	renderBitmap(src, pitch, destX, destY, w, h, realWidth, realHeight);
+
+	Common::Point p(destX, destY);
+	p = getRealCoords(p);
+
+	updateScreen(p.x, p.y, realWidth, realHeight, palMods, palModMapping);
+}
+
+void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	if (_scaleCursor) {
+		adjustCursorBuffer(w << 1, h << 1);
+		SciGfxDrvInternal::scale2x<byte>(_compositeBuffer, reinterpret_cast<const byte*>(cursor), w, w, h);
+		CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
+	} else {
+		CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
+	}
+}
+
+Common::Point UpscaledGfxDriver::getMousePos() const {
+	Common::Point res = GfxDriver::getMousePos();
+	res.x /= _hScaleMult;
+	res.y = res.y * _vScaleDiv / _vScaleMult;
+	return res;
+}
+
+void UpscaledGfxDriver::setMousePos(const Common::Point &pos) const {
+	g_system->warpMouse(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
+}
+
+void UpscaledGfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
+	g_system->setShakePos(shakeXOffset * _hScaleMult, shakeYOffset * _vScaleMult / _vScaleDiv);
+}
+
+void UpscaledGfxDriver::clearRect(const Common::Rect &r) const {
+	Common::Rect r2(r.left * _hScaleMult, r.top * _vScaleMult / _vScaleDiv, r.right * _hScaleMult, r.bottom * _vScaleMult / _vScaleDiv);
+	GfxDriver::clearRect(r2);
+}
+
+Common::Point UpscaledGfxDriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
+}
+
+void UpscaledGfxDriver::drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	hiresDestX &= ~(_textAlignX - 1);
+	byte *scb = _scaledBitmap + hiresDestY * _screenW * _srcPixelSize + hiresDestX * _srcPixelSize;
+	_renderGlyph(scb, _screenW, src, pitch, hiresW, hiresH, transpColor);
+	updateScreen(hiresDestX, hiresDestY, hiresW, hiresH, palMods, palModMapping);
+}
+
+void UpscaledGfxDriver::updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	byte *buff = _compositeBuffer;
+	int pitch = w * _pixelSize;
+	byte *scb = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
+	if (palMods && palModMapping) {
+		_colorConvMod(buff, scb, _screenW, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
+	} else if (_pixelSize != _srcPixelSize) {
+		_colorConv(buff, scb, _screenW, w, h, _internalPalette);
+	} else {
+		buff = scb;
+		pitch = _screenW *_pixelSize;
+	}
+
+	g_system->copyRectToScreen(buff, pitch, destX, destY, w, h);
+}
+
+void UpscaledGfxDriver::adjustCursorBuffer(uint16 newWidth, uint16 newHeight) {
+	// For configs which need/have the composite buffer for other purposes, we can skip this.
+	if (!_compositeBuffer)
+		_needCursorBuffer = true;
+	else if (!_needCursorBuffer)
+		return;
+
+	if (_cursorWidth * _cursorHeight < newWidth * newHeight) {
+		delete[] _compositeBuffer;
+		_compositeBuffer = new byte[newWidth * newHeight * _srcPixelSize]();
+		_cursorWidth = newWidth;
+		_cursorHeight = newHeight;
+	}
+}
+
+void UpscaledGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
+	byte *scb = _scaledBitmap + (dy << 1) * _screenW * _srcPixelSize + (dx << 1) * _srcPixelSize;
+	_renderScaled(scb, src, pitch, w, h);
+	realWidth = w << 1;
+	realHeight = h << 1;
+}
+
+GfxDriver *UpscaledGfxDriver_create(int rgbRendering, ...) {
+	return new UpscaledGfxDriver(1, true, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/vgagrey.cpp b/engines/sci/graphics/drivers/vgagrey.cpp
new file mode 100644
index 00000000000..991e44e5db1
--- /dev/null
+++ b/engines/sci/graphics/drivers/vgagrey.cpp
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class SCI1_VGAGreyScaleDriver final : public GfxDefaultDriver {
+public:
+	SCI1_VGAGreyScaleDriver(bool rgbRendering);
+	~SCI1_VGAGreyScaleDriver() override;
+	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
+private:
+	byte *_greyScalePalette;
+	static const char *_driverFile;
+};
+
+SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, false, rgbRendering), _greyScalePalette(nullptr) {
+	_greyScalePalette = new byte[_numColors * 3]();
+}
+
+SCI1_VGAGreyScaleDriver::~SCI1_VGAGreyScaleDriver() {
+	delete[] _greyScalePalette;
+}
+
+void SCI1_VGAGreyScaleDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	byte *d = _greyScalePalette;
+	for (uint i = 0; i < num; ++i) {
+		// In the driver files I inspected there were never any other color distributions than this.
+		// So I guess it is safe to hardcode that instead of loading it from the driver file.
+		d[0] = d[1] = d[2] = (colors[0] * 77 + colors[1] * 150 + colors[2] * 28) >> 8;
+		colors += 3;
+		d += 3;
+	}
+
+	GfxDefaultDriver::setPalette(_greyScalePalette, start, num, update, palMods, palModMapping);
+}
+
+const char *SCI1_VGAGreyScaleDriver::_driverFile = "VGA320BW.DRV";
+
+SCI_GFXDRV_VALIDATE_IMPL(SCI1_VGAGreyScale)
+
+GfxDriver *SCI1_VGAGreyScaleDriver_create(int rgbRendering, ...) {
+	return new SCI1_VGAGreyScaleDriver(rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/win16col.cpp b/engines/sci/graphics/drivers/win16col.cpp
new file mode 100644
index 00000000000..d35cf609122
--- /dev/null
+++ b/engines/sci/graphics/drivers/win16col.cpp
@@ -0,0 +1,184 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class WindowsGfx16ColorsDriver final : public SCI1_EGADriver {
+public:
+	// The original does not take into account the extra lines required for the 200->440 vertical scaling. There is a noticeable dithering glitch every 11th line, as the
+	// two pixels of the checkerbox pattern appear in the wrong order. I have implemented a fix for this which can be activated with the fixDithering parameter.
+	WindowsGfx16ColorsDriver(bool fixDithering, bool rgbRendering);
+	~WindowsGfx16ColorsDriver() override {}
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformWindows && checkDriver(&_driverFile, 1)); }
+private:
+	void loadData() override;
+	void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) override;
+	LineProc _renderLine2;
+	const bool _enhancedDithering;
+	static const char *_driverFile;
+};
+
+WindowsGfx16ColorsDriver::WindowsGfx16ColorsDriver(bool enhancedDithering, bool rgbRendering) : SCI1_EGADriver(rgbRendering), _enhancedDithering(enhancedDithering), _renderLine2(nullptr) {
+	static const byte win16Colors[48] = {
+		0x00, 0x00, 0x00, 0xA8, 0x00, 0x57, 0x00, 0xA8, 0x57, 0xA8, 0xA8, 0x57,
+		0x00, 0x00, 0xA8, 0xA8, 0x57, 0xA8, 0x57, 0xA8, 0xA8, 0x87, 0x88, 0x8F,
+		0xC0, 0xC7, 0xC8, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
+		0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+	};
+
+	_convPalette = win16Colors;
+	_vScaleMult = 11;
+	_vScaleDiv = 5;
+}
+
+template <typename T, bool extScale> void win16ColRenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool swap) {
+	const T *p = reinterpret_cast<const T*>(pal);
+	T *d1 = reinterpret_cast<T*>(dst);
+	T *d2 = d1 + (w << 1);
+	T *d3 = d2 + (w << 1);
+	T *&d3r = swap ? d2 : d1;
+
+	if (swap)
+		SWAP(d1, d2);
+
+	for (int i = 0; i < w; ++i) {
+		byte pt = patterns[*src++];
+		if (sizeof(T) == 1) {
+			*d1++ = d2[1] = pt & 0x0F;
+			*d1++ = *d2++ = pt >> 4;
+		} else {
+			*d1++ = d2[1] = p[pt & 0x0F];
+			*d1++ = *d2++ = p[pt >> 4];
+		}
+		d2++;
+
+		if (extScale) {
+			*d3++ = *(d3r - 2);
+			*d3++ = *(d3r - 1);
+		}
+	}
+
+	dst = reinterpret_cast<byte*>(extScale ? d3 : (swap ? d1 : d2));
+}
+
+void WindowsGfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	SCI1_EGADriver::initScreen(format);
+
+	static const LineProc lineProcs[] = {
+		&win16ColRenderLine<byte, false>,
+		&win16ColRenderLine<byte, true>,
+		&win16ColRenderLine<uint16, false>,
+		&win16ColRenderLine<uint16, true>,
+		&win16ColRenderLine<uint32, false>,
+		&win16ColRenderLine<uint32, true>
+	};
+
+	assert((_pixelSize | 1) < ARRAYSIZE(lineProcs));
+	_renderLine = lineProcs[_pixelSize & ~1];
+	_renderLine2 = lineProcs[_pixelSize | 1];
+}
+
+void WindowsGfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
+	// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
+	byte col1 = SciGfxDrvInternal::findColorInPalette(0x00000000, _convPalette, _numColors);
+	byte col2 = SciGfxDrvInternal::findColorInPalette(0x00FFFFFF, _convPalette, _numColors);
+	SciGfxDrvInternal::renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, false);
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
+}
+
+Common::Point WindowsGfx16ColorsDriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point(pos.x << 1, (pos.y << 1) + (pos.y + 4) / 5);
+}
+
+void WindowsGfx16ColorsDriver::loadData() {
+	Common::File file;
+	if (!file.open(_driverFile))
+		GFXDRV_ERR_OPEN(_driverFile);
+
+	int64 sz = file.size();
+	byte *buf = new byte[sz];
+	file.read(buf, sz);
+	file.close();
+
+	// We can keep the search for the table simple, since there are only two supported executables (KQ6
+	// Windows and SQ4 Windows) and both contain the following value only within the pattern table...
+	uint32 srch = FROM_LE_32(0xCC4C4404);
+
+	const byte *tblOffs = nullptr;
+	for (const byte *pos = buf; pos < buf + sz - 67 && tblOffs == nullptr; ++pos) {
+		// We check three times, just to be sure. Checking once would actually suffice.
+		if (READ_UINT32(pos) != srch || READ_UINT32(pos + 8) != srch || READ_UINT32(pos + 64) != srch)
+			continue;
+		tblOffs = pos - 4;
+	}
+
+	if (tblOffs == nullptr)
+		error("%s(): Failed to load 16 colors match table", __FUNCTION__);
+
+	byte *tbl = new byte[512];
+	memcpy(tbl, tblOffs, 512);
+	_egaMatchTable = tbl;
+
+	delete[] buf;
+
+	_colAdjust = (_egaMatchTable[482] == 0x79) ? 4 : 0;
+	_numColors = 16;
+}
+
+void WindowsGfx16ColorsDriver::renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
+	const byte *dst0 = dst;
+	byte mod = (y + 4) % 5;
+	byte swap = _enhancedDithering ? ((y + 4) / 5) & 1 : 0;
+	for (int i = 0; i < h; ++i) {
+		if (++mod == 5) {
+			_renderLine2(dst, src, w, patterns, palette, swap);
+			if (_enhancedDithering)
+				swap ^= 1;
+			mod = 0;
+		} else {
+			_renderLine(dst, src, w, patterns, palette, swap);
+		}
+		src += pitch;
+	}
+	realWidth = w << 1;
+	realHeight = (dst - dst0) / (realWidth * _pixelSize);
+}
+
+const char *WindowsGfx16ColorsDriver::_driverFile = "SCIWV.EXE";
+
+SCI_GFXDRV_VALIDATE_IMPL(WindowsGfx16Colors)
+
+GfxDriver *WindowsGfx16ColorsDriver_create(int rgbRendering, ...) {
+	return new WindowsGfx16ColorsDriver(true, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/drivers/win256col.cpp b/engines/sci/graphics/drivers/win256col.cpp
new file mode 100644
index 00000000000..33fba8e1314
--- /dev/null
+++ b/engines/sci/graphics/drivers/win256col.cpp
@@ -0,0 +1,259 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+#include "sci/graphics/drivers/gfxdriver_intern.h"
+
+namespace Sci {
+
+class WindowsGfx256ColorsDriver final : public UpscaledGfxDriver {
+public:
+	WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow, bool rgbRendering);
+	~WindowsGfx256ColorsDriver() override {}
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+	Common::Point getRealCoords(Common::Point &pos) const override;
+	void setColorMap(const byte *colorMap) override { _colorMap = colorMap; }
+	void setFlags(uint32 flags) override;
+	void clearFlags(uint32 flags) override;
+	bool supportsHiResGraphics() const override { return !_smallWindow; }
+	bool driverBasedTextRendering() const override { return false; }
+protected:
+	typedef void (*LineProc)(byte*&, const byte*, int, int, int);
+	LineProc _renderLine;
+private:
+	typedef void (*LineProcSpec)(byte*&, const byte*, int, int, const byte*);
+	LineProcSpec _renderLine2;
+	void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) override;
+	uint32 _flags;
+	const byte *_colorMap;
+	const bool _smallWindow;
+	const bool _dosStyleCursors;
+	uint16 _vScaleMult2;
+};
+
+WindowsGfx256ColorsDriver::WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow,bool rgbRendering) :
+	UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, coloredDosStyleCursors && !smallWindow, rgbRendering), _dosStyleCursors(coloredDosStyleCursors), _smallWindow(smallWindow),
+		_renderLine(nullptr), _renderLine2(nullptr), _flags(0), _colorMap(nullptr), _vScaleMult2(smallWindow ? 1 : 2) {
+	_virtualW = 320;
+	_virtualH = 200;
+	if (smallWindow)
+		_hScaleMult = 1;
+	_vScaleMult = _smallWindow ? 6 : 11;
+	_vScaleDiv = 5;
+}
+
+void largeWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
+	int dstPitch = pitch;
+	int dstPitch2 = pitch - (w << 1);
+	byte *d1 = dst;
+	byte *d2 = d1 + dstPitch;
+
+	if (ty == 5) {
+		byte *d3 = d2 + dstPitch;
+		for (int i = 0; i < w; ++i) {
+			d1[0] = d1[1] = d2[0] = d2[1] = d3[0] = d3[1] = *src++;
+			d1 += 2;
+			d2 += 2;
+			d3 += 2;
+		}
+		dst = d3 + dstPitch2;
+	} else {
+		for (int i = 0; i < w; ++i) {
+			d1[0] = d1[1] = d2[0] = d2[1] = *src++;
+			d1 += 2;
+			d2 += 2;
+		}
+		dst = d2 + dstPitch2;
+	}
+}
+
+void largeWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
+	int dstPitch = pitch;
+	int dstPitch2 = pitch - (w << 1);
+	byte *d1 = dst;
+	byte *d2 = d1 + dstPitch;
+
+	for (int i = 0; i < w; ++i) {
+		d1[0] = d1[1] = d2[0] = d2[1] = *src++;
+		d1 += 2;
+		d2 += 2;
+	}
+	dst = d2 + dstPitch2;
+}
+
+void smallWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
+	int dstPitch = pitch;
+	int dstPitch2 = pitch - w;
+	byte *d1 = dst;
+
+	if (ty == 5) {
+		byte *d2 = d1 + dstPitch;
+		for (int i = 0; i < w; ++i)
+			*d1++ = *d2++ = *src++;
+		dst = d2 + dstPitch2;
+	} else {
+		for (int i = 0; i < w; ++i)
+			*d1++ = *src++;
+		dst = d1 + dstPitch2;
+	}
+}
+
+void smallWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
+	int dstPitch = pitch - w;
+	byte *d1 = dst;
+
+	for (int i = 0; i < w; ++i)
+		*d1++ = *src++;
+	dst = d1 + dstPitch;
+}
+
+void hiresRenderLine(byte *&dst, const byte *src, int pitch, int w, const byte *colorMap) {
+	if (!colorMap) {
+		memcpy(dst, src, w);
+	} else {
+		byte *d = dst;
+		for (int i = 0; i < w; ++i)
+			*d++ = colorMap[*src++];
+	}
+	dst += pitch;
+}
+
+void renderLineDummy(byte *&, const byte* , int, int, const byte*) {
+}
+
+void WindowsGfx256ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	UpscaledGfxDriver::initScreen(format);
+	_renderLine = _smallWindow ? &smallWindowRenderLine : &largeWindowRenderLine;
+	_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
+}
+
+void WindowsGfx256ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+	GFXDRV_ASSERT_READY;
+	assert (h >= 0 && w >= 0);
+
+	if (!(_flags & (kHiResMode | kMovieMode))) {
+		UpscaledGfxDriver::copyRectToScreen(src, srcX, srcY, pitch, destX, destY, w, h, palMods, palModMapping);
+		return;
+	}
+
+	if (_flags & kMovieMode) {
+		destX = (_screenW >> 1) - (w & ~1) * _hScaleMult / 2;
+		destY = (_screenH >> 1) - (h & ~1) * _vScaleMult2 / 2;
+	}
+
+	src += (srcY * pitch + srcX * _srcPixelSize);
+	byte *dst = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
+
+	for (int i = 0; i < h; ++i) {
+		_renderLine2(dst, src, _screenW, w, _colorMap);
+		src += pitch;
+	}
+
+	if (_flags & kMovieMode) {
+		w *= _hScaleMult;
+		h *= _vScaleMult2;
+	}
+
+	updateScreen(destX, destY, w, h, palMods, palModMapping);
+}
+
+void WindowsGfx256ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	if (_dosStyleCursors) {
+		// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
+		// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
+		// It is a technical limitation on Windows 95 with VGA hardware that mouse cursors have to be b/w.
+		// Instead, we use the colored DOS style cursors as a default, since there was consensus to do that.
+		UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
+		return;
+	}
+	adjustCursorBuffer(w << 1, h << 1);
+
+	if (_pixelSize == 1)
+		copyCurrentPalette(_currentPalette, 0, _numColors);
+
+	byte col1 = SciGfxDrvInternal::findColorInPalette(0x00000000, _currentPalette, _numColors);
+	byte col2 = SciGfxDrvInternal::findColorInPalette(0x00FFFFFF, _currentPalette, _numColors);
+	SciGfxDrvInternal::renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, _smallWindow);
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
+}
+
+Common::Point WindowsGfx256ColorsDriver::getRealCoords(Common::Point &pos) const {
+	return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult2 + (pos.y + 4) / 5);
+}
+
+void WindowsGfx256ColorsDriver::setFlags(uint32 flags) {
+	flags ^= (_flags & flags);
+	if (!flags)
+		return;
+
+	if (flags & kMovieMode)
+		_renderLine2 = _smallWindow ? &smallWindowRenderLineMovie : &largeWindowRenderLineMovie;
+
+	_flags |= flags;
+}
+
+void WindowsGfx256ColorsDriver::clearFlags(uint32 flags) {
+	flags &= _flags;
+	if (!flags)
+		return;
+
+	if (flags & kMovieMode)
+		_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
+
+	_flags &= ~flags;
+}
+
+void WindowsGfx256ColorsDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
+	assert(_renderLine);
+
+	byte *dst = _scaledBitmap + (dy * _vScaleMult2 + (dy + 4) / 5) * _screenW * _srcPixelSize + dx *_hScaleMult * _srcPixelSize;
+	const byte *dstart = dst;
+	dy = (dy + 4) % 5;
+
+	while (h--) {
+		_renderLine(dst, src, _screenW, w, ++dy);
+		dy %= 5;
+		src += pitch;
+	}
+
+	realWidth = w * _hScaleMult;
+	realHeight = (dst - dstart) / _screenW;
+}
+
+GfxDriver *WindowsGfx256ColorsDriver_create(int rgbRendering, ...) {
+	va_list args;
+	va_start(args, rgbRendering);
+	int config = va_arg(args, int);
+	va_arg(args, int);
+	va_arg(args, int);
+	va_arg(args, int);
+	bool winCursors = (va_arg(args, int) != 0);
+	va_end(args);
+
+	return new WindowsGfx256ColorsDriver(!winCursors, config == 0, rgbRendering != 0);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/fontsjis.cpp b/engines/sci/graphics/fontsjis.cpp
index 462373cce6b..ba2dc499d29 100644
--- a/engines/sci/graphics/fontsjis.cpp
+++ b/engines/sci/graphics/fontsjis.cpp
@@ -21,7 +21,7 @@
 
 #include "sci/sci.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/scifont.h"
 #include "sci/graphics/fontsjis.h"
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
deleted file mode 100644
index ecdb45004ab..00000000000
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ /dev/null
@@ -1,2267 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "common/events.h"
-#include "common/system.h"
-
-#include "engines/util.h"
-
-#include "graphics/cursorman.h"
-#include "graphics/paletteman.h"
-
-#include "sci/graphics/gfxdrivers.h"
-#include "sci/resource/resource.h"
-#include "sci/sci.h"
-
-namespace Sci {
-
-#define GFXDRV_ASSERT_READY \
-	if (!_ready) \
-		error("%s(): initScreen() must be called before using this method", __FUNCTION__)
-
-#define GFXDRV_ERR_OPEN(x) \
-	error("%s(): Failed to open '%s'", __FUNCTION__, x)
-
-#define GFXDRV_ERR_VERSION(x) \
-	error("%s(): Driver file '%s' unknown version", __FUNCTION__, x)
-
-Common::Point GfxDriver::getMousePos() const {
-	return g_system->getEventManager()->getMousePos();
-}
-
-void GfxDriver::setMousePos(const Common::Point &pos) const {
-	g_system->warpMouse(pos.x, pos.y);
-}
-
-void GfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
-	g_system->setShakePos(shakeXOffset, shakeYOffset);
-}
-
-void GfxDriver::clearRect(const Common::Rect &r) const {
-	GFXDRV_ASSERT_READY;
-	g_system->fillScreen(r, 0);
-}
-
-void GfxDriver::copyCurrentPalette(byte *dest, int start, int num) const {
-	assert(dest);
-	assert(start + num <= 256);
-	g_system->getPaletteManager()->grabPalette(dest, start, num);
-}
-
-bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
-	Common::String missing;
-	while (listSize-- && *driverNames) {
-		if (Common::File::exists(*driverNames))
-			return true;
-		if (!missing.empty())
-			missing += " or ";
-		missing += "'" + Common::String(*driverNames) + "'";
-		++driverNames;
-	}
-	warning("Driver file %s not found. Starting game in default mode", missing.c_str());
-	return false;
-}
-
-GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering) : GfxDriver(screenWidth, screenHeight, 0), _cursorUsesScreenPalette(true),  _colorConv(nullptr), _colorConvMod(nullptr),
-	_srcPixelSize(1), _requestRGBMode(rgbRendering), _compositeBuffer(nullptr), _currentBitmap(nullptr), _internalPalette(nullptr), _currentPalette(nullptr), _virtualW(screenWidth), _virtualH(screenHeight), _alwaysCreateBmpBuffer(!isSCI0) {
-	switch (g_sci->getResMan()->getViewType()) {
-	case kViewEga:
-		_numColors = 16;	// QFG PC-98 with 8 colors also reports 16 here
-		break;
-	case kViewAmiga:
-		_numColors = 32;
-		break;
-	case kViewAmiga64:
-		_numColors = 64;
-		break;
-	case kViewVga:
-	case kViewVga11:
-		_numColors = 256;
-		break;
-	default:
-		break;
-	}
-
-	if (_numColors == 0)
-		error("GfxDefaultDriver: Unknown view type");
-}
-
-GfxDefaultDriver::~GfxDefaultDriver() {
-	delete[] _compositeBuffer;
-	delete[] _currentBitmap;
-	delete[] _internalPalette;
-	delete[] _currentPalette;
-}
-
-template <typename T> void colorConvert(byte *dst, const byte *src, int pitch, int w, int h, const byte *pal) {
-	T *d = reinterpret_cast<T*>(dst);
-	const T *p = reinterpret_cast<const T*>(pal);
-	const byte *s = src;
-	pitch -= w;
-
-	while (h--) {
-		for (int i = 0; i < w; ++i)
-			*d++ = p[*s++];
-		s += pitch;
-	}
-}
-
-#define applyMod(a, b) MIN<uint>(a * (128 + b) / 128, 255)
-template <typename T> void colorConvertMod(byte *dst, const byte *src, int pitch, int w, int h, const byte *srcPal, const byte *internalPal, Graphics::PixelFormat &f, const PaletteMod *mods, const byte *modMapping) {
-	T *d = reinterpret_cast<T*>(dst);
-	const T *p = reinterpret_cast<const T*>(internalPal);
-	const byte *s1 = src;
-	const byte *s2 = modMapping;
-	pitch -= w;
-
-	while (h--) {
-		for (int i = 0; i < w; ++i) {
-			byte m = *s2++;
-			if (m) {
-				const byte *col = &srcPal[*s1++ * 3];
-				*d++ = f.RGBToColor(applyMod(col[0], mods[m].r), applyMod(col[1], mods[m].g), applyMod(col[2], mods[m].b));
-			} else {
-				*d++ = p[*s1++];
-			}
-		}
-		s1 += pitch;
-		s2 += pitch;
-	}
-}
-#undef applyMod
-
-void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
-	Graphics::PixelFormat format8bt(Graphics::PixelFormat::createFormatCLUT8());
-	initGraphics(_screenW, _screenH, srcRGBFormat ? srcRGBFormat : (_requestRGBMode ? nullptr : &format8bt));
-	_format = g_system->getScreenFormat();
-
-	int srcPixelSize = srcRGBFormat ? _format.bytesPerPixel : 1;
-	if (srcPixelSize != _srcPixelSize || _pixelSize != _format.bytesPerPixel) {
-		delete[] _compositeBuffer;
-		delete[] _currentBitmap;
-		delete[] _internalPalette;
-		delete[] _currentPalette;
-		_compositeBuffer = _currentBitmap = _internalPalette = _currentPalette = nullptr;
-	}
-
-	_pixelSize = _format.bytesPerPixel;
-	_srcPixelSize = srcPixelSize;
-
-	if (_requestRGBMode && _pixelSize == 1)
-		warning("GfxDefaultDriver::initScreen(): RGB rendering not available in this ScummVM build");
-
-	if (_pixelSize != _srcPixelSize) {
-		uint32 bufferSize = _screenW * _screenH * _pixelSize;
-		_compositeBuffer = new byte[bufferSize]();
-		assert(_compositeBuffer);
-	}
-
-	// Not needed for SCI0, except for rgb rendering. Unfortunately, SCI_VERSION_01
-	// does need it and we can't tell the version from the number of colors there.
-	// That's why we have the _alwaysCreateBmpBuffer flag...
-	if (_alwaysCreateBmpBuffer || _numColors > 16 || _pixelSize > 1) {
-		_currentBitmap = new byte[_virtualW * _virtualH * _srcPixelSize]();
-		assert(_currentBitmap);
-	}
-
-	if (_numColors > 16 || _pixelSize > 1) {
-		// Not needed for SCI0, except for rgb rendering
-		_currentPalette = new byte[256 * 3]();
-		assert(_currentPalette);
-		if (_pixelSize != _srcPixelSize) {
-			_internalPalette = new byte[256 * _pixelSize]();
-			assert(_internalPalette);
-		}
-	}
-
-	static const ColorConvProc colorConvProcs[] = {
-		&colorConvert<byte>,
-		&colorConvert<uint16>,
-		&colorConvert<uint32>
-	};
-	assert((_pixelSize >> 1) < ARRAYSIZE(colorConvProcs));
-	_colorConv = colorConvProcs[_pixelSize >> 1];
-
-	static const ColorConvModProc colorConvModProcs[] = {
-		&colorConvertMod<byte>,
-		&colorConvertMod<uint16>,
-		&colorConvertMod<uint32>
-	};
-	assert((_pixelSize >> 1) < ARRAYSIZE(colorConvModProcs));
-	_colorConvMod = colorConvModProcs[_pixelSize >> 1];
-
-	_ready = true;
-}
-
-void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	if (_pixelSize > 1) {
-		updatePalette(colors, start, num);
-		if (update)
-			copyRectToScreen(_currentBitmap, 0, 0, _virtualW, 0, 0, _virtualW, _virtualH, palMods, palModMapping);
-		if (_cursorUsesScreenPalette)
-			CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
-	} else {
-		g_system->getPaletteManager()->setPalette(colors, start, num);
-	}
-}
-
-void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h) {
-	if (!dst)
-		return;
-
-	if (w == srcPitch && w == dstPitch) {
-		memcpy(dst + y * w, src, w * h);
-	} else {
-		const byte *s = src;
-		byte *d = dst + y * dstPitch + x;
-		for (int i = 0; i < h; ++i) {
-			memcpy(d, s, w);
-			s += srcPitch;
-			d += dstPitch;
-		}
-	}
-}
-
-void GfxDefaultDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	assert (h >= 0 && w >= 0);
-
-	src += (srcY * pitch + srcX * _srcPixelSize);
-	if (src != _currentBitmap)
-		updateBitmapBuffer(_currentBitmap, _screenW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
-
-	if (_pixelSize != _srcPixelSize) {
-		generateOutput(_compositeBuffer, src, pitch, w, h, palMods, palModMapping + destY * pitch + destX);
-		src = _compositeBuffer;
-		pitch = w * _pixelSize;
-	}
-
-	g_system->copyRectToScreen(src, pitch, destX, destY, w, h);
-}
-
-void GfxDefaultDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
-	if (_pixelSize > 1 && _currentPalette != nullptr)
-		CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
-}
-
-void GfxDefaultDriver::replaceMacCursor(const Graphics::Cursor*) {
-	// This is not needed for any non-Mac version of games at all)
-	error("GfxDefaultDriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
-}
-
-void GfxDefaultDriver::copyCurrentBitmap(byte *dest, uint32 size) const {
-	GFXDRV_ASSERT_READY;
-	assert(dest);
-	assert(size <= (uint32)(_screenW * _screenH));
-
-	// SCI 0 should not make calls to this method (except when using palette mods), but we have to know if it does...
-	if (!_currentBitmap)
-		error("GfxDefaultDriver::copyCurrentBitmap(): unexpected call");
-
-	// I have changed the implementation a bit from what the engine did before. For non-rgb rendering
-	// it would call OSystem::lockScreen() and then memcpy the data from there (which avoided the need
-	// for the extra bitmap buffer). However, OSystem::lockScreen() is meant more as an update method
-	// for the screen, the call to OSystem::unlockScreen() will turn the whole screen dirty (to be
-	// updated on the next OSysten::updateScreen() call. This is not what we need here, so I rather use
-	// the extra bitmap buffer (which is required for rgb rendering anyway).
-	memcpy(dest, _currentBitmap, size);
-}
-
-void GfxDefaultDriver::copyCurrentPalette(byte *dest, int start, int num) const {
-	GFXDRV_ASSERT_READY;
-
-	if (_pixelSize == 1) {
-		GfxDriver::copyCurrentPalette(dest, start, num);
-		return;
-	}
-
-	assert(dest);
-	assert(_currentPalette);
-	assert(start + num <= 256);
-	memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
-}
-
-void GfxDefaultDriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
-	// This is only needed for scaling drivers with unscaled hires fonts.
-	error("GfxDefaultDriver::drawTextFontGlyph(): Not implemented");
-}
-
-template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f) {
-	T *dst = &reinterpret_cast<T*>(dest)[start];
-	for (uint i = 0; i < num; ++i) {
-		*dst++ = f.RGBToColor(src[0], src[1], src[2]);
-		src += 3;
-	}
-}
-
-void GfxDefaultDriver::updatePalette(const byte *colors, uint start, uint num) {
-	memcpy(_currentPalette + start * 3, colors, num * 3);
-	if (_pixelSize == 4)
-		updateRGBPalette<uint32>(_internalPalette, colors, start, num, _format);
-	else if (_pixelSize == 2)
-		updateRGBPalette<uint16>(_internalPalette, colors, start, num, _format);
-	else
-		error("GfxDefaultDriver::updatePalette(): Unsupported pixel size %d", _pixelSize);
-}
-
-void GfxDefaultDriver::generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	if (palMods && palModMapping)
-		_colorConvMod(dst, src, pitch, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
-	else
-		_colorConv(dst, src, pitch, w, h, _internalPalette);
-}
-
-SCI0_DOSPreVGADriver::SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering) :
-	GfxDriver(screenW, screenH, numColors), _requestRGBMode(rgbRendering), _colors(nullptr), _compositeBuffer(nullptr), _internalPalette(nullptr) {
-}
-
-SCI0_DOSPreVGADriver::~SCI0_DOSPreVGADriver() {
-	delete[] _compositeBuffer;
-	delete[] _internalPalette;
-}
-
-void SCI0_DOSPreVGADriver::assignPalette(const byte *colors) {
-	_colors = colors;
-}
-
-void SCI0_DOSPreVGADriver::initScreen(const Graphics::PixelFormat*) {
-	Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
-	initGraphics(_screenW, _screenH, _requestRGBMode ? nullptr : &format);
-	format = g_system->getScreenFormat();
-	_pixelSize = format.bytesPerPixel;
-
-	if (_requestRGBMode && _pixelSize == 1)
-		warning("SCI0_DOSPreVGADriver::initScreen(): RGB rendering not available in this ScummVM build");
-
-	delete[] _compositeBuffer;
-	delete[] _internalPalette;
-	_internalPalette = nullptr;
-	_compositeBuffer = nullptr;
-
-	assert(_colors);
-	if (_pixelSize == 1) {
-		g_system->getPaletteManager()->setPalette(_colors, 0, _numColors);
-	} else {
-		byte *rgbpal = new byte[_numColors * _pixelSize]();
-		assert(rgbpal);
-
-		if (_pixelSize == 2)
-			updateRGBPalette<uint16>(rgbpal, _colors, 0, _numColors, format);
-		else if (_pixelSize == 4)
-			updateRGBPalette<uint32>(rgbpal, _colors, 0, _numColors, format);
-		else
-			error("SCI0_DOSPreVGADriver::initScreen(): Unsupported screen format");
-		_internalPalette = rgbpal;
-		CursorMan.replaceCursorPalette(_colors, 0, _numColors);
-	}
-
-	_compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
-	assert(_compositeBuffer);
-
-	setupRenderProc();
-
-	_ready = true;
-}
-
-void SCI0_DOSPreVGADriver::replaceMacCursor(const Graphics::Cursor*) {
-	// This is not needed for SCI0 (and not for any PC version of games at all)
-	error("SCI0_DOSPreVGADriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
-}
-
-void SCI0_DOSPreVGADriver::copyCurrentBitmap(byte*, uint32) const {
-	// This is not needed for SCI0
-	error("SCI0_DOSPreVGADriver::copyCurrentBitmap(): Not implemented");
-}
-
-void SCI0_DOSPreVGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
-	GFXDRV_ASSERT_READY;
-
-	if (_pixelSize == 1) {
-		GfxDriver::copyCurrentPalette(dest, start, num);
-		return;
-	}
-
-	assert(dest);
-	memcpy(dest + start * 3, _colors + start * 3, MIN<int>(num, _numColors) * 3);
-}
-
-void SCI0_DOSPreVGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
-	// This is only needed for scaling drivers with unscaled hires fonts.
-	error("SCI0_DOSPreVGADriver::drawTextFontGlyph(): Not implemented");
-}
-
-SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering) : SCI0_DOSPreVGADriver(4, 320, 200, rgbRendering), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard), _renderLine(nullptr) {
-	static const byte cgaColors[48] = {
-		/*
-		// Canonical CGA palette
-		0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
-		0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
-		0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
-		0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
-		*/
-		// Improved palette model taken from https://int10h.org/blog/2022/06/ibm-5153-color-true-cga-palette/
-		0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0x00, 0x00, 0xC4, 0xC4,
-		0xC4, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0xC4, 0x7E, 0x00, 0xC4, 0xC4, 0xC4,
-		0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0xDC, 0x4E, 0xDC, 0x4E, 0x4E, 0xF3, 0xF3,
-		0xDC, 0x4E, 0x4E, 0xF3, 0x4E, 0xF3, 0xF3, 0xF3, 0x4E, 0xFF, 0xFF, 0xFF
-	};
-
-	static const byte modeColorMap[3][4] = {
-		{ 0, 2, 4, 6 },
-		{ 0, 3, 5, 7 },
-		{ 0, 3, 4, 7 }
-	};
-
-	Common::File drv;
-	if (!drv.open(_driverFile))
-		GFXDRV_ERR_OPEN(_driverFile);
-
-	byte palIndex = 1;
-	byte palIntensity = 1;
-	byte mode = 4;
-
-	byte colMap[4];
-	memset(colMap, 0, sizeof(colMap));
-
-	uint16 eprcOffs = 0;
-
-	uint32 cmd = drv.readUint32LE();
-	if ((cmd & 0xFF) == 0xE9)
-		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
-
-	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
-		GFXDRV_ERR_VERSION(_driverFile);
-
-	drv.skip(drv.readByte() == 0x90 ? 2 : 1);
-
-	uint16 op1st = drv.readUint16LE();
-	int op1len = drv.readUint16LE() - op1st;
-
-	// sanity check
-	assert(op1len > 0 && op1len < 0x100);
-
-	drv.seek(op1st, SEEK_SET);
-	byte *buf = new byte[op1len]();
-	drv.read(buf, op1len);
-
-	// Try figuring out the correct settings...
-	for (int i = 0; i < op1len - 7; ++i) {
-		uint32 cfg = READ_BE_UINT32(buf + i);
-		cmd = READ_BE_UINT32(buf + 4 + i);
-		if ((cmd >> 16) == 0xCD10 && (cfg & 0xff00ff) == 0xB80000) {
-			mode = (cfg >> 8) & 0xff;
-		} else if (cmd == 0xB40BCD10) {
-			if (cfg >> 8 == 0x00B701B3) {
-				palIndex = cfg & 1;
-				palIntensity = (cfg >> 4) & 1;
-			} else if (cfg >> 8 == 0x00B700B3) {
-				colMap[0] = (cfg & 0x0f) + ((cfg & 0x10) >> 1);
-			}
-		}
-	}
-
-	delete[] buf;
-
-	assert(palIndex <= 1);
-	assert(palIntensity <= 1);
-
-	for (int i = 1; i < 4; ++i)
-		colMap[i] = modeColorMap[(!_disableMode5 && mode == 5) ? 2 : palIndex][i] + (palIntensity << 3);
-
-	memset (_palette, 0, sizeof(_palette));
-	for (int i = 0; i < 4; ++i) {
-		for (int ii = 0; ii < 3; ++ii)
-			_palette[i * 3 + ii] = cgaColors[colMap[i] * 3 + ii];
-	}
-
-	assignPalette(_palette);
-
-	_cgaPatterns = new uint16[256]();
-	// The pattern map is always located right before the driver entry point proc.
-	drv.seek(eprcOffs - 512, SEEK_SET);
-	for (int i = 0; i < 256; ++i)
-		_cgaPatterns[i] = drv.readUint16LE();
-
-	drv.close();
-}
-
-SCI0_CGADriver::~SCI0_CGADriver() {
-	delete[] _cgaPatterns;
-}
-
-void SCI0_CGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
-	GFXDRV_ASSERT_READY;
-
-	byte diff = srcX & 1;
-	srcX &= ~1;
-	destX &= ~1;
-	w = (w + diff + 1) & ~1;
-
-	src += (srcY * pitch + srcX);
-
-	byte *dst = _compositeBuffer;
-	int ty = destY;
-
-	for (int i = 0; i < h; ++i) {
-		_renderLine(dst, src, w, srcX & 3, ++ty, _cgaPatterns, _internalPalette);
-		src += pitch;
-	}
-
-	g_system->copyRectToScreen(_compositeBuffer, w * _pixelSize, destX, destY, w, h);
-}
-
-void SCI0_CGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor
-	// that has already been generated by the engine. We simply convert the colors as needed...
-	assert(keycolor == 1);
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	byte *d = _compositeBuffer;
-	for (uint i = w * h; i; --i)
-		*d++ = *s++ & 3;
-
-	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
-}
-
-
-template <typename T> void cgaRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const uint16 *patterns, const byte *pal) {
-	T *d = reinterpret_cast<T*>(dst);
-	const T *p = reinterpret_cast<const T*>(pal);
-	w >>= 1;
-
-	for (int i = 0; i < w; ++i) {
-		uint16 pattern = patterns[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
-		src += 2;
-		byte sh = (ty & 3) << 1;
-		byte lo = ((pattern & 0xff) >> sh) | ((pattern & 0xff) << (8 - sh));
-		byte hi = (pattern >> (8 + sh)) | ((pattern >> 8) << (8 - sh));
-		if (sizeof(T) == 1) {
-			*d++ = (lo >> (6 - (tx << 1))) & 3;
-			*d++ = (hi >> (4 - (tx << 1))) & 3;
-		} else {
-			*d++ = p[(lo >> (6 - (tx << 1))) & 3];
-			*d++ = p[(hi >> (4 - (tx << 1))) & 3];
-		}
-		tx ^= 2;
-	}
-
-	dst = reinterpret_cast<byte*>(d);
-}
-
-void SCI0_CGADriver::setupRenderProc() {
-	static const LineProc lineProcs[] = {
-		&cgaRenderLine<byte>,
-		&cgaRenderLine<uint16>,
-		&cgaRenderLine<uint32>
-	};
-
-	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
-	_renderLine = lineProcs[_pixelSize >> 1];
-}
-
-const char *SCI0_CGADriver::_driverFile = "CGA320C.DRV";
-
-const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
-	Common::File drv;
-	if (!drv.open(drvFile))
-		return nullptr;
-
-	uint16 eprcOffs = 0;
-
-	uint32 cmd = drv.readUint32LE();
-	if ((cmd & 0xFF) == 0xE9)
-		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
-
-	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
-		GFXDRV_ERR_VERSION(drv.getName());
-
-	// This is a safe assumption, as the early version pattern map is 4 times the size of the later one.
-	earlyVersion = (eprcOffs > 0x500);
-	uint16 size = earlyVersion ? 512 : 128;
-
-	byte *result = new byte[size];
-	// For CGA, the pattern map is always located before the entry point dispatcher proc.
-	drv.seek(eprcOffs - size, SEEK_SET);
-	drv.read(result, size);
-
-	// For Hercules there are some extra vars in between, all with initial values
-	// of zero. The last entry of the pattern map is definitely never zero...
-	int xtraOffs = 0;
-	while (result[size - 1 - xtraOffs] == 0)
-		++xtraOffs;
-	if (xtraOffs != 0) {
-		drv.seek(eprcOffs - size - xtraOffs, SEEK_SET);
-		drv.read(result, size);
-	}
-
-	drv.close();
-
-	if (earlyVersion) {
-		uint16 *r = reinterpret_cast<uint16*>(result);
-		for (int i = 0; i < 256; ++i)
-			r[i] = FROM_LE_16(r[i]);
-	}
-
-	return result;
-}
-
-SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering) : SCI0_DOSPreVGADriver(2, 640, 400, rgbRendering), _monochromePatterns(nullptr), _earlyVersion(false), _renderLine(nullptr) {
-	_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
-	_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
-	_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
-	_monochromePalette[5] = monochromeColor & 0xff;
-	assignPalette(_monochromePalette);
-
-	if (!(_monochromePatterns = monochrInit(_driverFiles[0], _earlyVersion)) && !(_monochromePatterns = monochrInit(_driverFiles[1], _earlyVersion)))
-		error("Failed to open '%s' or '%s'", _driverFiles[0], _driverFiles[1]);
-}
-
-SCI0_CGABWDriver::~SCI0_CGABWDriver() {
-	delete[] _monochromePatterns;
-}
-
-void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
-	GFXDRV_ASSERT_READY;
-
-	byte *dst = _compositeBuffer;
-	int ty = destY & 7;
-
-	if (_earlyVersion) {
-		++ty;
-		byte diff = srcX & 1;
-		srcX &= ~1;
-		destX &= ~1;
-		w = (w + diff + 1) & ~1;
-	}
-
-	src += (srcY * pitch + srcX);
-
-	for (int i = 0; i < h; ++i) {
-		_renderLine(dst, src, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
-		ty = (ty + 1) & 7;
-		src += pitch;
-	}
-
-	g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, destX << 1, destY << 1, w << 1, h << 1);
-}
-
-void SCI0_CGABWDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
-	// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
-	assert(keycolor == 1);
-	keycolor = 0x0f;
-	w <<= 1;
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	byte *d0 = _compositeBuffer;
-	byte *d1 = _compositeBuffer + w;
-
-	for (uint i = 0; i < h; ++i) {
-		for (uint ii = 0; ii < w; ++ii) {
-			*d0++ = *d1++ = *s ? (*s ^ 0x0e) : 0;
-			if (ii & 1)
-				++s;
-		}
-		d0 += w;
-		d1 += w;
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
-}
-
-Common::Point SCI0_CGABWDriver::getMousePos() const {
-	Common::Point res = GfxDriver::getMousePos();
-	res.x >>= 1;
-	res.y >>= 1;
-	return res;
-}
-
-void SCI0_CGABWDriver::setMousePos(const Common::Point &pos) const {
-	g_system->warpMouse(pos.x << 1, pos.y << 1);
-}
-
-void SCI0_CGABWDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
-	g_system->setShakePos(shakeXOffset << 1, shakeYOffset << 1);
-}
-
-void SCI0_CGABWDriver::clearRect(const Common::Rect &r) const {
-	Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
-	GfxDriver::clearRect(r2);
-}
-
-Common::Point SCI0_CGABWDriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point(pos.x << 1, pos.y << 1);
-}
-
-template <typename T> void cgabwRenderLine_v1(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
-	const T *p = reinterpret_cast<const T*>(pal);
-	const uint16 *patterns16 = reinterpret_cast<const uint16*>(patterns);
-	T *d1 = reinterpret_cast<T*>(dst);
-	T *d2 = d1 + (w << 1);
-	w >>= 1;
-
-	for (int i = 0; i < w; ++i) {
-		uint16 pt = patterns16[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
-		src += 2;
-		byte sh = (ty & 3) << 1;
-		byte lo = ((pt & 0xff) >> sh) | ((pt & 0xff) << (8 - sh));
-		byte hi = (pt >> (8 + sh)) | ((pt >> 8) << (8 - sh));
-		if (sizeof(T) == 1) {
-			*d1++ = *d2++ = ((lo >> (6 - (tx << 1))) >> 1) & 1;
-			*d1++ = *d2++ = (lo >> (6 - (tx << 1))) & 1;
-			*d1++ = *d2++ = ((hi >> (4 - (tx << 1))) >> 1) & 1;
-			*d1++ = *d2++ = (hi >> (4 - (tx << 1))) & 1;
-		} else {
-			*d1++ = *d2++ = p[((lo >> (6 - (tx << 1))) >> 1) & 1];
-			*d1++ = *d2++ = p[(lo >> (6 - (tx << 1))) & 1];
-			*d1++ = *d2++ = p[((hi >> (4 - (tx << 1))) >> 1) & 1];
-			*d1++ = *d2++ = p[(hi >> (4 - (tx << 1))) & 1];
-		}
-		tx ^= 2;
-	}
-
-	dst = reinterpret_cast<byte*>(d2);
-}
-
-template <typename T> void cgabwRenderLine_v2(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
-	const T *p = reinterpret_cast<const T*>(pal);
-	T *d1 = reinterpret_cast<T*>(dst);
-	T *d2 = d1 + (w << 1);
-
-	for (int i = 0; i < w; ++i) {
-		byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
-		if (sizeof(T) == 1) {
-			*d1++ = *d2++ = (pt >> 1) & 1;
-			*d1++ = *d2++ = pt & 1;
-		} else {
-			*d1++ = *d2++ = p[(pt >> 1) & 1];
-			*d1++ = *d2++ = p[pt & 1];
-		}
-		tx = (tx + 1) & 3;
-	}
-
-	dst = reinterpret_cast<byte*>(d2);
-}
-
-void SCI0_CGABWDriver::setupRenderProc() {
-	static const LineProc lineProcs[] = {
-		&cgabwRenderLine_v1<byte>,
-		&cgabwRenderLine_v1<uint16>,
-		&cgabwRenderLine_v1<uint32>,
-		&cgabwRenderLine_v2<byte>,
-		&cgabwRenderLine_v2<uint16>,
-		&cgabwRenderLine_v2<uint32>
-	};
-
-	int t = _pixelSize >> 1;
-	if (!_earlyVersion)
-		t += 3;
-
-	assert(t < ARRAYSIZE(lineProcs));
-	_renderLine = lineProcs[t];
-}
-
-const char *SCI0_CGABWDriver::_driverFiles[2] = { "CGA320BW.DRV", "CGA320M.DRV" };
-
-SCI0_HerculesDriver::SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage) : SCI0_DOSPreVGADriver(2, cropImage ? 640 : 720, cropImage ? 300 : 350, rgbRendering),
-	_centerX(cropImage ? 0 : 40), _centerY(cropImage ? 0 : 25), _monochromePatterns(nullptr), _renderLine(nullptr) {
-	_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
-	_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
-	_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
-	_monochromePalette[5] = monochromeColor & 0xff;
-	assignPalette(_monochromePalette);
-	bool unused = false;
-
-	if (!(_monochromePatterns = monochrInit(_driverFile, unused)))
-		GFXDRV_ERR_OPEN(_driverFile);
-}
-
-SCI0_HerculesDriver::~SCI0_HerculesDriver() {
-	delete[] _monochromePatterns;
-}
-
-void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
-	GFXDRV_ASSERT_READY;
-
-	byte *dst = _compositeBuffer;
-	byte sw = destY & 1;
-	src += (srcY * pitch + srcX);
-	destY = (destY & ~1) * 3 / 2 + (destY & 1);
-	int ty = destY & 7;
-	int rh = 0;
-
-	for (int i = 0; i < h; ++i) {
-		const byte *src2 = src;
-		_renderLine(dst, src2, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
-		ty = (ty + 1) & 7;
-		++rh;
-
-		if (sw & 1)
-			sw ^= 2;
-
-		if (sw != 3) {
-			src += pitch;
-			sw ^= 1;
-		} else {
-			--i;
-		}
-	}
-
-	g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, (destX << 1) + _centerX, destY + _centerY, w << 1, rh);
-}
-
-void SCI0_HerculesDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
-	// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
-	assert(keycolor == 1);
-	keycolor = 0x0f;
-	int alt = 0;
-	const byte *s = reinterpret_cast<const byte *>(cursor);
-	byte *d = _compositeBuffer;
-
-	for (uint i = 0; i < h; ++i) {
-		for (uint ii = 0; ii < (w << 1); ++ii) {
-			*d++ = *s ? (*s ^ 0x0e) : 0;
-			if (ii & 1)
-				++s;
-		}
-		if (i & 1) {
-			alt ^= 1;
-			if (alt) {
-				s -= w;
-				--i;
-			}
-		}
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w << 1, (h & ~1) * 3 / 2 + (h & 1), hotspotX << 1, (hotspotY & ~1) * 3 / 2 + (hotspotY & 1), keycolor);
-}
-
-Common::Point SCI0_HerculesDriver::getMousePos() const {
-	Common::Point res = GfxDriver::getMousePos();
-	res.x = CLIP<int>(res.x - _centerX, 0, 639) >> 1;
-	res.y = (CLIP<int>(res.y - _centerY, 0, 299) * 2 + 1) / 3;
-	return res;
-}
-
-void SCI0_HerculesDriver::setMousePos(const Common::Point &pos) const {
-	g_system->warpMouse((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
-}
-
-void SCI0_HerculesDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
-	g_system->setShakePos(shakeXOffset << 1, (shakeYOffset & ~1) * 3 / 2 + (shakeYOffset & 1));
-}
-
-void SCI0_HerculesDriver::clearRect(const Common::Rect &r) const {
-	Common::Rect r2((r.left << 1) + _centerX, (r.top & ~1) * 3 / 2 + (r.top & 1) + _centerY, (r.right << 1) + 40, (r.bottom & ~1) * 3 / 2 + (r.bottom & 1) + _centerY);
-	GfxDriver::clearRect(r2);
-}
-
-Common::Point SCI0_HerculesDriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
-}
-
-template <typename T> void herculesRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
-	T *d = reinterpret_cast<T*>(dst);
-	const T *p = reinterpret_cast<const T*>(pal);
-
-	for (int i = 0; i < w; ++i) {
-		byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
-		if (sizeof(T) == 1) {
-			*d++ = (pt >> 1) & 1;
-			*d++ = pt & 1;
-		} else {
-			*d++ = p[(pt >> 1) & 1];
-			*d++ = p[pt & 1];
-		}
-		tx = (tx + 1) & 3;
-	}
-
-	dst = reinterpret_cast<byte*>(d);
-}
-
-void SCI0_HerculesDriver::setupRenderProc() {
-	static const LineProc lineProcs[] = {
-		&herculesRenderLine<byte>,
-		&herculesRenderLine<uint16>,
-		&herculesRenderLine<uint32>
-	};
-
-	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
-	_renderLine = lineProcs[_pixelSize >> 1];
-}
-
-const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
-
-
-SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, false, rgbRendering), _greyScalePalette(nullptr) {
-	_greyScalePalette = new byte[_numColors * 3]();
-}
-
-SCI1_VGAGreyScaleDriver::~SCI1_VGAGreyScaleDriver() {
-	delete[] _greyScalePalette;
-}
-
-void SCI1_VGAGreyScaleDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	byte *d = _greyScalePalette;
-	for (uint i = 0; i < num; ++i) {
-		// In the driver files I inspected there were never any other color distributions than this.
-		// So I guess it is safe to hardcode that instead of loading it from the driver file.
-		d[0] = d[1] = d[2] = (colors[0] * 77 + colors[1] * 150 + colors[2] * 28) >> 8;
-		colors += 3;
-		d += 3;
-	}
-
-	GfxDefaultDriver::setPalette(_greyScalePalette, start, num, update, palMods, palModMapping);
-}
-
-const char *SCI1_VGAGreyScaleDriver::_driverFile = "VGA320BW.DRV";
-
-SCI1_EGADriver::SCI1_EGADriver(bool rgbRendering) : GfxDriver(320, 200, 256), _requestRGBMode(rgbRendering), _egaColorPatterns(nullptr), _egaMatchTable(nullptr),
-	_currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _renderLine(nullptr), _vScaleMult(2), _vScaleDiv(1) {
-	static const byte egaColors[48] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
-		0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
-		0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
-		0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
-	};
-	_convPalette = egaColors;
-}
-
-SCI1_EGADriver::~SCI1_EGADriver() {
-	delete[] _egaMatchTable;
-	delete[] _egaColorPatterns;
-	delete[] _compositeBuffer;
-	delete[] _currentBitmap;
-	delete[] _currentPalette;
-	delete[] _internalPalette;
-}
-
-template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool) {
-	const T *p = reinterpret_cast<const T*>(pal);
-	T *d1 = reinterpret_cast<T*>(dst);
-	T *d2 = d1 + (w << 1);
-
-	for (int i = 0; i < w; ++i) {
-		byte pt = patterns[*src++];
-		if (sizeof(T) == 1) {
-			*d1++ = *d2++ = pt >> 4;
-			*d1++ = *d2++ = pt & 0x0f;
-		} else {
-			*d1++ = *d2++ = p[pt >> 4];
-			*d1++ = *d2++ = p[pt & 0x0f];
-		}
-	}
-	dst = reinterpret_cast<byte*>(d2);
-}
-
-void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
-	if (!_ready)
-		loadData();
-
-	Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
-	initGraphics(_screenW << 1, _screenH * _vScaleMult / _vScaleDiv, _requestRGBMode ? nullptr : &format);
-	format = g_system->getScreenFormat();
-	_pixelSize = format.bytesPerPixel;
-
-	if (_requestRGBMode && _pixelSize == 1)
-		warning("SCI1_EGADriver::initScreen(): RGB rendering not available in this ScummVM build");
-
-	delete[] _egaColorPatterns;
-	delete[] _compositeBuffer;
-	delete[] _currentBitmap;
-	delete[] _currentPalette;
-	delete[] _internalPalette;
-	_internalPalette = nullptr;
-	_egaColorPatterns = _compositeBuffer = _currentBitmap = _currentPalette = nullptr;
-
-	if (_pixelSize == 1) {
-		g_system->getPaletteManager()->setPalette(_convPalette, 0, 16);
-	} else {
-		byte *rgbpal = new byte[_numColors * _pixelSize]();
-		assert(rgbpal);
-
-		if (_pixelSize == 2)
-			updateRGBPalette<uint16>(rgbpal, _convPalette, 0, 16, format);
-		else if (_pixelSize == 4)
-			updateRGBPalette<uint32>(rgbpal, _convPalette, 0, 16, format);
-		else
-			error("SCI1_EGADriver::initScreen(): Unsupported screen format");
-		_internalPalette = rgbpal;
-		CursorMan.replaceCursorPalette(_convPalette, 0, 16);
-	}
-
-	_compositeBuffer = new byte[(_screenW << 1) * (_screenH * _vScaleMult / _vScaleDiv) * _pixelSize]();
-	assert(_compositeBuffer);
-	_currentBitmap = new byte[_screenW * _screenH]();
-	assert(_currentBitmap);
-	_currentPalette = new byte[256 * 3]();
-	assert(_currentPalette);
-	_egaColorPatterns = new byte[256]();
-	assert(_egaColorPatterns);
-
-	static const LineProc lineProcs[] = {
-		&ega640RenderLine<byte>,
-		&ega640RenderLine<uint16>,
-		&ega640RenderLine<uint32>
-	};
-
-	assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
-	_renderLine = lineProcs[_pixelSize >> 1];
-
-	_ready = true;
-}
-
-void SCI1_EGADriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) {
-	GFXDRV_ASSERT_READY;
-	memcpy(_currentPalette + start * 3, colors, num * 3);
-	byte *d = &_egaColorPatterns[start];
-	for (uint i = 0; i < num; ++i) {
-		*d++ = _egaMatchTable[((MIN<byte>((colors[0] >> 2) + _colAdjust, 63) & 0x38) << 3) | (MIN<byte>((colors[1] >> 2) + _colAdjust, 63) & 0x38) | (MIN<byte>((colors[2] >> 2) + _colAdjust, 63) >> 3)];
-		colors += 3;
-	}
-	if (update)
-		copyRectToScreen(_currentBitmap, 0, 0, _screenW, 0, 0, _screenW, _screenH, nullptr, nullptr);
-}
-
-void SCI1_EGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
-	GFXDRV_ASSERT_READY;
-	assert (h >= 0 && w >= 0);
-
-	src += (srcY * pitch + srcX);
-
-	if (src != _currentBitmap)
-		updateBitmapBuffer(_currentBitmap, _screenW, src, pitch, destX, destY, w, h);
-
-	uint16 realWidth, realHeight;
-	renderBitmap(_compositeBuffer, src, pitch, destY, w, h, _egaColorPatterns, _internalPalette, realWidth, realHeight);
-
-	Common::Point pos(destX, destY);
-	pos = getRealCoords(pos);
-
-	g_system->copyRectToScreen(_compositeBuffer, realWidth * _pixelSize, pos.x, pos.y, realWidth, realHeight);
-}
-
-void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	int dstPitch = (w << 1);
-	byte *d1 = _compositeBuffer;
-	byte *d2 = _compositeBuffer + dstPitch;
-	uint32 newKeyColor = 0xFF;
-
-	for (uint i = 0; i < h; ++i) {
-		for (uint ii = 0; ii < w; ++ii) {
-			byte col = *s++;
-			if (col == keycolor) {
-				*d1++ = *d2++ = newKeyColor;
-				*d1++ = *d2++ = newKeyColor;
-			} else {
-				byte pt = _egaColorPatterns[col];
-				*d1++ = *d2++ = pt >> 4;
-				*d1++ = *d2++ = pt & 0x0f;
-			}
-		}
-		d1 += dstPitch;
-		d2 += dstPitch;
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
-}
-
-void SCI1_EGADriver::copyCurrentBitmap(byte *dest, uint32 size) const {
-	GFXDRV_ASSERT_READY;
-	assert(dest);
-	assert(size <= (uint32)(_screenW * _screenH));
-	memcpy(dest, _currentBitmap, size);
-}
-
-void SCI1_EGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
-	GFXDRV_ASSERT_READY;
-	assert(dest);
-	assert(start + num <= 256);
-	memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
-}
-
-void SCI1_EGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
-	// This is only needed for scaling drivers with unscaled hires fonts.
-	error("SCI1_EGADriver::drawTextFontGlyph(): Not implemented");
-}
-
-Common::Point SCI1_EGADriver::getMousePos() const {
-	Common::Point res = GfxDriver::getMousePos();
-	res.x >>= 1;
-	res.y = res.y * _vScaleDiv / _vScaleMult;
-	return res;
-}
-
-void SCI1_EGADriver::setMousePos(const Common::Point &pos) const {
-	g_system->warpMouse(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
-}
-
-void SCI1_EGADriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
-	g_system->setShakePos(shakeXOffset << 1, shakeYOffset * _vScaleMult / _vScaleDiv);
-}
-
-void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
-	Common::Rect r2(r.left << 1, r.top * _vScaleMult / _vScaleDiv, r.right << 1, r.bottom * _vScaleMult / _vScaleDiv);
-	GfxDriver::clearRect(r2);
-}
-
-Common::Point SCI1_EGADriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
-}
-
-void SCI1_EGADriver::loadData() {
-	Common::File drv;
-	if (!drv.open(_driverFile))
-		GFXDRV_ERR_OPEN(_driverFile);
-
-	uint16 eprcOffs = 0;
-
-	uint32 cmd = drv.readUint32LE();
-	if ((cmd & 0xFF) == 0xE9)
-		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
-
-	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR) || drv.readUint32LE() != 0xFEDCBA98 || !drv.skip(4))
-		GFXDRV_ERR_VERSION(_driverFile);
-
-	uint32 pos = (drv.pos() + 1) & ~1;
-
-	drv.seek(pos);
-	drv.seek(drv.readUint16LE());
-	uint32 colResponse = drv.readUint32LE();
-	_numColors = (colResponse >> 8) & 0xffff;
-	if (_numColors < 16 || _numColors > 256 || (colResponse & 0xff0000ff) != 0xC30000B8)
-		error("SCI1_EGADriver: Failed to retrieve color info from '%s'", _driverFile);
-
-	drv.seek(pos + 20);
-	drv.seek(drv.readUint16LE());
-	byte *buff = new byte[128];
-	drv.read(buff, 128);
-
-	uint16 tableOffs = 0;
-	for (int i = 0; i < 120 && !tableOffs; ++i) {
-		uint32 c = READ_BE_UINT32(buff + i);
-		if (c == 0x8BD82E8A) {
-			if (buff[i + 4] == 0x87)
-				tableOffs = READ_LE_UINT16(buff + i + 5);
-		} else if (c == 0xD0E8D0E8) {
-			for (int ii = 4; ii < 14; ++ii) {
-				if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
-					c = READ_BE_UINT32(buff + i + ii + 2);
-					if ((c & 0xFFFFFF) == 0x83F83F)
-						_colAdjust = c >> 24;
-				}
-			}
-		}
-	}
-	delete[] buff;
-
-	if (!tableOffs)
-		error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
-
-	drv.seek(tableOffs);
-	byte *table = new byte[512]();
-	drv.read(table, 512);
-	_egaMatchTable = table;
-
-	if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
-		GFXDRV_ERR_VERSION(_driverFile);
-
-	drv.close();
-}
-
-void SCI1_EGADriver::renderBitmap(byte *dst, const byte *src, int pitch, int, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
-	for (int i = 0; i < h; ++i) {
-		_renderLine(dst, src, w, patterns, palette, 0);
-		src += pitch;
-	}
-	realWidth = w << 1;
-	realHeight = h << 1;
-}
-
-const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
-
-template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h) {
-	const T *s = reinterpret_cast<const T*>(src);
-	int dstPitch = pitch << 1;
-	T *d1 = reinterpret_cast<T*>(dst);
-	T *d2 = d1 + dstPitch;
-	pitch -= w;
-	dstPitch += (pitch << 1);
-
-	while (h--) {
-		for (int i = 0; i < w; ++i) {
-			d1[0] = d1[1] = d2[0] = d2[1] = *s++;
-			d1 += 2;
-			d2 += 2;
-		}
-		s += pitch;
-		d1 += dstPitch;
-		d2 += dstPitch;
-	}
-}
-
-UpscaledGfxDriver::UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering) :
-	UpscaledGfxDriver(640, 400, textAlignX, scaleCursor, rgbRendering) {
-}
-
-UpscaledGfxDriver::UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering) :
-	GfxDefaultDriver(scaledW, scaledH, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
-	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _cursorWidth(0), _cursorHeight(0), _hScaleMult(2), _vScaleMult(2), _vScaleDiv(1) {
-	_virtualW = 320;
-	_virtualH = 200;
-}
-
-UpscaledGfxDriver::~UpscaledGfxDriver() {
-	delete[] _scaledBitmap;
-}
-
-void renderGlyph(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
-	dstPitch -= w;
-	srcPitch -= w;
-
-	while (h--) {
-		for (int i = 0; i < w; ++i) {
-			byte in = *src++;
-			if (in != transpCol)
-				*dst = in;
-			++dst;
-		}
-		src += srcPitch;
-		dst += dstPitch;
-	}
-}
-
-void UpscaledGfxDriver::initScreen(const Graphics::PixelFormat *format) {
-	GfxDefaultDriver::initScreen(format);
-	_scaledBitmap = new byte[_screenW * _screenH * _srcPixelSize]();
-
-	static const ScaledRenderProc scaledRenderProcs[] = {
-		&scale2x<byte>,
-		&scale2x<uint16>,
-		&scale2x<uint32>
-	};
-	assert((_srcPixelSize >> 1) < ARRAYSIZE(scaledRenderProcs));
-	_renderScaled = scaledRenderProcs[_srcPixelSize >> 1];
-	_renderGlyph = &renderGlyph;
-}
-
-void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	if (_pixelSize == 1) {
-		GfxDefaultDriver::setPalette(colors, start, num, update, palMods, palModMapping);
-		return;
-	}
-	updatePalette(colors, start, num);
-	if (update)
-		updateScreen(0, 0, _screenW, _screenH, palMods, palModMapping);
-	if (_cursorUsesScreenPalette)
-		CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
-}
-
-void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	assert (h >= 0 && w >= 0);
-
-	src += (srcY * pitch + srcX * _srcPixelSize);
-	if (src != _currentBitmap)
-		updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
-
-	int realWidth = 0;
-	int realHeight = 0;
-
-	// We need to scale and color convert the bitmap in separate functions, because we want
-	// to keep the scaled non-color-modified bitmap for palette updates in rgb rendering mode.
-	renderBitmap(src, pitch, destX, destY, w, h, realWidth, realHeight);
-
-	Common::Point p(destX, destY);
-	p = getRealCoords(p);
-
-	updateScreen(p.x, p.y, realWidth, realHeight, palMods, palModMapping);
-}
-
-void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	if (_scaleCursor) {
-		adjustCursorBuffer(w << 1, h << 1);
-		scale2x<byte>(_compositeBuffer, reinterpret_cast<const byte*>(cursor), w, w, h);
-		CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
-	} else {
-		CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
-	}
-}
-
-Common::Point UpscaledGfxDriver::getMousePos() const {
-	Common::Point res = GfxDriver::getMousePos();
-	res.x /= _hScaleMult;
-	res.y = res.y * _vScaleDiv / _vScaleMult;
-	return res;
-}
-
-void UpscaledGfxDriver::setMousePos(const Common::Point &pos) const {
-	g_system->warpMouse(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
-}
-
-void UpscaledGfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
-	g_system->setShakePos(shakeXOffset * _hScaleMult, shakeYOffset * _vScaleMult / _vScaleDiv);
-}
-
-void UpscaledGfxDriver::clearRect(const Common::Rect &r) const {
-	Common::Rect r2(r.left * _hScaleMult, r.top * _vScaleMult / _vScaleDiv, r.right * _hScaleMult, r.bottom * _vScaleMult / _vScaleDiv);
-	GfxDriver::clearRect(r2);
-}
-
-Common::Point UpscaledGfxDriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
-}
-
-void UpscaledGfxDriver::drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	hiresDestX &= ~(_textAlignX - 1);
-	byte *scb = _scaledBitmap + hiresDestY * _screenW * _srcPixelSize + hiresDestX * _srcPixelSize;
-	_renderGlyph(scb, _screenW, src, pitch, hiresW, hiresH, transpColor);
-	updateScreen(hiresDestX, hiresDestY, hiresW, hiresH, palMods, palModMapping);
-}
-
-void UpscaledGfxDriver::updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	byte *buff = _compositeBuffer;
-	int pitch = w * _pixelSize;
-	byte *scb = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
-	if (palMods && palModMapping) {
-		_colorConvMod(buff, scb, _screenW, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
-	} else if (_pixelSize != _srcPixelSize) {
-		_colorConv(buff, scb, _screenW, w, h, _internalPalette);
-	} else {
-		buff = scb;
-		pitch = _screenW *_pixelSize;
-	}
-
-	g_system->copyRectToScreen(buff, pitch, destX, destY, w, h);
-}
-
-void UpscaledGfxDriver::adjustCursorBuffer(uint16 newWidth, uint16 newHeight) {
-	// For configs which need/have the composite buffer for other purposes, we can skip this.
-	if (!_compositeBuffer)
-		_needCursorBuffer = true;
-	else if (!_needCursorBuffer)
-		return;
-
-	if (_cursorWidth * _cursorHeight < newWidth * newHeight) {
-		delete[] _compositeBuffer;
-		_compositeBuffer = new byte[newWidth * newHeight * _srcPixelSize]();
-		_cursorWidth = newWidth;
-		_cursorHeight = newHeight;
-	}
-}
-
-void UpscaledGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
-	byte *scb = _scaledBitmap + (dy << 1) * _screenW * _srcPixelSize + (dx << 1) * _srcPixelSize;
-	_renderScaled(scb, src, pitch, w, h);
-	realWidth = w << 1;
-	realHeight = h << 1;
-}
-
-WindowsGfx256ColorsDriver::WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow,bool rgbRendering) :
-	UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, coloredDosStyleCursors && !smallWindow, rgbRendering), _dosStyleCursors(coloredDosStyleCursors), _smallWindow(smallWindow),
-		_renderLine(nullptr), _renderLine2(nullptr), _flags(0), _colorMap(nullptr), _vScaleMult2(smallWindow ? 1 : 2) {
-	_virtualW = 320;
-	_virtualH = 200;
-	if (smallWindow)
-		_hScaleMult = 1;
-	_vScaleMult = _smallWindow ? 6 : 11;
-	_vScaleDiv = 5;
-}
-
-void largeWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
-	int dstPitch = pitch;
-	int dstPitch2 = pitch - (w << 1);
-	byte *d1 = dst;
-	byte *d2 = d1 + dstPitch;
-
-	if (ty == 5) {
-		byte *d3 = d2 + dstPitch;
-		for (int i = 0; i < w; ++i) {
-			d1[0] = d1[1] = d2[0] = d2[1] = d3[0] = d3[1] = *src++;
-			d1 += 2;
-			d2 += 2;
-			d3 += 2;
-		}
-		dst = d3 + dstPitch2;
-	} else {
-		for (int i = 0; i < w; ++i) {
-			d1[0] = d1[1] = d2[0] = d2[1] = *src++;
-			d1 += 2;
-			d2 += 2;
-		}
-		dst = d2 + dstPitch2;
-	}
-}
-
-void largeWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
-	int dstPitch = pitch;
-	int dstPitch2 = pitch - (w << 1);
-	byte *d1 = dst;
-	byte *d2 = d1 + dstPitch;
-
-	for (int i = 0; i < w; ++i) {
-		d1[0] = d1[1] = d2[0] = d2[1] = *src++;
-		d1 += 2;
-		d2 += 2;
-	}
-	dst = d2 + dstPitch2;
-}
-
-void smallWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
-	int dstPitch = pitch;
-	int dstPitch2 = pitch - w;
-	byte *d1 = dst;
-
-	if (ty == 5) {
-		byte *d2 = d1 + dstPitch;
-		for (int i = 0; i < w; ++i)
-			*d1++ = *d2++ = *src++;
-		dst = d2 + dstPitch2;
-	} else {
-		for (int i = 0; i < w; ++i)
-			*d1++ = *src++;
-		dst = d1 + dstPitch2;
-	}
-}
-
-void smallWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
-	int dstPitch = pitch - w;
-	byte *d1 = dst;
-
-	for (int i = 0; i < w; ++i)
-		*d1++ = *src++;
-	dst = d1 + dstPitch;
-}
-
-void hiresRenderLine(byte *&dst, const byte *src, int pitch, int w, const byte *colorMap) {
-	if (!colorMap) {
-		memcpy(dst, src, w);
-	} else {
-		byte *d = dst;
-		for (int i = 0; i < w; ++i)
-			*d++ = colorMap[*src++];
-	}
-	dst += pitch;
-}
-
-void renderLineDummy(byte *&, const byte* , int, int, const byte*) {
-}
-
-void WindowsGfx256ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
-	UpscaledGfxDriver::initScreen(format);
-	_renderLine = _smallWindow ? &smallWindowRenderLine : &largeWindowRenderLine;
-	_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
-}
-
-void WindowsGfx256ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	assert (h >= 0 && w >= 0);
-
-	if (!(_flags & (kHiResMode | kMovieMode))) {
-		UpscaledGfxDriver::copyRectToScreen(src, srcX, srcY, pitch, destX, destY, w, h, palMods, palModMapping);
-		return;
-	}
-
-	if (_flags & kMovieMode) {
-		destX = (_screenW >> 1) - (w & ~1) * _hScaleMult / 2;
-		destY = (_screenH >> 1) - (h & ~1) * _vScaleMult2 / 2;
-	}
-
-	src += (srcY * pitch + srcX * _srcPixelSize);
-	byte *dst = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
-
-	for (int i = 0; i < h; ++i) {
-		_renderLine2(dst, src, _screenW, w, _colorMap);
-		src += pitch;
-	}
-
-	if (_flags & kMovieMode) {
-		w *= _hScaleMult;
-		h *= _vScaleMult2;
-	}
-
-	updateScreen(destX, destY, w, h, palMods, palModMapping);
-}
-
-byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors) {
-	byte color[3];
-	for (int i = 2; i >= 0; --i) {
-		color[i] = rgbTriplet & 0xFF;
-		rgbTriplet >>= 8;
-	}
-	int min = 65025;
-	byte match = 0;
-	for (int i = 0; i < numColors && min; ++i) {
-		const byte *rgb = &palette[i * 3];
-		int t = (color[0] - rgb[0]) * (color[0] - rgb[0]) + (color[1] - rgb[1]) * (color[1] - rgb[1]) + (color[2] - rgb[2]) * (color[2] - rgb[2]);
-		if (t < min) {
-			min = t;
-			match = i;
-		}
-	}
-	return match;
-}
-
-void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor, bool noScale) {
-	const byte *s = reinterpret_cast<const byte*>(src);
-	uint16 min = 65025;
-	uint16 max = 0;
-
-	byte newKeyColor = 0;
-	while (newKeyColor == blackColor || newKeyColor == whiteColor)
-		++newKeyColor;
-
-	for (uint i = 0; i < w * h; ++i) {
-		byte col = *s++;
-		if (col == keycolor)
-			continue;
-		const byte *rgb = &palette[col * 3];
-		uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
-		if (t > max)
-			max = t;
-		if (t < min)
-			min = t;
-	}
-
-#if 0
-	// The original interpreter will accidentally let the value overflow like this,
-	// making most cursors completely white. I have fixed it.
-	uint16 med = (uint16)(min + max) >> 1;
-#else
-	uint16 med = (min + max) >> 1;
-#endif
-	uint16 lim1 = max - (max - min) / 3;
-	uint16 lim2 = min + max - lim1;
-	s = reinterpret_cast<const byte*>(src);
-
-	if (w < 17 && h < 17 && !noScale) {
-		// Small cursors (like the insignia ring in KQ6) get scaled and dithered.
-		byte *dst2 = dst + (w << 1);
-		for (uint i = 0; i < h; ++i) {
-			for (uint ii = 0; ii < w; ++ii) {
-				byte col = *s++;
-				if (col == keycolor) {
-					*dst++ = *dst2++ = newKeyColor;
-					*dst++ = *dst2++ = newKeyColor;
-					continue;
-				}
-				const byte *rgb = &palette[col * 3];
-				uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
-
-				dst[0] = dst2[1] = t > lim2 ? whiteColor : blackColor;
-				dst2[0] = dst[1] = t > lim1 ? whiteColor : blackColor;
-				dst += 2;
-				dst2 += 2;
-			};
-			dst	+= (w << 1);
-			dst2 += (w << 1);
-		}
-		w <<= 1;
-		h <<= 1;
-		hotX <<= 1;
-		hotY <<= 1;
-	} else {
-		for (uint i = 0; i < w * h; ++i) {
-			byte col = *s++;
-			if (col == keycolor) {
-				*dst++ = newKeyColor;
-				continue;
-			}
-			const byte *rgb = &palette[col * 3];
-			uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
-			*dst++ = t > med ? whiteColor : blackColor;
-		}
-	}
-	keycolor = newKeyColor;
-}
-
-void WindowsGfx256ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	if (_dosStyleCursors) {
-		// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
-		// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
-		// It is a technical limitation on Windows 95 with VGA hardware that mouse cursors have to be b/w.
-		// Instead, we use the colored DOS style cursors as a default, since there was consensus to do that.
-		UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
-		return;
-	}
-	adjustCursorBuffer(w << 1, h << 1);
-
-	if (_pixelSize == 1)
-		copyCurrentPalette(_currentPalette, 0, _numColors);
-
-	byte col1 = findColorInPalette(0x00000000, _currentPalette, _numColors);
-	byte col2 = findColorInPalette(0x00FFFFFF, _currentPalette, _numColors);
-	renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, _smallWindow);
-	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
-}
-
-Common::Point WindowsGfx256ColorsDriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult2 + (pos.y + 4) / 5);
-}
-
-void WindowsGfx256ColorsDriver::setFlags(uint32 flags) {
-	flags ^= (_flags & flags);
-	if (!flags)
-		return;
-
-	if (flags & kMovieMode)
-		_renderLine2 = _smallWindow ? &smallWindowRenderLineMovie : &largeWindowRenderLineMovie;
-
-	_flags |= flags;
-}
-
-void WindowsGfx256ColorsDriver::clearFlags(uint32 flags) {
-	flags &= _flags;
-	if (!flags)
-		return;
-
-	if (flags & kMovieMode)
-		_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
-
-	_flags &= ~flags;
-}
-
-void WindowsGfx256ColorsDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
-	assert(_renderLine);
-
-	byte *dst = _scaledBitmap + (dy * _vScaleMult2 + (dy + 4) / 5) * _screenW * _srcPixelSize + dx *_hScaleMult * _srcPixelSize;
-	const byte *dstart = dst;
-	dy = (dy + 4) % 5;
-
-	while (h--) {
-		_renderLine(dst, src, _screenW, w, ++dy);
-		dy %= 5;
-		src += pitch;
-	}
-
-	realWidth = w * _hScaleMult;
-	realHeight = (dst - dstart) / _screenW;
-}
-
-WindowsGfx16ColorsDriver::WindowsGfx16ColorsDriver(bool enhancedDithering, bool rgbRendering) : SCI1_EGADriver(rgbRendering), _enhancedDithering(enhancedDithering), _renderLine2(nullptr) {
-	static const byte win16Colors[48] = {
-		0x00, 0x00, 0x00, 0xA8, 0x00, 0x57, 0x00, 0xA8, 0x57, 0xA8, 0xA8, 0x57,
-		0x00, 0x00, 0xA8, 0xA8, 0x57, 0xA8, 0x57, 0xA8, 0xA8, 0x87, 0x88, 0x8F,
-		0xC0, 0xC7, 0xC8, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
-		0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-	};
-
-	_convPalette = win16Colors;
-	_vScaleMult = 11;
-	_vScaleDiv = 5;
-}
-
-template <typename T, bool extScale> void win16ColRenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool swap) {
-	const T *p = reinterpret_cast<const T*>(pal);
-	T *d1 = reinterpret_cast<T*>(dst);
-	T *d2 = d1 + (w << 1);
-	T *d3 = d2 + (w << 1);
-	T *&d3r = swap ? d2 : d1;
-
-	if (swap)
-		SWAP(d1, d2);
-
-	for (int i = 0; i < w; ++i) {
-		byte pt = patterns[*src++];
-		if (sizeof(T) == 1) {
-			*d1++ = d2[1] = pt & 0x0F;
-			*d1++ = *d2++ = pt >> 4;
-		} else {
-			*d1++ = d2[1] = p[pt & 0x0F];
-			*d1++ = *d2++ = p[pt >> 4];
-		}
-		d2++;
-
-		if (extScale) {
-			*d3++ = *(d3r - 2);
-			*d3++ = *(d3r - 1);
-		}
-	}
-
-	dst = reinterpret_cast<byte*>(extScale ? d3 : (swap ? d1 : d2));
-}
-
-void WindowsGfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
-	SCI1_EGADriver::initScreen(format);
-
-	static const LineProc lineProcs[] = {
-		&win16ColRenderLine<byte, false>,
-		&win16ColRenderLine<byte, true>,
-		&win16ColRenderLine<uint16, false>,
-		&win16ColRenderLine<uint16, true>,
-		&win16ColRenderLine<uint32, false>,
-		&win16ColRenderLine<uint32, true>
-	};
-
-	assert((_pixelSize | 1) < ARRAYSIZE(lineProcs));
-	_renderLine = lineProcs[_pixelSize & ~1];
-	_renderLine2 = lineProcs[_pixelSize | 1];
-}
-
-void WindowsGfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
-	// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
-	byte col1 = findColorInPalette(0x00000000, _convPalette, _numColors);
-	byte col2 = findColorInPalette(0x00FFFFFF, _convPalette, _numColors);
-	renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, false);
-	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
-}
-
-Common::Point WindowsGfx16ColorsDriver::getRealCoords(Common::Point &pos) const {
-	return Common::Point(pos.x << 1, (pos.y << 1) + (pos.y + 4) / 5);
-}
-
-void WindowsGfx16ColorsDriver::loadData() {
-	Common::File file;
-	if (!file.open(_driverFile))
-		GFXDRV_ERR_OPEN(_driverFile);
-
-	int64 sz = file.size();
-	byte *buf = new byte[sz];
-	file.read(buf, sz);
-	file.close();
-
-	// We can keep the search for the table simple, since there are only two supported executables (KQ6
-	// Windows and SQ4 Windows) and both contain the following value only within the pattern table...
-	uint32 srch = FROM_LE_32(0xCC4C4404);
-
-	const byte *tblOffs = nullptr;
-	for (const byte *pos = buf; pos < buf + sz - 67 && tblOffs == nullptr; ++pos) {
-		// We check three times, just to be sure. Checking once would actually suffice.
-		if (READ_UINT32(pos) != srch || READ_UINT32(pos + 8) != srch || READ_UINT32(pos + 64) != srch)
-			continue;
-		tblOffs = pos - 4;
-	}
-
-	if (tblOffs == nullptr)
-		error("%s(): Failed to load 16 colors match table", __FUNCTION__);
-
-	byte *tbl = new byte[512];
-	memcpy(tbl, tblOffs, 512);
-	_egaMatchTable = tbl;
-
-	delete[] buf;
-
-	_colAdjust = (_egaMatchTable[482] == 0x79) ? 4 : 0;
-	_numColors = 16;
-}
-
-void WindowsGfx16ColorsDriver::renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
-	const byte *dst0 = dst;
-	byte mod = (y + 4) % 5;
-	byte swap = _enhancedDithering ? ((y + 4) / 5) & 1 : 0;
-	for (int i = 0; i < h; ++i) {
-		if (++mod == 5) {
-			_renderLine2(dst, src, w, patterns, palette, swap);
-			if (_enhancedDithering)
-				swap ^= 1;
-			mod = 0;
-		} else {
-			_renderLine(dst, src, w, patterns, palette, swap);
-		}
-		src += pitch;
-	}
-	realWidth = w << 1;
-	realHeight = (dst - dst0) / (realWidth * _pixelSize);
-}
-
-const char *WindowsGfx16ColorsDriver::_driverFile = "SCIWV.EXE";
-
-PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette) :
-	UpscaledGfxDriver(textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
-		_cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
-	// Palette taken from driver file (identical for all versions of the
-	// driver I have seen so far, also same for SCI0 and SCI1)
-	static const byte pc98colorsV16[] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x07, 0x07,
-		0x07, 0x00, 0x00, 0x07, 0x00, 0x07, 0x05, 0x07, 0x00, 0x09, 0x09, 0x09,
-		0x06, 0x06, 0x06, 0x00, 0x00, 0x0f, 0x07, 0x0f, 0x06, 0x00, 0x0f, 0x0f,
-		0x0f, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f
-	};
-
-	byte *col = new byte[768]();
-	const byte *s = pc98colorsV16;
-
-	for (uint i = 0; i < sizeof(pc98colorsV16) / 3; ++i) {
-		int a = ((i & 6) == 4 || (i & 6) == 2 ? i ^ 6 : i) * 3;
-		col[a + 0] = (s[1] * 0x11);
-		col[a + 1] = (s[0] * 0x11);
-		col[a + 2] = (s[2] * 0x11);
-		s += 3;
-	}
-
-	if (_fontStyle == kFontStyleTextMode) {
-		byte *d = &col[48];
-		for (uint8 i = 0; i < 8; ++i) {
-			*d++ = (i & 4) ? 0xff : 0;
-			*d++ = (i & 2) ? 0xff : 0;
-			*d++ = (i & 1) ? 0xff : 0;
-		}
-	}
-
-	if (needsUnditheringPalette) {
-		// We store the text mode color separately, since we need the slots for the undithering.
-		if (_fontStyle == kFontStyleTextMode) {
-			byte *tpal = new byte[24]();
-			memcpy(tpal, &col[48], 24);
-			_textModePalette = tpal;
-		}
-		// For the undithered mode, we generate the missing colors using the same formula as for EGA.
-		byte *d = &col[48];
-		for (int i = 16; i < 256; i++) {
-			const byte *s1 = &col[(i & 0x0f) * 3];
-			const byte *s2 = &col[(i >> 4) * 3];
-			for (int ii = 0; ii < 3; ++ii)
-				*d++ = (byte)(0.5 + (pow(0.5 * ((pow(*s1++ / 255.0, 2.2 / 1.0) * 255.0) + (pow(*s2++ / 255.0, 2.2 / 1.0) * 255.0)) / 255.0, 1.0 / 2.2) * 255.0));
-		}
-	}
-
-	_convPalette = col;
-}
-
-PC98Gfx16ColorsDriver::~PC98Gfx16ColorsDriver() {
-	delete[] _convPalette;
-	delete[] _textModePalette;
-}
-
-void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
-	dstPitch -= w;
-	srcPitch -= w;
-
-	while (h--) {
-		for (int i = 0; i < w - 1; ++i) {
-			uint8 a = *src++;
-			uint8 b = *src;
-			if (a != transpCol)
-				*dst = a;
-			else if (b != transpCol)
-				*dst = b;
-			++dst;
-		}
-		byte l = *src++;
-		if (l != transpCol)
-			*dst = l;
-		++dst;
-		src += srcPitch;
-		dst += dstPitch;
-	}
-}
-
-void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
-	assert(h == 16); // This is really not suitable for anything but the special SCI1 PC98 glyph drawing
-	dstPitch -= w;
-	srcPitch -= w;
-
-	while (h--) {
-		if (h > 10 || h < 5) {
-			for (int i = 0; i < w - 1; ++i) {
-				uint8 a = *src++;
-				uint8 b = *src;
-				if (a != transpCol)
-					*dst = a;
-				else if (b != transpCol)
-					*dst = b;
-				++dst;
-			}
-			byte l = *src++;
-			if (l != transpCol)
-				*dst = l;
-			++dst;
-		} else {
-			for (int i = 0; i < w; ++i) {
-				byte in = *src++;
-				if (in != transpCol)
-					*dst = in;
-				++dst;
-			}
-		}
-		src += srcPitch;
-		dst += dstPitch;
-	}
-}
-
-void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
-	UpscaledGfxDriver::initScreen(format);
-
-	assert(_convPalette);
-	GfxDefaultDriver::setPalette(_convPalette, 0, 256, true, nullptr, nullptr);
-
-	if (_fontStyle == kFontStyleTextMode)
-		_renderGlyph = &renderPC98GlyphFat;
-
-	if (_fontStyle != kFontStyleSpecialSCI1)
-		return;
-
-	_renderGlyph = &renderPC98GlyphSpecial;
-}
-
-void PC98Gfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	if (!_cursorScaleHeightOnly) {
-		UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
-		return;
-	}
-
-	// Special case for PQ2 which scales the cursor height (but not the width)
-	adjustCursorBuffer(w, h << 1);
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	byte *d = _compositeBuffer;
-
-	for (uint i = 0; i < h; ++i) {
-		memcpy(d, s, w);
-		d += w;
-		memcpy(d, s, w);
-		d += w;
-		s += w;
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX, hotspotY << 1, keycolor);
-}
-
-byte PC98Gfx16ColorsDriver::remapTextColor(byte color) const {
-	// Always black for QFG and SCI1. For QFG, this is on purpose. The code just copies the inverted glyph data
-	// into all 4 vmem planes. For SCI1 the driver opcode actually has a pen color argument, but it isn't put
-	// to any use. Not sure if it is intended.
-	if (_fontStyle != kFontStyleTextMode)
-		return 0;
-
-	color &= 7;
-	// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
-	// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
-	// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
-	// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
-	// and cyan colors are turned into white.
-	if (color & 2)
-		color |= 4;
-	if (color & 4)
-		color |= 2;
-	// This is the blue color that PQ2 uses basically for all Japanese text...
-	if (color == 0)
-		color = 1;
-
-	byte textCol = color;
-	color += 0x10;
-
-	if (_textModePalette) {
-		// If we have used up the whole space of the CLUT8 for the undithering, we try
-		// to relocate the color which will work for all text mode colors with the default
-		// palette that is used by the PC-98 ports...
-		for (int i = 0; i < 256; ++i) {
-			if (_convPalette[i * 3] != _textModePalette[textCol * 3] || _convPalette[i * 3 + 1] != _textModePalette[textCol * 3 + 1] || _convPalette[i * 3 + 2] != _textModePalette[textCol * 3 + 2])
-				continue;
-			color = i;
-			break;
-		}
-		if (color >= 16)
-			color = 0;
-	}
-	return color;
-}
-
-SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering) :
-	UpscaledGfxDriver(8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _useTextMode(useTextModeForSJISChars), _convPalette(nullptr) {
-	byte *col = new byte[8 * 3]();
-	_convPalette = col;
-
-	for (uint8 i = 0; i < 8; ++i) {
-		*col++ = (i & 4) ? 0xff : 0;
-		*col++ = (i & 2) ? 0xff : 0;
-		*col++ = (i & 1) ? 0xff : 0;
-	}
-}
-
-SCI0_PC98Gfx8ColorsDriver::~SCI0_PC98Gfx8ColorsDriver() {
-	delete[] _convPalette;
-}
-
-void pc98SimpleDither(byte *dst, const byte *src, int pitch, int w, int h) {
-	int dstPitch = pitch << 1;
-	byte *d1 = dst;
-	byte *d2 = d1 + dstPitch;
-	pitch -= w;
-	dstPitch += (pitch << 1);
-
-	while (h--) {
-		for (int i = 0; i < w; ++i) {
-			uint8 v = *src++;
-			d1[0] = d2[0] = (v & 7);
-			d1[1] = d2[1] = (v & 8) ? (v & 7) : 0;
-			d1 += 2;
-			d2 += 2;
-		}
-		src += pitch;
-		d1 += dstPitch;
-		d2 += dstPitch;
-	}
-}
-
-void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
-	UpscaledGfxDriver::initScreen(format);
-	_renderScaled = &pc98SimpleDither;
-	if (_useTextMode)
-		_renderGlyph = &renderPC98GlyphFat;
-	assert(_convPalette);
-	GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
-}
-
-void SCI0_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	adjustCursorBuffer(w, _cursorScaleHeightOnly ? h << 1 : h);
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	byte *d = _compositeBuffer;
-	uint32 newKeyColor = 0xFF;
-
-	for (uint i = 0; i < h; ++i) {
-		for (uint ii = 0; ii < w; ++ii) {
-			byte col = *s++;
-			*d++ = (col == keycolor) ? newKeyColor : (col & 7);
-		}
-	}
-
-	// Special case for PQ2 which 2x scales the cursor height
-	if (_cursorScaleHeightOnly) {
-		s = _compositeBuffer + h * w - w;
-		d = _compositeBuffer + h * w * 2 - w;
-
-		for (uint i = 0; i < h; ++i) {
-			memcpy(d, s, w);
-			d -= w;
-			memcpy(d, s, w);
-			d -= w;
-			s -= w;
-		}
-		h <<= 1;
-		hotspotX <<= 1;
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, newKeyColor);
-}
-
-byte SCI0_PC98Gfx8ColorsDriver::remapTextColor(byte color) const {
-	// Always black. For QFG, this is on purpose. The code just copies the inverted glyph data into all 4 vmem planes.
-	if (!_useTextMode)
-		return 0;
-
-	color &= 7;
-	// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
-	// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
-	// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
-	// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
-	// and cyan colors are turned into white.
-	if (color & 2)
-		color |= 4;
-	if (color & 4)
-		color |= 2;
-	// This is the blue color that PQ2 uses basically for all Japanese text...
-	if (color == 0)
-		color = 1;
-
-	return color;
-}
-
-const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
-
-SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
-	Common::File drv;
-	if (!drv.open(_driverFile))
-		GFXDRV_ERR_OPEN(_driverFile);
-
-	uint16 eprcOffs = 0;
-
-	uint32 cmd = drv.readUint32LE();
-	if ((cmd & 0xFF) == 0xE9)
-		eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
-
-	if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
-		GFXDRV_ERR_VERSION(_driverFile);
-
-	uint32 pos = (drv.pos() + 1) & ~1;
-
-	drv.seek(pos + 2);
-	drv.seek(drv.readUint16LE());
-	byte *buff = new byte[190];
-	drv.read(buff, 190);
-
-	uint16 tableOffs = 0;
-	int step = 0;
-	for (int i = 0; i < 182 && !tableOffs; ++i) {
-		uint32 c = READ_BE_UINT32(buff + i);
-		if (step == 0 && ((c & 0xFFF0FFF0) != 0xD1E0D1E0 || (READ_BE_UINT32(buff + i + 4) ^ c)))
-			continue;
-
-		if (step == 0) {
-			step = 1;
-			i += 7;
-			continue;
-		}
-
-		if (c >> 20 != 0x81C || ((READ_BE_UINT32(buff + i + 4) >> 20) ^ (c >> 20)))
-			continue;
-
-		if ((c & 0xFFFF) == READ_BE_UINT16(buff + i + 6))
-			tableOffs = FROM_BE_16(c);
-	}
-	delete[] buff;
-
-	if (!tableOffs)
-		error("SCI1_PC98Gfx8ColorsDriver: Failed to load dithering data from '%s'", _driverFile);
-
-	drv.seek(tableOffs);
-	byte *dmx = new byte[96]();
-	drv.read(dmx, 96);
-
-	if (drv.readUint16LE() != 0xA800 || drv.readUint16LE() != 0xB000)
-		GFXDRV_ERR_VERSION(_driverFile);
-
-	drv.close();
-
-	byte *dt = new byte[1536]();
-	_ditheringTable = dt;
-
-	for (uint16 i = 0; i < 256; ++i) {
-		for (int ii = 0; ii < 6; ++ii)
-			*dt++ = (dmx[(i >> 4) * 6 + ii] & 0xCC) | (dmx[(i & 0x0f) * 6 + ii] & 0x33);
-	}
-
-	delete[] dmx;
-
-	_textAlignX = 1;
-
-	byte *col = new byte[8 * 3]();
-	_convPalette = col;
-
-	for (uint8 i = 0; i < 8; ++i) {
-		*col++ = (i & 2) ? 0xff : 0;
-		*col++ = (i & 1) ? 0xff : 0;
-		*col++ = (i & 4) ? 0xff : 0;
-	}
-}
-
-SCI1_PC98Gfx8ColorsDriver::~SCI1_PC98Gfx8ColorsDriver() {
-	delete[] _ditheringTable;
-	delete[] _convPalette;
-}
-
-void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, const byte *tbl) {
-	int dstPitch = pitch << 1;
-	byte *d1 = dst;
-	byte *d2 = d1 + dstPitch;
-	pitch -= w;
-	dstPitch += (pitch << 1);
-
-	while (h--) {
-		byte sh = 0;
-		for (int i = 0; i < (w >> 1); ++i) {
-			const byte *c = &tbl[(src[0] << 4 | src[1]) * 6];
-			for (int ii = sh; ii < sh + 4; ++ii) {
-				*d1++ = (((c[0] >> (7 - ii)) & 1) << 2) | (((c[1] >> (7 - ii)) & 1) << 1) | ((c[2] >> (7 - ii)) & 1);
-				*d2++ = (((c[3] >> (7 - ii)) & 1) << 2) | (((c[4] >> (7 - ii)) & 1) << 1) | ((c[5] >> (7 - ii)) & 1);
-			}
-			src += 2;
-			sh ^= 4;
-		}
-
-		src += pitch;
-		d1 += dstPitch;
-		d2 += dstPitch;
-	}
-}
-
-void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
-	UpscaledGfxDriver::initScreen(format);
-
-	_renderGlyph = &renderPC98GlyphFat;
-
-	assert(_convPalette);
-	GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
-}
-
-void SCI1_PC98Gfx8ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	GFXDRV_ASSERT_READY;
-	assert (h >= 0 && w >= 0);
-
-	byte diff = srcX & 7;
-	srcX &= ~7;
-	destX &= ~7;
-	w = (w + diff + 7) & ~7;
-
-	src += (srcY * pitch + srcX * _srcPixelSize);
-	if (src != _currentBitmap)
-		updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
-
-	byte *scb = _scaledBitmap + (destY << 1) * _screenW * _srcPixelSize + (destX << 1) * _srcPixelSize;
-	renderPlanarMatrix(scb, src, pitch, w, h, _ditheringTable);
-
-	updateScreen(destX << 1, destY << 1,  w << 1, h << 1, palMods, palModMapping);
-}
-
-void SCI1_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
-	GFXDRV_ASSERT_READY;
-	adjustCursorBuffer(w << 1, h << 1);
-	const byte *s = reinterpret_cast<const byte*>(cursor);
-	byte *d1 = _compositeBuffer;
-	uint32 newKeyColor = 0xFF;
-
-	int dstPitch = (w << 1);
-	byte *d2 = _compositeBuffer + dstPitch;
-
-	for (uint i = 0; i < h; ++i) {
-		for (uint ii = 0; ii < w; ++ii) {
-			byte col = *s++;
-			if (col == keycolor) {
-				*d1++ = *d2++ = newKeyColor;
-				*d1++ = *d2++ = newKeyColor;
-			} else {
-				*d1++ = *d2++ = (col & 7);
-				*d1++ = *d2++ = (col & 8) ? (col & 7) : 0;
-			}
-		}
-		d1 += dstPitch;
-		d2 += dstPitch;
-	}
-
-	CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
-}
-
-byte SCI1_PC98Gfx8ColorsDriver::remapTextColor(byte) const {
-	// Always black. The driver opcode actually has a pen color argument, but it isn't put to any use. Not sure if it is intended.
-	return 0;
-}
-const char *SCI1_PC98Gfx8ColorsDriver::_driverFile = "9801V8.DRV";
-
-#undef GFXDRV_ASSERT_READY
-#undef GFXDRV_ERR_OPEN
-#undef GFXDRV_ERR_VERSION
-
-} // End of namespace Sci
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
deleted file mode 100644
index e4a54095c0d..00000000000
--- a/engines/sci/graphics/gfxdrivers.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef SCI_GRAPHICS_GFXDRIVERS_H
-#define SCI_GRAPHICS_GFXDRIVERS_H
-
-#include "common/platform.h"
-#include "common/rect.h"
-#include "graphics/pixelformat.h"
-
-namespace Graphics {
-	class Cursor;
-}
-
-namespace Sci {
-
-struct PaletteMod;
-
-class GfxDriver {
-public:
-	enum DrawFlags : uint32 {
-		kHiResMode		=	1 << 0,
-		kMovieMode		=	1 << 1
-	};
-
-	GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _ready(false), _pixelSize(1) {}
-	virtual ~GfxDriver() {}
-	virtual void initScreen(const Graphics::PixelFormat *srcRGBFormat = nullptr) = 0; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
-	virtual void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) = 0;
-	virtual void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) = 0;
-	virtual void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) = 0;
-	virtual void replaceMacCursor(const Graphics::Cursor *cursor) = 0;
-	virtual Common::Point getMousePos() const;
-	virtual void setMousePos(const Common::Point &pos) const;
-	virtual void setShakePos(int shakeXOffset, int shakeYOffset) const;
-	virtual void clearRect(const Common::Rect &r) const;
-	virtual void copyCurrentBitmap(byte *dest, uint32 size) const = 0;
-	virtual void copyCurrentPalette(byte *dest, int start, int num) const;
-	virtual void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) = 0;
-	virtual byte remapTextColor(byte color) const { return color; }
-	virtual void setColorMap(const byte *colorMap) {}
-	virtual Common::Point getRealCoords(Common::Point &pos) const { return pos; }
-	virtual void setFlags(uint32 flags) {}
-	virtual void clearFlags(uint32 flags) {}
-	virtual bool supportsPalIntensity() const = 0;
-	virtual bool supportsHiResGraphics() const = 0;
-	virtual bool driverBasedTextRendering() const = 0;
-	uint16 numColors() const { return _numColors; }
-	byte pixelSize() const { return _pixelSize; }
-
-protected:
-	bool _ready;
-	static bool checkDriver(const char *const *driverNames, int listSize);
-	const uint16 _screenW;
-	const uint16 _screenH;
-	uint16 _numColors;
-	byte _pixelSize;
-};
-
-class GfxDefaultDriver : public GfxDriver {
-public:
-	GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering);
-	~GfxDefaultDriver() override;
-	void initScreen(const Graphics::PixelFormat *srcRGBFormat) override; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
-	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	void replaceMacCursor(const Graphics::Cursor*) override;
-	void copyCurrentBitmap(byte *dest, uint32 size) const override;
-	void copyCurrentPalette(byte *dest, int start, int num) const override;
-	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
-	bool supportsPalIntensity() const override { return true; }
-	bool supportsHiResGraphics() const override { return false; }
-	bool driverBasedTextRendering() const override { return false; }
-protected:
-	void updatePalette(const byte *colors, uint start, uint num);
-	byte *_compositeBuffer;
-	byte *_currentBitmap;
-	byte *_currentPalette;
-	byte *_internalPalette;
-	uint16 _virtualW;
-	uint16 _virtualH;
-	Graphics::PixelFormat _format;
-	byte _srcPixelSize;
-	bool _cursorUsesScreenPalette;
-	const bool _alwaysCreateBmpBuffer;
-	const bool _requestRGBMode;
-	typedef void (*ColorConvProc)(byte*, const byte*, int, int, int, const byte*);
-	ColorConvProc _colorConv;
-	typedef void (*ColorConvModProc)(byte*, const byte*, int, int, int, const byte*, const byte*, Graphics::PixelFormat&, const PaletteMod*, const byte*);
-	ColorConvModProc _colorConvMod;
-private:
-	void generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
-};
-
-class SCI0_DOSPreVGADriver : public GfxDriver {
-public:
-	SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering);
-	~SCI0_DOSPreVGADriver() override;
-	void initScreen(const Graphics::PixelFormat*) override;
-	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
-	void replaceMacCursor(const Graphics::Cursor*) override;
-	void copyCurrentBitmap(byte*, uint32) const override;
-	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
-	void copyCurrentPalette(byte *dest, int start, int num) const override;
-	bool supportsPalIntensity() const override { return false; }
-	bool supportsHiResGraphics() const override { return false; }
-	bool driverBasedTextRendering() const override { return false; }
-protected:
-	void assignPalette(const byte *colors);
-	byte *_compositeBuffer;
-	const byte *_internalPalette;
-private:
-	virtual void setupRenderProc() = 0;
-	const bool _requestRGBMode;
-	const byte *_colors;
-};
-
-class SCI0_CGADriver final : public SCI0_DOSPreVGADriver {
-public:
-	SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering);
-	~SCI0_CGADriver() override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
-private:
-	void setupRenderProc() override;
-	uint16 *_cgaPatterns;
-	byte _palette[12];
-	const bool _disableMode5;
-	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const uint16*, const byte*);
-	LineProc _renderLine;
-	static const char *_driverFile;
-};
-
-class SCI0_CGABWDriver final : public SCI0_DOSPreVGADriver {
-public:
-	SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering);
-	~SCI0_CGABWDriver() override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	Common::Point getMousePos() const override;
-	void setMousePos(const Common::Point &pos) const override;
-	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
-	void clearRect(const Common::Rect &r) const override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(_driverFiles, 2); }
-private:
-	void setupRenderProc() override;
-	byte _monochromePalette[6];
-	const byte *_monochromePatterns;
-	bool _earlyVersion;
-	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
-	LineProc _renderLine;
-	static const char *_driverFiles[2];
-};
-
-class SCI0_HerculesDriver final : public SCI0_DOSPreVGADriver {
-public:
-	SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage);
-	~SCI0_HerculesDriver() override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	Common::Point getMousePos() const override;
-	void setMousePos(const Common::Point &pos) const override;
-	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
-	void clearRect(const Common::Rect &r) const override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
-private:
-	void setupRenderProc() override;
-	const uint16 _centerX;
-	const uint16 _centerY;
-	byte _monochromePalette[6];
-	const byte *_monochromePatterns;
-	typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
-	LineProc _renderLine;
-	static const char *_driverFile;
-};
-
-class SCI1_VGAGreyScaleDriver final : public GfxDefaultDriver {
-public:
-	SCI1_VGAGreyScaleDriver(bool rgbRendering);
-	~SCI1_VGAGreyScaleDriver() override;
-	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
-private:
-	byte *_greyScalePalette;
-	static const char *_driverFile;
-};
-
-class SCI1_EGADriver : public GfxDriver {
-public:
-	SCI1_EGADriver(bool rgbRendering);
-	~SCI1_EGADriver() override;
-	void initScreen(const Graphics::PixelFormat*) override;
-	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	void replaceMacCursor(const Graphics::Cursor *cursor) override {}
-	void copyCurrentBitmap(byte *dest, uint32 size) const override;
-	void copyCurrentPalette(byte *dest, int start, int num) const override;
-	void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
-	Common::Point getMousePos() const override;
-	void setMousePos(const Common::Point &pos) const override;
-	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
-	void clearRect(const Common::Rect &r) const override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	bool supportsPalIntensity() const override { return false; }
-	bool supportsHiResGraphics() const override { return false; }
-	bool driverBasedTextRendering() const override { return false; }
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
-protected:
-	typedef void (*LineProc)(byte*&, const byte*, int, const byte*, const byte*, bool);
-	LineProc _renderLine;
-	const byte *_convPalette;
-	uint16 _vScaleMult, _vScaleDiv;
-	const byte *_egaMatchTable;
-	byte *_egaColorPatterns;
-	uint8 _colAdjust;
-	byte *_compositeBuffer;
-	byte *_currentPalette;
-private:
-	virtual void loadData();
-	virtual void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight);
-	byte *_currentBitmap;
-	const byte *_internalPalette;
-	const bool _requestRGBMode;
-	static const char *_driverFile;
-};
-
-class UpscaledGfxDriver : public GfxDefaultDriver {
-public:
-	UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering);
-	~UpscaledGfxDriver() override;
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	Common::Point getMousePos() const override;
-	void setMousePos(const Common::Point &pos) const override;
-	void setShakePos(int shakeXOffset, int shakeYOffset) const override;
-	void clearRect(const Common::Rect &r) const override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) override; // For HiRes fonts. PC-98 versions bypass the video driver for this and render directly on top of the vram.
-	bool driverBasedTextRendering() const override { return true; }
-protected:
-	UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering);
-	void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
-	void adjustCursorBuffer(uint16 newWidth, uint16 newHeight);
-	typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int);
-	GlyphRenderProc _renderGlyph;
-	typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
-	ScaledRenderProc _renderScaled;
-	uint16 _textAlignX;
-	uint16 _hScaleMult;
-	uint16 _vScaleMult;
-	uint16 _vScaleDiv;
-	byte *_scaledBitmap;
-private:
-	virtual void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight);
-	const bool _scaleCursor;
-	uint16 _cursorWidth;
-	uint16 _cursorHeight;
-	bool _needCursorBuffer;
-};
-
-class WindowsGfx256ColorsDriver final : public UpscaledGfxDriver {
-public:
-	WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow, bool rgbRendering);
-	~WindowsGfx256ColorsDriver() override {}
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	void setColorMap(const byte *colorMap) override { _colorMap = colorMap; }
-	void setFlags(uint32 flags) override;
-	void clearFlags(uint32 flags) override;
-	bool supportsHiResGraphics() const override { return !_smallWindow; }
-protected:
-	typedef void (*LineProc)(byte*&, const byte*, int, int, int);
-	LineProc _renderLine;
-private:
-	typedef void (*LineProcSpec)(byte*&, const byte*, int, int, const byte*);
-	LineProcSpec _renderLine2;
-	void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) override;
-	uint32 _flags;
-	const byte *_colorMap;
-	const bool _smallWindow;
-	const bool _dosStyleCursors;
-	uint16 _vScaleMult2;
-};
-
-class WindowsGfx16ColorsDriver final : public SCI1_EGADriver {
-public:
-	// The original does not take into account the extra lines required for the 200->440 vertical scaling. There is a noticeable dithering glitch every 11th line, as the
-	// two pixels of the checkerbox pattern appear in the wrong order. I have implemented a fix for this which can be activated with the fixDithering parameter.
-	WindowsGfx16ColorsDriver(bool fixDithering, bool rgbRendering);
-	~WindowsGfx16ColorsDriver() override {}
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	Common::Point getRealCoords(Common::Point &pos) const override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformWindows && checkDriver(&_driverFile, 1)); }
-private:
-	void loadData() override;
-	void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) override;
-	LineProc _renderLine2;
-	const bool _enhancedDithering;
-	static const char *_driverFile;
-};
-
-class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
-public:
-	enum SjisFontStyle {
-		kFontStyleNone,
-		kFontStyleTextMode,
-		kFontStyleSpecialSCI1
-	};
-
-	PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette);
-	~PC98Gfx16ColorsDriver() override;
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	byte remapTextColor(byte color) const override;
-private:
-	const byte *_convPalette;
-	const byte *_textModePalette;
-	const bool _cursorScaleHeightOnly;
-	SjisFontStyle _fontStyle;
-};
-
-class SCI0_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
-public:
-	SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering);
-	~SCI0_PC98Gfx8ColorsDriver() override;
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	byte remapTextColor(byte color) const override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(_driverFiles, 2); }
-private:
-	const byte *_convPalette;
-	const bool _cursorScaleHeightOnly;
-	const bool _useTextMode;
-	static const char *_driverFiles[2];
-};
-
-class SCI1_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
-public:
-	SCI1_PC98Gfx8ColorsDriver(bool rgbRendering);
-	~SCI1_PC98Gfx8ColorsDriver() override;
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
-	void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
-	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
-	byte remapTextColor(byte) const override;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(&_driverFile, 1); }
-private:
-	const byte *_ditheringTable;
-	const byte *_convPalette;
-	static const char *_driverFile;
-};
-
-} // End of namespace Sci
-
-#endif // SCI_GRAPHICS_GFXDRIVERS_H
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index 27017b6834a..c384958607c 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -23,7 +23,7 @@
 #include "sci/engine/kernel.h"
 #include "sci/engine/selector.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/maciconbar.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/screen.h"
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 977470c548e..c57b2d5ce6f 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -33,7 +33,7 @@
 #include "sci/graphics/picture.h"
 #include "sci/graphics/view.h"
 #include "sci/graphics/screen.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/portrait.h"
 #include "sci/graphics/text16.h"
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 85a9bd12686..ad75722e27f 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -27,7 +27,7 @@
 #include "sci/sci.h"
 #include "sci/engine/state.h"
 #include "sci/graphics/cache.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/maciconbar.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/remap.h"
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 5e4f28c95f8..62af1c70b2b 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -26,7 +26,7 @@
 #include "sci/sci.h"
 #include "sci/event.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/portrait.h"
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 02412073f88..0ac7f3947f2 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -34,7 +34,7 @@
 #include "sci/graphics/view.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/scifx.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 
 namespace Sci {
 
@@ -143,84 +143,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		}
 	}
 
-	bool enablePaletteMods = ConfMan.hasKey("palette_mods") && ConfMan.getBool("palette_mods");
-	bool requestRGB = enablePaletteMods || (ConfMan.hasKey("rgb_rendering") && ConfMan.getBool("rgb_rendering"));
-
-	_gfxDrv = nullptr;
-	switch (renderMode) {
-	case Common::kRenderCGA:
-		_gfxDrv = new SCI0_CGADriver(false, requestRGB);
-		break;
-	case Common::kRenderCGA_BW:
-		_gfxDrv = new SCI0_CGABWDriver(0xffffff, requestRGB);
-		break;
-	case Common::kRenderHercA:
-	case Common::kRenderHercG:
-		_gfxDrv = new SCI0_HerculesDriver(renderMode == Common::kRenderHercG ? 0x66ff66 : 0xffbf66, requestRGB, false);
-		break;
-	case Common::kRenderEGA:
-		if (getSciVersion() > SCI_VERSION_1_EGA_ONLY)
-			_gfxDrv = new SCI1_EGADriver(requestRGB);
-		break;
-	case Common::kRenderVGAGrey:
-		_gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
-		break;
-	case Common::kRenderWin16c:
-		_gfxDrv = new WindowsGfx16ColorsDriver(true, requestRGB);
-		break;
-	case Common::kRenderPC98_8c:
-		if (g_sci->getGameId() == GID_PQ2)
-			// PQ2 is a bit special, probably the oldest of the PC-98 ports. Unlike all the others, it uses text mode print
-			// and it doesn't even have a 16 colors drivers. See comment below...
-			_gfxDrv = new SCI0_PC98Gfx8ColorsDriver(true, true, requestRGB);
-		else if (getSciVersion() <= SCI_VERSION_01)
-			_gfxDrv = new SCI0_PC98Gfx8ColorsDriver(false, false, requestRGB);
-		else
-			_gfxDrv = new SCI1_PC98Gfx8ColorsDriver(requestRGB);
-		_hiresGlyphBuffer = new byte[16 * 16]();
-		break;
-	default:
-		break;
-	}
-
-	if (_gfxDrv == nullptr) {
-		switch (g_sci->getPlatform()) {
-		case Common::kPlatformPC98:
-			if (g_sci->getGameId() == GID_PQ2)
-				// PQ2 is a bit special, probably the oldest of the PC-98 ports. Unlike all the others, it uses text mode print,
-				// so the text color is a system color outside the normal 16 colors palette. The original does not even have a
-				// 16 colors mode driver. Only the 8 colors mode, where the colors are identical for text and graphics mode.
-				// But we do want to provide the 16 colors mode, since it is not a big deal (i.e., it does not require data
-				// from a driver file and the fat print is also already there for the 8 colors mode). So we just make the
-				// necessary adjustments.
-				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, true, PC98Gfx16ColorsDriver::kFontStyleTextMode, requestRGB, ConfMan.getBool("disable_dithering"));
-			else if (getSciVersion() <= SCI_VERSION_01)
-				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, false, PC98Gfx16ColorsDriver::kFontStyleNone, requestRGB, true);
-			else
-				_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, requestRGB, true);
-			break;
-
-		case Common::kPlatformWindows:
-		case Common::kPlatformDOS:
-			// King's Quest 6 has hires content in the Windows version which we also allow to be optionally enabled in the DOS version
-			// and which we also optionally allow to be disabled in the Windows version. Also, the Windows versions of King's Quest 6
-			// and Space Quest 4 have support in the original interpreter code for a small 320 x 240 window on desktops with resolutions
-			// of less than 640 x 480, but I haven't managed to produce it in a Win95 VM; the windows setting don't seem to allow less
-			// than 640 x 480, so I don't know if it is actually possible to set it up. Anyway, we can use it here, for the configs that
-			// do not require hires support.
-			if ((g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_SQ4) && (g_sci->getPlatform() == Common::kPlatformWindows || g_sci->useHiresGraphics())) {
-				_gfxDrv = new WindowsGfx256ColorsDriver(!ConfMan.getBool("windows_cursors"), !g_sci->useHiresGraphics(), requestRGB);
-				break;
-			}
-			// fallthrough
-		default:
-			if (g_sci->getLanguage() == Common::KO_KOR)
-				_gfxDrv = new UpscaledGfxDriver(1, true, requestRGB);
-			else // The driver has to be told if is SCI_VERSION_01, since that cannot be determined from the number of colors.
-				_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, getSciVersion() < SCI_VERSION_01, requestRGB);
-			break;
-		}
-	}
+	_gfxDrv = SciGfxDriver::create(renderMode, _displayWidth, _displayHeight + extraHeight);
 	assert(_gfxDrv);
 
 	// Buffer for rendering a single two-byte character
@@ -265,7 +188,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 	}
 
 	// Set up palette mods if requested
-	if (enablePaletteMods)
+	if (ConfMan.hasKey("palette_mods") && ConfMan.getBool("palette_mods"))
 		setupCustomPaletteMods(this);
 
 	// Initialize the actual screen
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index bfd144a71d6..58b935e462d 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -25,7 +25,7 @@
 
 #include "sci/sci.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/transitions.h"
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 25b84f65cf3..dd87ed973b1 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -21,7 +21,7 @@
 
 #include "sci/sci.h"
 #include "sci/engine/state.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/remap.h"
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 51b3a2d8b9c..8801ea412e3 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -50,7 +50,6 @@ MODULE_OBJS := \
 	graphics/cursor.o \
 	graphics/fontkorean.o \
 	graphics/fontsjis.o \
-	graphics/gfxdrivers.o \
 	graphics/macfont.o \
 	graphics/maciconbar.o \
 	graphics/menu.o \
@@ -66,6 +65,20 @@ MODULE_OBJS := \
 	graphics/text16.o \
 	graphics/transitions.o \
 	graphics/view.o \
+	graphics/drivers/cga.o \
+	graphics/drivers/cgabw.o \
+	graphics/drivers/common.o \
+	graphics/drivers/default.o \
+	graphics/drivers/ega.o \
+	graphics/drivers/hercules.o \
+	graphics/drivers/init.o \
+	graphics/drivers/pc98_8col_sci0.o \
+	graphics/drivers/pc98_8col_sci1.o \
+	graphics/drivers/pc98_16col.o \
+	graphics/drivers/upscaled.o \
+	graphics/drivers/vgagrey.o \
+	graphics/drivers/win16col.o \
+	graphics/drivers/win256col.o \
 	parser/grammar.o \
 	parser/said.o \
 	parser/vocabulary.o \
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 9334370bbef..41007473fe9 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -54,7 +54,7 @@
 #include "sci/graphics/controls16.h"
 #include "sci/graphics/coordadjuster.h"
 #include "sci/graphics/cursor.h"
-#include "sci/graphics/gfxdrivers.h"
+#include "sci/graphics/drivers/gfxdriver.h"
 #include "sci/graphics/macfont.h"
 #include "sci/graphics/maciconbar.h"
 #include "sci/graphics/menu.h"
@@ -319,29 +319,11 @@ Common::Error SciEngine::run() {
 	}
 
 	if (getSciVersion() < SCI_VERSION_2) {
-		Common::RenderMode renderMode = Common::kRenderDefault;
-
 		bool undither = ConfMan.getBool("disable_dithering");
-		if (ConfMan.hasKey("render_mode"))
-			renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
-
-		// Check if the selected render mode is available for the game. This is quite specific for each game.
-		// Sometime it is only EGA, sometimes only CGA b/w without CGA 4 colors, etc. Also set default mode if undithering is enabled.
-		Common::Platform p = getPlatform();
-		if ((renderMode == Common::kRenderEGA && (((getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY) && undither) ||
-			(getSciVersion() >= SCI_VERSION_1_EARLY && getSciVersion() <= SCI_VERSION_1_1 && !SCI1_EGADriver::validateMode(p)))) ||
-			(renderMode == Common::kRenderVGAGrey && !SCI1_VGAGreyScaleDriver::validateMode(p)) ||
-			(renderMode == Common::kRenderCGA && !SCI0_CGADriver::validateMode(p)) ||
-			(renderMode == Common::kRenderCGA_BW && !SCI0_CGABWDriver::validateMode(p)) ||
-			((renderMode == Common::kRenderHercA || renderMode == Common::kRenderHercG) && !SCI0_HerculesDriver::validateMode(p)) ||
-			(renderMode == Common::kRenderPC98_8c && ((getSciVersion() <= SCI_VERSION_01 && !SCI0_PC98Gfx8ColorsDriver::validateMode(p)) ||
-			(getSciVersion() > SCI_VERSION_01 && !SCI1_PC98Gfx8ColorsDriver::validateMode(p)))) ||
-			(renderMode == Common::kRenderWin16c && getSciVersion() >= SCI_VERSION_1_1 && !WindowsGfx16ColorsDriver::validateMode(p)) ||
-			(renderMode == Common::kRenderPC98_16c && undither) ||
-			(getLanguage() == Common::KO_KOR)) // No extra modes supported for the Korean fan-patched games
-				renderMode = Common::kRenderDefault;
-
-		// Disable undithering for CGA, Hercules and other unsuitable video modes
+		Common::RenderMode renderMode = SciGfxDriver::getRenderMode();
+
+		// Disable undithering for CGA, Hercules and other unsuitable video modes. The render mode should have been set to
+		// kRenderDefault by determineRenderMode() if undithering is selected, but we want to make sure that this matches.
 		if (renderMode != Common::kRenderDefault)
 			undither = false;
 




More information about the Scummvm-git-logs mailing list