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

bluegr noreply at scummvm.org
Tue Aug 6 05:47:30 UTC 2024


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

Summary:
0aca97d8f5 COMMON: add PC-98 8-color render mode to launcher
9dc71ef701 SCI: (PC-98) - add gfx drivers for PC-98 games
8eac961aa6 SCI: (PQ2/PC-98) - make some specific gfx adjustments
3ce0915e58 SCI: make Korean fan-patched games use upscaled gfx driver
bfdb490eae SCI: fix bug 15309
b815a5e80c SCI: remove commented out code
c9c061b84e TEST: fix rendermodes test


Commit: 0aca97d8f5fd79043fbc852e5631901e7b818e84
    https://github.com/scummvm/scummvm/commit/0aca97d8f5fd79043fbc852e5631901e7b818e84
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
COMMON: add PC-98 8-color render mode to launcher

Changed paths:
    base/commandLine.cpp
    common/gui_options.cpp
    common/gui_options.h
    common/rendermode.cpp
    common/rendermode.h
    engines/kyra/detection_tables.h
    engines/kyra/engine/kyra_lok.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index f3fb5281181..554f3c03316 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -190,8 +190,8 @@ static const char HELP_STRING4[] =
 	"  --[no-]dirtyrects        Enable dirty rectangles optimisation in software renderer\n"
 	"                           (default: enabled)\n"
 	"  --render-mode=MODE       Enable additional render modes (hercGreen, hercAmber,\n"
-	"                           cga, ega, vga, amiga, fmtowns, pc9821, pc9801, 2gs,\n"
-	"                           atari, macintosh, macintoshbw)\n"
+	"                           cga, ega, vga, amiga, fmtowns, pc98-256c, pc98-16c, pc98-8c, 2gs,\n"
+	"                           atari, macintosh, macintoshbw, vgaGray)\n"
 #ifdef ENABLE_EVENTRECORDER
 	"  --record-mode=MODE       Specify record mode for event recorder (record, playback,\n"
 	"                           info, update, passthrough [default])\n"
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index 61a9209b807..854b6a1496c 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -70,8 +70,8 @@ const struct GameOpt {
 	{ GUIO_RENDERVGA,			"vga" },
 	{ GUIO_RENDERAMIGA,			"amiga" },
 	{ GUIO_RENDERFMTOWNS,		"fmtowns" },
-	{ GUIO_RENDERPC9821,		"pc9821" },
-	{ GUIO_RENDERPC9801,		"pc9801" },
+	{ GUIO_RENDERPC98_256C,		"pc98-256c" },
+	{ GUIO_RENDERPC98_16C,		"pc98-16c" },
 	{ GUIO_RENDERAPPLE2GS,		"2gs" },
 	{ GUIO_RENDERATARIST,		"atari" },
 	{ GUIO_RENDERMACINTOSH,		"macintosh" },
@@ -80,6 +80,7 @@ const struct GameOpt {
 	{ GUIO_RENDERZX,			"zx" },
 	{ GUIO_RENDERC64,			"c64" },
 	{ GUIO_RENDERVGAGREY,		"vgaGray" },
+	{ GUIO_RENDERPC98_8C,		"pc98-8c" },
 
 	{ GUIO_GAMEOPTIONS1, "gameOption1" },
 	{ GUIO_GAMEOPTIONS2, "gameOption2" },
diff --git a/common/gui_options.h b/common/gui_options.h
index 912e6f6587e..cc7065fc9e7 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -59,8 +59,8 @@
 #define GUIO_RENDERVGA			"\x1c"
 #define GUIO_RENDERAMIGA		"\x1d"
 #define GUIO_RENDERFMTOWNS		"\x1e"
-#define GUIO_RENDERPC9821		"\x1f"
-#define GUIO_RENDERPC9801		"\x20"
+#define GUIO_RENDERPC98_256C	"\x1f"
+#define GUIO_RENDERPC98_16C		"\x20"
 #define GUIO_RENDERAPPLE2GS		"\x21"
 #define GUIO_RENDERATARIST		"\x22"
 #define GUIO_RENDERMACINTOSH	"\x23"
@@ -71,6 +71,7 @@
 #define GUIO_RENDERZX	    	"\x28"
 #define GUIO_RENDERC64	    	"\x29"
 #define GUIO_RENDERVGAGREY    	"\x2A"
+#define GUIO_RENDERPC98_8C   	"\x2B"
 
 #define GUIO_LINKSPEECHTOSFX "\x30"
 #define GUIO_LINKMUSICTOSFX  "\x31"
diff --git a/common/rendermode.cpp b/common/rendermode.cpp
index 69ec47c8863..48084692424 100644
--- a/common/rendermode.cpp
+++ b/common/rendermode.cpp
@@ -43,8 +43,9 @@ const RenderModeDescription g_renderModes[] = {
 	{ "vga", "VGA", kRenderVGA },
 	{ "amiga", "Amiga", kRenderAmiga },
 	{ "fmtowns", "FM-TOWNS", kRenderFMTowns },
-	{ "pc9821", _s("PC-9821 (256 Colors)"), kRenderPC9821 },
-	{ "pc9801", _s("PC-9801 (16 Colors)"), kRenderPC9801 },
+	{ "pc98-256c", _s("PC-9821 (256 Colors)"), kRenderPC98_256c },
+	{ "pc98-16c", _s("PC-9801 (16 Colors)"), kRenderPC98_16c },
+	{ "pc98-8c", _s("PC-9801 (8 Colors)"), kRenderPC98_8c },
 	{ "2gs", "Apple IIgs", kRenderApple2GS },
 	{ "atari", "Atari ST", kRenderAtariST },
 	{ "macintosh", "Macintosh", kRenderMacintosh },
@@ -73,8 +74,8 @@ static const RenderGUIOMapping s_renderGUIOMapping[] = {
 	{ kRenderVGA,			GUIO_RENDERVGA },
 	{ kRenderAmiga,			GUIO_RENDERAMIGA },
 	{ kRenderFMTowns,		GUIO_RENDERFMTOWNS },
-	{ kRenderPC9821,		GUIO_RENDERPC9821 },
-	{ kRenderPC9801,		GUIO_RENDERPC9801 },
+	{ kRenderPC98_256c,		GUIO_RENDERPC98_256C },
+	{ kRenderPC98_16c,		GUIO_RENDERPC98_16C },
 	{ kRenderApple2GS,		GUIO_RENDERAPPLE2GS },
 	{ kRenderAtariST,		GUIO_RENDERATARIST },
 	{ kRenderMacintosh,		GUIO_RENDERMACINTOSH },
@@ -84,7 +85,8 @@ static const RenderGUIOMapping s_renderGUIOMapping[] = {
 	{ kRenderCPC,		    GUIO_RENDERCPC },
 	{ kRenderZX,			GUIO_RENDERZX },
 	{ kRenderC64,			GUIO_RENDERC64 },
-	{ kRenderVGAGrey,		GUIO_RENDERVGAGREY }
+	{ kRenderVGAGrey,		GUIO_RENDERVGAGREY },
+	{ kRenderPC98_8c,		GUIO_RENDERPC98_8C },
 };
 
 DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Hercules Green", "lowres")
diff --git a/common/rendermode.h b/common/rendermode.h
index 4a40bd347f4..65b25c69a0b 100644
--- a/common/rendermode.h
+++ b/common/rendermode.h
@@ -54,8 +54,8 @@ enum RenderMode {
 	kRenderHercA = 5,
 	kRenderAmiga = 6,
 	kRenderFMTowns = 7,
-	kRenderPC9821 = 8,
-	kRenderPC9801 = 9,
+	kRenderPC98_256c = 8,
+	kRenderPC98_16c = 9,
 	kRenderApple2GS = 10,
 	kRenderAtariST = 11,
 	kRenderMacintosh = 12,
@@ -65,7 +65,8 @@ enum RenderMode {
 	kRenderCPC = 16,
 	kRenderZX = 17,
 	kRenderC64 = 18,
-	kRenderVGAGrey = 19
+	kRenderVGAGrey = 19,
+	kRenderPC98_8c = 20
 };
 
 struct RenderModeDescription {
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 53171996895..d6e7f56beb6 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -393,7 +393,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9821, GUIO_RENDERPC9801)
+			GUIO4(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_256C, GUIO_RENDERPC98_16C)
 		},
 		KYRA1_TOWNS_SJIS_FLAGS
 	},
@@ -962,7 +962,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::EN_ANY,
 			Common::kPlatformPC98,
 			ADGF_CD,
-			GUIO3(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9821)
+			GUIO3(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_256C)
 		},
 		KYRA2_TOWNS_FLAGS
 	},
@@ -975,7 +975,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_CD,
-			GUIO3(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9821)
+			GUIO3(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_256C)
 		},
 		KYRA2_TOWNS_SJIS_FLAGS
 	},
@@ -1765,7 +1765,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO5(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_16C, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS)
 		},
 		LOL_PC9801_FLAGS
 	},
@@ -1793,7 +1793,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO6(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS, GAMEOPTION_LOL_SAVENAMES)
+			GUIO6(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_16C, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS, GAMEOPTION_LOL_SAVENAMES)
 		},
 		LOL_PC9801_FLAGS
 	},
@@ -1967,7 +1967,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO6(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP, GAMEOPTION_EOB_ADDRULES)
+			GUIO6(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC98_16C, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP, GAMEOPTION_EOB_ADDRULES)
 		},
 		EOB_PC98_FLAGS
 	},
@@ -2115,7 +2115,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO9(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_RENDERPC9821, GUIO_RENDERPC9801, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP, GAMEOPTION_EOB_ADDRULES)
+			GUIO9(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_RENDERPC98_256C, GUIO_RENDERPC98_16C, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP, GAMEOPTION_EOB_ADDRULES)
 		},
 		EOB2_PC98_FLAGS
 	},
diff --git a/engines/kyra/engine/kyra_lok.cpp b/engines/kyra/engine/kyra_lok.cpp
index 9e9106e2129..75a6a976025 100644
--- a/engines/kyra/engine/kyra_lok.cpp
+++ b/engines/kyra/engine/kyra_lok.cpp
@@ -192,7 +192,7 @@ KyraEngine_LoK::~KyraEngine_LoK() {
 }
 
 Common::Error KyraEngine_LoK::init() {
-	if (Common::parseRenderMode(ConfMan.get("render_mode")) == Common::kRenderPC9801)
+	if (Common::parseRenderMode(ConfMan.get("render_mode")) == Common::kRenderPC98_16c)
 		_screen = new Screen_LoK_16(this, _system);
 	else
 		_screen = new Screen_LoK(this, _system);


Commit: 9dc71ef7011ec964ac8c38608af0ec606025bbd3
    https://github.com/scummvm/scummvm/commit/9dc71ef7011ec964ac8c38608af0ec606025bbd3
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
SCI: (PC-98) - add gfx drivers for PC-98 games

16 colors and 8 colors modes for the SCI0 and SCI1
PC-98 games.

The 8 colors modes are new, the 16 colors mode is
basically the pre-existing mode, just with some
minor fixes (palette, font drawing, etc.).

The final goal is to get rid of the upscaling inside the
engine, but that will require more work on the other
game versions that depend on it.

Changed paths:
    engines/sci/detection_internal.h
    engines/sci/graphics/controls16.cpp
    engines/sci/graphics/cursor.cpp
    engines/sci/graphics/fontsjis.cpp
    engines/sci/graphics/gfxdrivers.cpp
    engines/sci/graphics/gfxdrivers.h
    engines/sci/graphics/screen.cpp
    engines/sci/graphics/screen.h
    engines/sci/graphics/text16.cpp
    engines/sci/sci.cpp


diff --git a/engines/sci/detection_internal.h b/engines/sci/detection_internal.h
index 6dde0a85f1f..1d4883f2fbb 100644
--- a/engines/sci/detection_internal.h
+++ b/engines/sci/detection_internal.h
@@ -137,7 +137,11 @@ static Common::String customizeGuiOptions(Common::Path gamePath, Common::String
 		{ SCI_VERSION_0_EARLY,	SCI_VERSION_1_EGA_ONLY,		"HERCMONO.DRV",		GUIO_RENDERHERCGREEN },
 		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"VGA320.DRV",		GUIO_RENDERVGA },
 		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"VGA320BW.DRV",		GUIO_RENDERVGAGREY },
-		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"EGA640.DRV",		GUIO_RENDEREGA }
+		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"EGA640.DRV",		GUIO_RENDEREGA },
+		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V16.DRV",		GUIO_RENDERPC98_16C },
+		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801VID.DRV",		GUIO_RENDERPC98_16C },
+		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V8.DRV",		GUIO_RENDERPC98_8C },
+		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V8M.DRV",		GUIO_RENDERPC98_8C }
 	};
 
 	Common::FSNode node(gamePath);
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index 87dc4936d33..a57c3f2d98c 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -32,6 +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/ports.h"
 #include "sci/graphics/paint16.h"
 #include "sci/graphics/scifont.h"
@@ -371,14 +372,23 @@ void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *tex
 		_paint16->eraseRect(rect);
 		rect.grow(-1);
 		if (!g_sci->hasMacFonts()) {
-			_text16->Box(text, languageSplitter, false, rect, alignment, fontId);
+			// The PC-98 versions set the 'show` argument here (unlike normal DOS versions).
+			_text16->Box(text, languageSplitter, _screen->gfxDriver()->driverBasedTextRendering(), rect, alignment, fontId);
 		} else {
 			_text16->macDraw(text, rect, alignment, fontId, _text16->GetFontId(), 0);
 		}
 		if (style & SCI_CONTROLS_STYLE_SELECTED) {
 			_paint16->frameRect(rect);
 		}
-		if (!getPicNotValid())
+
+		// I have checked the PC-98 versions of QFG1 and KQ5. These set all rect bounds for the
+		// screen update rect to 0 after the text drawing. So nothing gets updated on screen.
+		// Otherwise, it would just overdraw the hi-res text. I have looked at the DOS version of
+		// QFG1 for comparison. There, it copies the text box rect into the screen update rect.
+		// So this specific handling for the PC-98 versions is correct.
+		bool allowScreenUpdate = _screen->gfxDriver()->driverBasedTextRendering() ? false : true;
+
+		if (allowScreenUpdate && !getPicNotValid())
 			_paint16->bitsShow(rect);
 	} else {
 		_paint16->invertRect(rect);
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index c8e20412d3b..856a8f01bcf 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -178,12 +178,6 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 
 	_screen->gfxDriver()->replaceCursor(rawBitmap->getUnsafeDataAt(0, heightWidth * heightWidth), heightWidth, heightWidth, hotspot.x, hotspot.y, SCI_CURSOR_SCI0_TRANSPARENCYCOLOR);
 
-	if (g_system->getScreenFormat().bytesPerPixel != 1) {
-		byte buf[3*256];
-		_screen->grabPalette(buf, 0, 256);
-		CursorMan.replaceCursorPalette(buf, 0, 256);
-	}
-
 	kernelShow();
 }
 
@@ -251,15 +245,10 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
 		Common::SpanOwner<SciSpan<byte> > cursorBitmap;
 		cursorBitmap->allocate(width * height, "upscaled cursor bitmap");
 		_screen->scale2x(rawBitmap, *cursorBitmap, celInfo->width, celInfo->height);
-		CursorMan.replaceCursor(cursorBitmap->getUnsafeDataAt(0, width * height), width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
+		_screen->gfxDriver()->replaceCursor(cursorBitmap->getUnsafeDataAt(0, width * height), width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
 	} else {
 		_screen->gfxDriver()->replaceCursor(rawBitmap.getUnsafeDataAt(0, width * height), width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
 	}
-	if (g_system->getScreenFormat().bytesPerPixel != 1) {
-		byte buf[3*256];
-		_screen->grabPalette(buf, 0, 256);
-		CursorMan.replaceCursorPalette(buf, 0, 256);
-	}
 
 	kernelShow();
 
diff --git a/engines/sci/graphics/fontsjis.cpp b/engines/sci/graphics/fontsjis.cpp
index 8872f6e1352..462373cce6b 100644
--- a/engines/sci/graphics/fontsjis.cpp
+++ b/engines/sci/graphics/fontsjis.cpp
@@ -21,6 +21,7 @@
 
 #include "sci/sci.h"
 #include "sci/engine/state.h"
+#include "sci/graphics/gfxdrivers.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/scifont.h"
 #include "sci/graphics/fontsjis.h"
@@ -31,7 +32,7 @@ GfxFontSjis::GfxFontSjis(GfxScreen *screen, GuiResourceId resourceId)
 	: _resourceId(resourceId), _screen(screen) {
 	assert(resourceId != -1);
 
-	if (!_screen->getUpscaledHires())
+	if (!_screen->getUpscaledHires() && !_screen->gfxDriver()->driverBasedTextRendering())
 		error("I don't want to initialize, when not being in upscaled hires mode");
 
 	_commonFont = Graphics::FontSJIS::createFont(Common::kPlatformPC98);
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 5f7e79f60eb..b64f9710275 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -62,12 +62,12 @@ bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
 		missing += "'" + Common::String(*driverNames) + "'";
 		++driverNames;
 	}
-	warning("Driver file %s not found. Starting game in EGA mode", missing.c_str());
+	warning("Driver file %s not found. Starting game in default mode", missing.c_str());
 	return false;
 }
 
-GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDriver(screenWidth, screenHeight, 0),
-	_srcPixelSize(1), _requestRGBMode(rgbRendering), _compositeBuffer(nullptr), _currentBitmap(nullptr), _internalPalette(nullptr), _currentPalette(nullptr) {
+GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, 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) {
 	switch (g_sci->getResMan()->getViewType()) {
 	case kViewEga:
 		_numColors = 16;	// QFG PC-98 with 8 colors also reports 16 here
@@ -97,6 +97,43 @@ GfxDefaultDriver::~GfxDefaultDriver() {
 	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));
@@ -125,7 +162,7 @@ void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
 
 	if (_numColors > 16 || _pixelSize > 1) {
 		// Not needed for SCI0, except for rgb rendering
-		_currentBitmap = new byte[_screenW * _screenH * _srcPixelSize]();
+		_currentBitmap = new byte[_virtualW * _virtualH * _srcPixelSize]();
 		assert(_currentBitmap);
 		_currentPalette = new byte[256 * 3]();
 		assert(_currentPalette);
@@ -135,6 +172,22 @@ void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
 		}
 	}
 
+	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;
 }
 
@@ -143,14 +196,14 @@ void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num, bool
 	if (_pixelSize > 1) {
 		updatePalette(colors, start, num);
 		if (update)
-			copyRectToScreen(_currentBitmap, 0, 0, _screenW, 0, 0, _screenW, _screenH, palMods, palModMapping);
-		CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
+			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;
@@ -187,6 +240,13 @@ void GfxDefaultDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int
 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 {
@@ -221,6 +281,11 @@ void GfxDefaultDriver::copyCurrentPalette(byte *dest, int start, int num) const
 	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) {
@@ -239,59 +304,11 @@ void GfxDefaultDriver::updatePalette(const byte *colors, uint start, uint num) {
 		error("GfxDefaultDriver::updatePalette(): Unsupported pixel size %d", _pixelSize);
 }
 
-template <typename T> void render(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 renderMod(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::generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
-	if (palMods && palModMapping) {
-		if (_pixelSize == 2)
-			renderMod<uint16>(dst, src, pitch, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
-		else if (_pixelSize == 4)
-			renderMod<uint32>(dst, src, pitch, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
-		else
-			error("GfxDefaultDriver::generateOutput(): Unsupported pixel size %d", _pixelSize);
-	} else {
-		if (_pixelSize == 2)
-			render<uint16>(dst, src, pitch, w, h, _internalPalette);
-		else if (_pixelSize == 4)
-			render<uint32>(dst, src, pitch, w, h, _internalPalette);
-		else
-			error("GfxDefaultDriver::generateOutput(): Unsupported pixel size %d", _pixelSize);
-	}
+	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) :
@@ -346,6 +363,11 @@ void SCI0_DOSPreVGADriver::initScreen(const Graphics::PixelFormat*) {
 	_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");
@@ -363,7 +385,12 @@ void SCI0_DOSPreVGADriver::copyCurrentPalette(byte *dest, int start, int num) co
 	memcpy(dest + start * 3, _colors + start * 3, MIN<int>(num, _numColors) * 3);
 }
 
-SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering) : SCI0_DOSPreVGADriver(4, 320, 200, rgbRendering), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard) {
+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
@@ -577,7 +604,7 @@ const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
 	return result;
 }
 
-SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering) : SCI0_DOSPreVGADriver(2, 640, 400, rgbRendering), _monochromePatterns(nullptr), _earlyVersion(false) {
+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;
@@ -723,7 +750,7 @@ void SCI0_CGABWDriver::setupRenderProc() {
 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) {
+	_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;
@@ -867,7 +894,7 @@ void SCI1_VGAGreyScaleDriver::setPalette(const byte *colors, uint start, uint nu
 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) {
+	_currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _renderLine(nullptr) {
 	Common::File drv;
 	if (!drv.open(_driverFile))
 		error("SCI1_EGADriver: Failed to open '%s'", _driverFile);
@@ -1068,8 +1095,6 @@ void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotsp
 		d2 += dstPitch;
 	}
 
-	keycolor = newKeyColor;
-
 	CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
 }
 
@@ -1087,6 +1112,11 @@ void SCI1_EGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
 	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;
@@ -1101,6 +1131,532 @@ void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
 
 const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
 
+/*SCI0_MacGfxDriver::SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, rgbRendering) {
+	if (!_compositeBuffer)
+		_compositeBuffer = new byte[64 * 64 * _pixelSize]();
+	_cursorUsesScreenPalette = false;
+}
+
+SCI0_MacGfxDriver::~SCI0_MacGfxDriver() {
+}
+
+void SCI0_MacGfxDriver::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");
+}
+
+SCI1_MacGfxDriver::SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, rgbRendering) {
+	_cursorUsesScreenPalette = false;
+}
+
+SCI1_MacGfxDriver::~SCI1_MacGfxDriver() {
+}
+
+void SCI1_MacGfxDriver::initScreen(const Graphics::PixelFormat *format) {
+	GfxDefaultDriver::initScreen(format);
+	if (!_compositeBuffer)
+		_compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
+}
+
+void SCI1_MacGfxDriver::replaceCursor(const void*, uint, uint, int, int, uint32) {
+	// This is not needed for SCI1 Mac versions of games.
+	error("SCI1_MacUpscaledGfxDriver::replaceCursor(const void*, uint, uint, int, int, uint32): Not implemented");
+}
+
+void SCI1_MacGfxDriver::replaceMacCursor(const Graphics::Cursor *c) {
+	GFXDRV_ASSERT_READY;
+
+	if (!c || !c->getSurface())
+		error("SCI1_MacUpscaledGfxDriver::replaceMacCursor(): Invalid cursor");
+
+	uint16 w = c->getWidth();
+	uint16 h = c->getHeight();
+	uint16 hotX = c->getHotspotX();
+	uint16 hotY = c->getHotspotY();
+
+	scale2x<byte>(_compositeBuffer, c->getSurface(
+	h <<= 1;), w, w, h);
+	w <<= 1;
+	hotX <<= 1;
+	hotY <<= 1;
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotX, hotY, c->getKeyColor(), false, nullptr, c->getMask());
+	if (c->getPalette())
+		CursorMan.replaceCursorPalette(c->getPalette(), c->getPaletteStartIndex(), c->getPaletteCount());
+}
+*/
+
+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(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering) :
+	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr) {
+	_virtualW = screenWidth;
+	_virtualH = screenHeight;
+}
+
+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;
+
+	if (!_compositeBuffer && _scaleCursor) // We need at least a small buffer for the scaled cursor
+		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
+}
+
+void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+	GfxDefaultDriver::setPalette(colors, start, num, update, palMods, palModMapping);
+	if (_pixelSize > 1 && update)
+		updateScreen(0, 0, _screenW * _srcPixelSize, _screenH, palMods, palModMapping);
+}
+
+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;
+	src += (srcY * pitch + srcX * _srcPixelSize);
+	if (src != _currentBitmap)
+		updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
+
+	// 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.
+	byte *scb = _scaledBitmap + (destY << 1) * _screenW * _srcPixelSize + (destX << 1) * _srcPixelSize;
+	_renderScaled(scb, src, pitch, w, h);
+
+	updateScreen(destX << 1, destY << 1,  w << 1, h << 1, palMods, palModMapping);
+}
+
+void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+	GFXDRV_ASSERT_READY;
+	if (_scaleCursor) {
+		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 >>= 1;
+	res.y >>= 1;
+	return res;
+}
+
+void UpscaledGfxDriver::clearRect(const Common::Rect &r) const {
+	Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
+	GfxDriver::clearRect(r2);
+}
+
+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);
+}
+
+PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool scaleCursor, bool specialFontStyle, bool rgbRendering) : UpscaledGfxDriver(320, 200, textAlignX, scaleCursor, rgbRendering), _sci1FontStyle(specialFontStyle), _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;
+	}
+
+	// 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;
+}
+
+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 (!_sci1FontStyle)
+		return;
+
+	_renderGlyph = &renderPC98GlyphSpecial;
+
+	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
+	if (_currentBitmap == nullptr) {
+		_currentBitmap = new byte[_virtualW * _virtualH]();
+		assert(_currentBitmap);
+	}
+}
+
+SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 8, false, rgbRendering), _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;
+	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;
+	const byte *s = reinterpret_cast<const byte*>(cursor);
+	byte *d1 = _compositeBuffer;
+	uint32 newKeyColor = 0xFF;
+
+	for (uint i = 0; i < h; ++i) {
+		for (uint ii = 0; ii < w; ++ii) {
+			byte col = *s++;
+			*d1++ = (col == keycolor) ? newKeyColor : (col & 7);
+		}
+	}
+
+	CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, newKeyColor);
+}
+
+const char *SCI0_PC98Gfx8ColorsDriver::_driverFile = "9801V8M.DRV";
+
+SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 0, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
+	Common::File drv;
+	if (!drv.open(_driverFile))
+		error("SCI1_PC98Gfx8ColorsDriver: Failed to open '%s'", _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))
+		error("SCI1_PC98Gfx8ColorsDriver: Driver file '%s' unknown 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)
+		error("SCI1_PC98Gfx8ColorsDriver: Driver file '%s' unknown 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);
+	byte c[6];
+
+	while (h--) {
+		for (int i = 0; i < (w >> 2); ++i) {
+			const byte *t1 = &tbl[(src[0] << 4 | src[1]) * 6];
+			const byte *t2 = &tbl[(src[2] << 4 | src[3]) * 6];
+
+			for (int ii = 0; ii < 6; ++ii)
+				c[ii] = (t1[ii] & 0xF0) | (t2[ii] & 0x0F);
+
+			for (int ii = 0; ii < 8; ++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 += 4;
+		}
+		src += pitch;
+		d1 += dstPitch;
+		d2 += dstPitch;
+	}
+}
+
+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 SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+	UpscaledGfxDriver::initScreen(format);
+
+	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
+	if (_currentBitmap == nullptr) {
+		_currentBitmap = new byte[_virtualW * _virtualH]();
+		assert(_currentBitmap);
+	}
+
+	_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;
+
+	srcX &= ~7;
+	destX &= ~7;
+	w = (w + 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;
+	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);
+}
+
+const char *SCI1_PC98Gfx8ColorsDriver::_driverFile = "9801V8.DRV";
+
 #undef GFXDRV_ASSERT_READY
 #undef GFXDRV_ASEERT_ALIGNED
 
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 0901c53e28a..fa061db4bc5 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -22,9 +22,14 @@
 #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;
@@ -37,11 +42,14 @@ public:
 	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 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 bool supportsPalIntensity() const = 0;
+	virtual bool driverBasedTextRendering() const = 0;
 	uint16 numColors() const { return _numColors; }
 	byte pixelSize() const { return _pixelSize; }
 protected:
@@ -61,18 +69,28 @@ public:
 	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 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 _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);
 };
@@ -82,10 +100,13 @@ 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*) {}
+	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 driverBasedTextRendering() const override { return false; }
 protected:
 	void assignPalette(const byte *colors);
 	byte *_compositeBuffer;
@@ -102,7 +123,7 @@ public:
 	~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() { return checkDriver(&_driverFile, 1); }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
 private:
 	void setupRenderProc() override;
 	uint16 *_cgaPatterns;
@@ -121,7 +142,7 @@ public:
 	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
 	Common::Point getMousePos() const override;
 	void clearRect(const Common::Rect &r) const override;
-	static bool validateMode() { return checkDriver(_driverFiles, 2); }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(_driverFiles, 2); }
 private:
 	void setupRenderProc() override;
 	byte _monochromePalette[6];
@@ -140,7 +161,7 @@ public:
 	void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
 	Common::Point getMousePos() const override;
 	void clearRect(const Common::Rect &r) const override;
-	static bool validateMode() { return checkDriver(&_driverFile, 1); }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
 private:
 	void setupRenderProc() override;
 	const uint16 _centerX;
@@ -157,7 +178,7 @@ 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() { return checkDriver(&_driverFile, 1); }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
 private:
 	byte *_greyScalePalette;
 	static const char *_driverFile;
@@ -171,12 +192,15 @@ public:
 	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 clearRect(const Common::Rect &r) const override;
 	bool supportsPalIntensity() const override { return false; }
-	static bool validateMode() { return checkDriver(&_driverFile, 1); }
+	bool driverBasedTextRendering() const override { return false; }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
 private:
 	byte *_compositeBuffer;
 	byte *_currentBitmap;
@@ -191,6 +215,87 @@ private:
 	static const char *_driverFile;
 };
 
+/*class SCI0_MacGfxDriver final : public GfxDefaultDriver {
+public:
+	SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
+	~SCI0_MacGfxDriver() override;
+	void replaceMacCursor(const Graphics::Cursor *cursor) override;
+private:
+};
+
+class SCI1_MacGfxDriver final : public GfxDefaultDriver {
+public:
+	SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
+	~SCI1_MacGfxDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void replaceCursor(const void*, uint, uint, int, int, uint32) override;
+	void replaceMacCursor(const Graphics::Cursor *cursor) override;
+private:
+};*/
+
+class UpscaledGfxDriver : public GfxDefaultDriver {
+public:
+	UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 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 clearRect(const Common::Rect &r) 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:
+	void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
+	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;
+	byte *_scaledBitmap;
+private:
+	const bool _scaleCursor;
+};
+
+class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
+public:
+	PC98Gfx16ColorsDriver(int textAlignX, bool scaleCursor, bool specialFontStyle, bool rgbRendering);
+	~PC98Gfx16ColorsDriver() override;
+	void initScreen(const Graphics::PixelFormat *format) override;
+	void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
+private:
+	const byte *_convPalette;
+	const bool _sci1FontStyle;
+};
+
+class SCI0_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
+public:
+	SCI0_PC98Gfx8ColorsDriver(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;
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(&_driverFile, 1); }
+private:
+	const byte *_convPalette;
+	static const char *_driverFile;
+};
+
+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;
+	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/screen.cpp b/engines/sci/graphics/screen.cpp
index 01ccbc08a59..13abbb996af 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -38,7 +38,7 @@
 
 namespace Sci {
 
-GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _resMan(resMan) {
+GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _resMan(resMan), _hiresGlyphBuffer(nullptr) {
 
 	// Scale the screen, if needed
 	_upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
@@ -66,9 +66,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 	// Korean versions of games use hi-res font on upscaled version of the game.
 	if ((g_sci->getLanguage() == Common::KO_KOR) && (getSciVersion() <= SCI_VERSION_1_1))
 		_upscaledHires = GFX_SCREEN_UPSCALED_640x400;
-	// Japanese versions of games use hi-res font on upscaled version of the game.
-	if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
-		_upscaledHires = GFX_SCREEN_UPSCALED_640x400;
 
 	if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
 		if (getSciVersion() <= SCI_VERSION_01) {
@@ -119,7 +116,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 			_upscaledWidthMapping[i] = (i * 3) >> 1;
 		break;
 	case GFX_SCREEN_UPSCALED_640x400:
-		// Police Quest 2 and Quest For Glory on PC9801 (Japanese)
 		// Mac SCI1/1.1 with hi-res Mac fonts
 		// Korean fan translations
 		_displayWidth = _scriptWidth * 2;
@@ -174,36 +170,55 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 	bool requestRGB = enablePaletteMods || (ConfMan.hasKey("rgb_rendering") && ConfMan.getBool("rgb_rendering"));
 
 	_gfxDrv = nullptr;
-	if (getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
-		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;
-		default:
-			break;
-		}
-	} else {
-		switch (renderMode) {
-		case Common::kRenderEGA:
+	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::kRenderVGAGrey:
+		_gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
+		break;
+	case Common::kRenderPC98_8c:
+		if (getSciVersion() <= SCI_VERSION_01)
+			_gfxDrv = new SCI0_PC98Gfx8ColorsDriver(requestRGB);
+		else
+			_gfxDrv = new SCI1_PC98Gfx8ColorsDriver(requestRGB);
+		_hiresGlyphBuffer = new byte[16 * 16]();
+		break;
+	default:
+		break;
+	}
+
+	if (_gfxDrv == nullptr) {
+		switch (g_sci->getPlatform()) {
+		/*case Common::kPlatformMacintosh:
+			if (getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY)
+				_gfxDrv = new SCI0_MacGfxDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
+			else
+				_gfxDrv = new SCI1_MacGfxDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
+			break;*/
+		case Common::kPlatformPC98:
+			if (getSciVersion() <= SCI_VERSION_01)
+				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, false, requestRGB);
+			else
+				_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, requestRGB);
+			_hiresGlyphBuffer = new byte[16 * 16]();
 			break;
 		default:
+			_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
 			break;
 		}
 	}
-
-	if (_gfxDrv == nullptr)
-		_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
 	assert(_gfxDrv);
 
 	_displayPixels = _displayWidth * _displayHeight;
@@ -265,6 +280,7 @@ GfxScreen::~GfxScreen() {
 	free(_displayScreen);
 	free(_paletteMapScreen);
 	delete[] _backupScreen;
+	delete[] _hiresGlyphBuffer;
 	delete _gfxDrv;
 }
 
@@ -534,12 +550,33 @@ void GfxScreen::putHangulChar(Graphics::FontKorean *commonFont, int16 x, int16 y
 	commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1);
 }
 
-// We put hires kanji chars onto upscaled background, so we need to adjust
-// coordinates. Caller gives use low-res ones.
 void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color) {
-	byte *displayPtr = _displayScreen + y * _displayWidth * 2 + x * 2;
+	// We put hires kanji chars onto upscaled background, so we need to adjust coordinates. Caller gives use low-res ones.
+
+	// The PC-98 gfx driver's normal blitting opcode will scale everything up by 2. So that opcode does not get used for
+	// the hires glyphs.
+
+	// SCI0 interpreters don't actually render glyphs via the gfx driver. The QFG interpreter copies the glyph data directly
+	// into the video mem planes. For QFG, the glyph data gets xored with 0xff and copied into all 4 planes. So it will be
+	// black text on white background. Also, the interpreter divides the x-coordinate by 4 to find the right position in the
+	// vmem planes. It does not do any bit shifting to fix the x-coordinate. So the text will be aligned on byte boundaries
+	// in vmem which equals 4 pixel boundaries in lowres. We make that bounds adjustment in the driver, since the layout
+	// relies on it. PQ2 on the other hand uses the PC-98 text mode for text print instead of rendering it in graphics mode
+	// (many PC-98 games do that). In an emulator you can see easily recognize it, since the mouse cursor will move underneath
+	// the text. The use of the text mode has a similiar effect to x-coordinates as what happens with QFG: In text mode, the
+	// coordinates can only be set as text columns and lines, so the coordinates have to be divided and loose some precision
+	// ('& ~3' for x, and '& ~7' for y).
+
+	// SCI1 (KQ5/SQ4) has a gfx driver opcode to render the glyphs via the PC-98 GRCG. In the 16 colors drivers it uses a unique
+	// way to do that: The first 5 lines and the last 5 lines of the glyph get scaled 2x horizontally (= basically fat print), the
+	// middle 6 lines are drawn normally. It's the same for KQ5 and SQ4. This is also implemented in our on-top rendering in the
+	// driver. Unlike SCI0, the SCI1 gfx opcode for the text glyph rendering is actually able to properly x-shift the glyph data
+	// to the right x coordinate. However, my impression is that Sierra just fixed the x-bounds in the game scripts here.
+
+	memset(_hiresGlyphBuffer, 0xff, 256);
 	// we don't use outline, so color 0 is actually not used
-	commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1);
+	commonFont->drawChar(_hiresGlyphBuffer, chr, 16, 1, color, 0, -1, -1);
+	_gfxDrv->drawTextFontGlyph(_hiresGlyphBuffer, 16, x << 1, y << 1, 16, 16, 0xff, _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
 }
 
 int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 5bff720ec7f..b27804faa6e 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -234,6 +234,11 @@ private:
 	 */
 	GfxScreenUpscaledMode _upscaledHires;
 
+	/**
+	 * This buffer is used to draw a single hires font glyph.
+	 */
+	byte *_hiresGlyphBuffer;
+
 	/**
 	 * This here holds a translation for vertical+horizontal coordinates between native
 	 * (visual) and actual (display) screen.
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 764a54ce1ee..12ac67ff73b 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -595,10 +595,8 @@ void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const
 	while (*curTextPos) {
 		// We need to check for Shift-JIS every line
 		//  Police Quest 2 PC-9801 often draws English + Japanese text during the same call
-		if (g_sci->getLanguage() == Common::JA_JPN) {
-			if (SwitchToFont900OnSjis(curTextPos, languageSplitter))
-				doubleByteMode = true;
-		}
+		if (g_sci->getLanguage() == Common::JA_JPN)
+			doubleByteMode = SwitchToFont900OnSjis(curTextPos, languageSplitter);
 
 		int16 charCount = GetLongest(curTextPos, rect.width(), fontId);
 		if (charCount == 0)
@@ -646,7 +644,10 @@ void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const
 			curTextLine = textString.c_str();
 		}
 
-		if (show) {
+		// This seems to be the method used by the original PC-98 interpreters. They will set the `show`
+		// argument (only) for the SCI_CONTROLS_TYPE_TEXT, but then there is a separate code path for
+		// the SJIS characters, which will not get the screen surface update (since they get drawn directly).
+		if (show && !doubleByteMode) {
 			Show(curTextLine, 0, charCount, fontId, previousPenColor);
 		} else {
 			Draw(curTextLine, 0, charCount, fontId, previousPenColor);
@@ -657,25 +658,6 @@ void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const
 	}
 	SetFont(previousFontId);
 	_ports->penColor(previousPenColor);
-
-	if (doubleByteMode) {
-		// Kanji is written by pc98 rom to screen directly. Because of
-		// GetLongest() behavior (not cutting off the last char, that causes a
-		// new line), results in the script thinking that the text would need
-		// less space. The coordinate adjustment in fontsjis.cpp handles the
-		// incorrect centering because of that and this code actually shows all
-		// of the chars - if we don't do this, the scripts will only show most
-		// of the chars, but the last few pixels won't get shown most of the
-		// time.
-		Common::Rect kanjiRect = rect;
-		_ports->offsetRect(kanjiRect);
-		kanjiRect.left &= 0xFFC;
-		kanjiRect.right = kanjiRect.left + maxTextWidth;
-		kanjiRect.bottom = kanjiRect.top + hline;
-		kanjiRect.left *= 2; kanjiRect.right *= 2;
-		kanjiRect.top *= 2; kanjiRect.bottom *= 2;
-		_screen->copyDisplayRectToScreen(kanjiRect);
-	}
 }
 
 void GfxText16::DrawString(const Common::String &textOrig) {
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 49baa8fab72..530212e8e49 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -327,13 +327,17 @@ Common::Error SciEngine::run() {
 
 		// 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()))) ||
-			(renderMode == Common::kRenderVGAGrey && !SCI1_VGAGreyScaleDriver::validateMode()) ||
-			(renderMode == Common::kRenderCGA && !SCI0_CGADriver::validateMode()) ||
-			(renderMode == Common::kRenderCGA_BW && !SCI0_CGABWDriver::validateMode()) ||
-			((renderMode == Common::kRenderHercA || renderMode == Common::kRenderHercG) && !SCI0_HerculesDriver::validateMode()))
-			renderMode = Common::kRenderDefault;
+			(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::kRenderPC98_16c && undither))
+				renderMode = Common::kRenderDefault;
 
 		// Disable undithering for CGA and Hercules modes
 		if (renderMode != Common::kRenderDefault)


Commit: 8eac961aa6e8bfb4ee61f86892c314dc8b27ced1
    https://github.com/scummvm/scummvm/commit/8eac961aa6e8bfb4ee61f86892c314dc8b27ced1
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
SCI: (PQ2/PC-98) - make some specific gfx adjustments

PQ2 seems to be the oldest of these PC-98 ports. It
uses text mode print, a vertically scaled mouse pointer,
etc. Also, it doesn't really have a 16 colors mode.
I make the necessary adjustments here to provide
both 16 and 8 colors modes, since there is no technical
limitation not to have the 16 colors...

Changed paths:
    engines/sci/detection_internal.h
    engines/sci/graphics/gfxdrivers.cpp
    engines/sci/graphics/gfxdrivers.h
    engines/sci/graphics/screen.cpp
    engines/sci/sci.cpp


diff --git a/engines/sci/detection_internal.h b/engines/sci/detection_internal.h
index 1d4883f2fbb..94f9ff8749a 100644
--- a/engines/sci/detection_internal.h
+++ b/engines/sci/detection_internal.h
@@ -139,9 +139,10 @@ static Common::String customizeGuiOptions(Common::Path gamePath, Common::String
 		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"VGA320BW.DRV",		GUIO_RENDERVGAGREY },
 		{ SCI_VERSION_1_EARLY,	SCI_VERSION_1_1,			"EGA640.DRV",		GUIO_RENDEREGA },
 		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V16.DRV",		GUIO_RENDERPC98_16C },
-		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801VID.DRV",		GUIO_RENDERPC98_16C },
-		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V8.DRV",		GUIO_RENDERPC98_8C },
-		{ SCI_VERSION_01,		SCI_VERSION_1_LATE,			"9801V8M.DRV",		GUIO_RENDERPC98_8C }
+		{ SCI_VERSION_01,		SCI_VERSION_01,				"9801VID.DRV",		GUIO_RENDERPC98_16C },
+		{ SCI_VERSION_1_LATE,	SCI_VERSION_1_LATE,			"9801V8.DRV",		GUIO_RENDERPC98_8C },
+		{ SCI_VERSION_01,		SCI_VERSION_01,				"9801V8M.DRV",		GUIO_RENDERPC98_8C },
+		{ SCI_VERSION_01,		SCI_VERSION_01,				"9801VID.DRV",		GUIO_RENDERPC98_8C },
 	};
 
 	Common::FSNode node(gamePath);
@@ -160,7 +161,6 @@ static Common::String customizeGuiOptions(Common::Path gamePath, Common::String
 		}
 	}
 
-
 	return guiOptions;
 }
 
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index b64f9710275..963cb0ed4e3 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -1207,7 +1207,8 @@ template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w,
 }
 
 UpscaledGfxDriver::UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering) :
-	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr) {
+	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor),
+	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _fixedTextColor(-1) {
 	_virtualW = screenWidth;
 	_virtualH = screenHeight;
 }
@@ -1216,7 +1217,7 @@ UpscaledGfxDriver::~UpscaledGfxDriver() {
 	delete[] _scaledBitmap;
 }
 
-void renderGlyph(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
+void renderGlyph(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol, int) {
 	dstPitch -= w;
 	srcPitch -= w;
 
@@ -1295,7 +1296,7 @@ void UpscaledGfxDriver::drawTextFontGlyph(const byte *src, int pitch, int hiresD
 	GFXDRV_ASSERT_READY;
 	hiresDestX &= ~(_textAlignX - 1);
 	byte *scb = _scaledBitmap + hiresDestY * _screenW * _srcPixelSize + hiresDestX * _srcPixelSize;
-	_renderGlyph(scb, _screenW, src, pitch, hiresW, hiresH, transpColor);
+	_renderGlyph(scb, _screenW, src, pitch, hiresW, hiresH, transpColor, _fixedTextColor);
 	updateScreen(hiresDestX, hiresDestY, hiresW, hiresH, palMods, palModMapping);
 }
 
@@ -1315,7 +1316,8 @@ void UpscaledGfxDriver::updateScreen(int destX, int destY, int w, int h, const P
 	g_system->copyRectToScreen(buff, pitch, destX, destY, w, h);
 }
 
-PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool scaleCursor, bool specialFontStyle, bool rgbRendering) : UpscaledGfxDriver(320, 200, textAlignX, scaleCursor, rgbRendering), _sci1FontStyle(specialFontStyle), _convPalette(nullptr) {
+PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, int sjisTextModeColor, bool rgbRendering, bool needsUnditheringPalette) :
+	UpscaledGfxDriver(320, 200, textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _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[] = {
@@ -1336,13 +1338,39 @@ PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool scaleCursor, b
 		s+=3;
 	}
 
-	// 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));
+	if (sjisTextModeColor != -1) {
+		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;
+		}
+		_fixedTextColor = sjisTextModeColor + 0x10;
+	}
+
+	if (needsUnditheringPalette) {		
+		if (sjisTextModeColor != -1) {
+			// If we need the rest of the CLUT8 for the undithering, we can't use that space for the
+			// text mode colors, but this really only matters for the blue text color in PQ2. We try
+			// to relocate that color which should work for the blue color that PQ2 uses...
+			for (int i = 0; i < 16; ++i) {
+				if (col[i * 3] != col[_fixedTextColor * 3] || col[i * 3 + 1] != col[_fixedTextColor * 3 + 1] || col[i * 3 + 2] != col[_fixedTextColor * 3 + 2])
+					continue;
+				_fixedTextColor = i;
+				break;
+			}
+			if (_fixedTextColor >= 16)
+				_fixedTextColor = -1;
+		}
+
+		// 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;
@@ -1352,7 +1380,30 @@ PC98Gfx16ColorsDriver::~PC98Gfx16ColorsDriver() {
 	delete[] _convPalette;
 }
 
-void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
+void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol, int fixedCol) {
+	dstPitch -= w;
+	srcPitch -= w;
+
+	while (h--) {
+		for (int i = 0; i < w - 1; ++i) {
+			uint8 a = *src++;
+			uint8 b = *src;
+			if (a != transpCol)
+				*dst = (fixedCol == -1) ? a : fixedCol;
+			else if (b != transpCol)
+				*dst = (fixedCol == -1) ? b : fixedCol;
+			++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, int) {
 	assert(h == 16); // This is really not suitable for anything but the special SCI1 PC98 glyph drawing
 	dstPitch -= w;
 	srcPitch -= w;
@@ -1388,10 +1439,16 @@ void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPit
 void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
 
+	if (!_compositeBuffer && _cursorScaleHeightOnly) // We need at least a small buffer for the scaled cursor
+		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
+
 	assert(_convPalette);
 	GfxDefaultDriver::setPalette(_convPalette, 0, 256, true, nullptr, nullptr);
 
-	if (!_sci1FontStyle)
+	if (_fontStyle == kFontStyleFat)
+		_renderGlyph = &renderPC98GlyphFat;
+
+	if (_fontStyle != kFontStyleSpecialSCI1)
 		return;
 
 	_renderGlyph = &renderPC98GlyphSpecial;
@@ -1403,7 +1460,30 @@ void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	}
 }
 
-SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 8, false, rgbRendering), _convPalette(nullptr) {
+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)
+	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);
+}
+
+SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, SjisFontStyle sjisFontStyle, int sjisTextModeColor, bool rgbRendering) :
+	UpscaledGfxDriver(320, 200, 8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _fontStyle(sjisFontStyle), _convPalette(nullptr) {
 	byte *col = new byte[8 * 3]();
 	_convPalette = col;
 
@@ -1412,6 +1492,8 @@ SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool rgbRendering) : Upscal
 		*col++ = (i & 2) ? 0xff : 0;
 		*col++ = (i & 1) ? 0xff : 0;
 	}
+
+	_fixedTextColor = sjisTextModeColor;
 }
 
 SCI0_PC98Gfx8ColorsDriver::~SCI0_PC98Gfx8ColorsDriver() {
@@ -1441,7 +1523,11 @@ void pc98SimpleDither(byte *dst, const byte *src, int pitch, int w, int h) {
 
 void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
+	if (!_compositeBuffer) // We need at least a small buffer for the cursor
+		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
 	_renderScaled = &pc98SimpleDither;
+	if (_fontStyle == kFontStyleFat)
+		_renderGlyph = &renderPC98GlyphFat;
 	assert(_convPalette);
 	GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
 }
@@ -1449,20 +1535,36 @@ void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format)
 void SCI0_PC98Gfx8ColorsDriver::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);
-	byte *d1 = _compositeBuffer;
+	byte *d = _compositeBuffer;
 	uint32 newKeyColor = 0xFF;
 
 	for (uint i = 0; i < h; ++i) {
 		for (uint ii = 0; ii < w; ++ii) {
 			byte col = *s++;
-			*d1++ = (col == keycolor) ? newKeyColor : (col & 7);
+			*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);
 }
 
-const char *SCI0_PC98Gfx8ColorsDriver::_driverFile = "9801V8M.DRV";
+const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
 
 SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 0, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
 	Common::File drv;
@@ -1573,32 +1675,12 @@ void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, con
 	}
 }
 
-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 SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
 
+	if (!_compositeBuffer) // We need at least a small buffer for the cursor
+		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
+
 	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
 	if (_currentBitmap == nullptr) {
 		_currentBitmap = new byte[_virtualW * _virtualH]();
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index fa061db4bc5..e76e749ad87 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -247,11 +247,12 @@ public:
 	bool driverBasedTextRendering() const override { return true; }
 protected:
 	void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
-	typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int);
+	typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int, int);
 	GlyphRenderProc _renderGlyph;
 	typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
 	ScaledRenderProc _renderScaled;
 	uint16 _textAlignX;
+	int16 _fixedTextColor;
 	byte *_scaledBitmap;
 private:
 	const bool _scaleCursor;
@@ -259,26 +260,40 @@ private:
 
 class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
 public:
-	PC98Gfx16ColorsDriver(int textAlignX, bool scaleCursor, bool specialFontStyle, bool rgbRendering);
+	enum SjisFontStyle {
+		kFontStyleNone,
+		kFontStyleFat,
+		kFontStyleSpecialSCI1
+	};
+
+	PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, int sjisTextModeColor, 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;
 private:
 	const byte *_convPalette;
-	const bool _sci1FontStyle;
+	const bool _cursorScaleHeightOnly;
+	SjisFontStyle _fontStyle;
 };
 
 class SCI0_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
 public:
-	SCI0_PC98Gfx8ColorsDriver(bool rgbRendering);
+	enum SjisFontStyle {
+		kFontStyleNone,
+		kFontStyleFat
+	};
+	SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, SjisFontStyle sjisFontStyle, int sjisTextModeColor, 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;
-	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(&_driverFile, 1); }
+	static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(_driverFiles, 2); }
 private:
 	const byte *_convPalette;
-	static const char *_driverFile;
+	const bool _cursorScaleHeightOnly;
+	SjisFontStyle _fontStyle;
+	static const char *_driverFiles[2];
 };
 
 class SCI1_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 13abbb996af..e1bb35fdfbf 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -189,8 +189,12 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		_gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
 		break;
 	case Common::kRenderPC98_8c:
-		if (getSciVersion() <= SCI_VERSION_01)
-			_gfxDrv = new SCI0_PC98Gfx8ColorsDriver(requestRGB);
+		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, SCI0_PC98Gfx8ColorsDriver::kFontStyleFat, 1, requestRGB);
+		else if (getSciVersion() <= SCI_VERSION_01)
+			_gfxDrv = new SCI0_PC98Gfx8ColorsDriver(false, SCI0_PC98Gfx8ColorsDriver::kFontStyleNone, -1, requestRGB);
 		else
 			_gfxDrv = new SCI1_PC98Gfx8ColorsDriver(requestRGB);
 		_hiresGlyphBuffer = new byte[16 * 16]();
@@ -208,10 +212,18 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 				_gfxDrv = new SCI1_MacGfxDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
 			break;*/
 		case Common::kPlatformPC98:
-			if (getSciVersion() <= SCI_VERSION_01)
-				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, false, requestRGB);
+			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 SCI1 8 colors mode). So we just make the
+				// necessary adjustments.
+				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, true, PC98Gfx16ColorsDriver::kFontStyleFat, 1, requestRGB, ConfMan.getBool("disable_dithering"));
+			else if (getSciVersion() <= SCI_VERSION_01)
+				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, false, PC98Gfx16ColorsDriver::kFontStyleNone, -1, requestRGB, true);
 			else
-				_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, requestRGB);
+				_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, -1, requestRGB, true);
 			_hiresGlyphBuffer = new byte[16 * 16]();
 			break;
 		default:
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 530212e8e49..e937ed08d52 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -339,7 +339,7 @@ Common::Error SciEngine::run() {
 			(renderMode == Common::kRenderPC98_16c && undither))
 				renderMode = Common::kRenderDefault;
 
-		// Disable undithering for CGA and Hercules modes
+		// Disable undithering for CGA, Hercules and other unsuitable video modes
 		if (renderMode != Common::kRenderDefault)
 			undither = false;
 


Commit: 3ce0915e584f7515513b73a6703a9ba246c1a9d4
    https://github.com/scummvm/scummvm/commit/3ce0915e584f7515513b73a6703a9ba246c1a9d4
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
SCI: make Korean fan-patched games use upscaled gfx driver

Next step to get rid of upscaling code in the engine.
Tested with SQ4. Since all these fan-patched versions
seem to work the same way, I am optimistic that the
other games run fine, too. Unfortunately, it is difficult
to obtain these fan patches. I only found one for SQ4.

Changed paths:
    engines/sci/graphics/gfxdrivers.cpp
    engines/sci/graphics/gfxdrivers.h
    engines/sci/graphics/screen.cpp


diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 963cb0ed4e3..cb0ae898230 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -1207,8 +1207,8 @@ template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w,
 }
 
 UpscaledGfxDriver::UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering) :
-	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor),
-	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _fixedTextColor(-1) {
+	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
+	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _fixedTextColor(-1), _cursorWidth(0), _cursorHeight(0) {
 	_virtualW = screenWidth;
 	_virtualH = screenHeight;
 }
@@ -1245,9 +1245,6 @@ void UpscaledGfxDriver::initScreen(const Graphics::PixelFormat *format) {
 	assert((_srcPixelSize >> 1) < ARRAYSIZE(scaledRenderProcs));
 	_renderScaled = scaledRenderProcs[_srcPixelSize >> 1];
 	_renderGlyph = &renderGlyph;
-
-	if (!_compositeBuffer && _scaleCursor) // We need at least a small buffer for the scaled cursor
-		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
 }
 
 void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
@@ -1273,6 +1270,7 @@ void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, in
 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 {
@@ -1316,6 +1314,21 @@ void UpscaledGfxDriver::updateScreen(int destX, int destY, int w, int h, const P
 	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;
+	}
+}
+
 PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, int sjisTextModeColor, bool rgbRendering, bool needsUnditheringPalette) :
 	UpscaledGfxDriver(320, 200, textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _fontStyle(sjisFontStyle), _cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
 	// Palette taken from driver file (identical for all versions of the
@@ -1439,9 +1452,6 @@ void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPit
 void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
 
-	if (!_compositeBuffer && _cursorScaleHeightOnly) // We need at least a small buffer for the scaled cursor
-		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
-
 	assert(_convPalette);
 	GfxDefaultDriver::setPalette(_convPalette, 0, 256, true, nullptr, nullptr);
 
@@ -1468,6 +1478,7 @@ void PC98Gfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, in
 	}
 
 	// 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;
 
@@ -1523,8 +1534,6 @@ void pc98SimpleDither(byte *dst, const byte *src, int pitch, int w, int h) {
 
 void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
-	if (!_compositeBuffer) // We need at least a small buffer for the cursor
-		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
 	_renderScaled = &pc98SimpleDither;
 	if (_fontStyle == kFontStyleFat)
 		_renderGlyph = &renderPC98GlyphFat;
@@ -1534,6 +1543,7 @@ void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format)
 
 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;
@@ -1566,7 +1576,7 @@ void SCI0_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h
 
 const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
 
-SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 0, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
+SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
 	Common::File drv;
 	if (!drv.open(_driverFile))
 		error("SCI1_PC98Gfx8ColorsDriver: Failed to open '%s'", _driverFile);
@@ -1678,9 +1688,6 @@ void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, con
 void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
 
-	if (!_compositeBuffer) // We need at least a small buffer for the cursor
-		_compositeBuffer = new byte[40 * 40 * _srcPixelSize]();
-
 	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
 	if (_currentBitmap == nullptr) {
 		_currentBitmap = new byte[_virtualW * _virtualH]();
@@ -1712,6 +1719,7 @@ void SCI1_PC98Gfx8ColorsDriver::copyRectToScreen(const byte *src, int srcX, int
 
 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;
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index e76e749ad87..b2e449833d6 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -247,6 +247,7 @@ public:
 	bool driverBasedTextRendering() const override { return true; }
 protected:
 	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, int);
 	GlyphRenderProc _renderGlyph;
 	typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
@@ -256,6 +257,9 @@ protected:
 	byte *_scaledBitmap;
 private:
 	const bool _scaleCursor;
+	uint16 _cursorWidth;
+	uint16 _cursorHeight;
+	bool _needCursorBuffer;
 };
 
 class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index e1bb35fdfbf..1a07b3e3965 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -63,10 +63,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		}
 	}
 
-	// Korean versions of games use hi-res font on upscaled version of the game.
-	if ((g_sci->getLanguage() == Common::KO_KOR) && (getSciVersion() <= SCI_VERSION_1_1))
-		_upscaledHires = GFX_SCREEN_UPSCALED_640x400;
-
 	if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
 		if (getSciVersion() <= SCI_VERSION_01) {
 			// Macintosh SCI0 games used 480x300, while the scripts were running at 320x200
@@ -117,7 +113,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		break;
 	case GFX_SCREEN_UPSCALED_640x400:
 		// Mac SCI1/1.1 with hi-res Mac fonts
-		// Korean fan translations
 		_displayWidth = _scriptWidth * 2;
 		_displayHeight = _scriptHeight * 2;
 		for (int i = 0; i <= _scriptHeight; i++)
@@ -182,11 +177,14 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		_gfxDrv = new SCI0_HerculesDriver(renderMode == Common::kRenderHercG ? 0x66ff66 : 0xffbf66, requestRGB, false);
 		break;
 	case Common::kRenderEGA:
-		if (getSciVersion() > SCI_VERSION_1_EGA_ONLY)
+		// No support for this mode in the Korean version yet.
+		if (getSciVersion() > SCI_VERSION_1_EGA_ONLY && g_sci->getLanguage() != Common::KO_KOR)
 			_gfxDrv = new SCI1_EGADriver(requestRGB);
 		break;
 	case Common::kRenderVGAGrey:
-		_gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
+		// No support for this mode in the Korean version yet.
+		if (g_sci->getLanguage() != Common::KO_KOR)
+			_gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
 		break;
 	case Common::kRenderPC98_8c:
 		if (g_sci->getGameId() == GID_PQ2)
@@ -224,15 +222,21 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 				_gfxDrv = new PC98Gfx16ColorsDriver(8, false, false, PC98Gfx16ColorsDriver::kFontStyleNone, -1, requestRGB, true);
 			else
 				_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, -1, requestRGB, true);
-			_hiresGlyphBuffer = new byte[16 * 16]();
 			break;
 		default:
-			_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
+			if (g_sci->getLanguage() == Common::KO_KOR)
+				_gfxDrv = new UpscaledGfxDriver(_displayWidth, _displayHeight + extraHeight, 1, true, requestRGB);
+			else
+				_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
 			break;
 		}
 	}
 	assert(_gfxDrv);
 
+	// Buffer for rendering a single two-byte character
+	if (_gfxDrv->driverBasedTextRendering())
+		_hiresGlyphBuffer = new byte[16 * 16]();
+
 	_displayPixels = _displayWidth * _displayHeight;
 
 	// Allocate visual, priority, control and display screen
@@ -554,36 +558,40 @@ void GfxScreen::putMacChar(const Graphics::Font *commonFont, int16 x, int16 y, u
 	commonFont->drawChar(&_displayScreenSurface, chr, x, y, color);
 }
 
-// We put hires hangul chars onto upscaled background, so we need to adjust
-// coordinates. Caller gives use low-res ones.
 void GfxScreen::putHangulChar(Graphics::FontKorean *commonFont, int16 x, int16 y, uint16 chr, byte color) {
-	byte *displayPtr = _displayScreen + y * _displayWidth * 2 + x * 2;
+	// We put hires Hangul chars onto upscaled background, so we need to adjust coordinates. Caller coordinates are
+	// low-res ones. Same magic as for the Japanese SJIS characters...
+	memset(_hiresGlyphBuffer, 0xff, 256);
 	// we don't use outline, so color 0 is actually not used
-	commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1);
+	uint16 charWidth = commonFont->getCharWidth(chr);
+	commonFont->drawChar(_hiresGlyphBuffer, chr, charWidth, 1, color, 0, -1, -1);
+	_gfxDrv->drawTextFontGlyph(_hiresGlyphBuffer, charWidth, x << 1, y << 1, charWidth, commonFont->getFontHeight(), 0xff, _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
 }
 
 void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color) {
-	// We put hires kanji chars onto upscaled background, so we need to adjust coordinates. Caller gives use low-res ones.
+	// We put hires SJIS ROM chars onto upscaled background, so we need to adjust coordinates. Caller coordinates are
+	// low-res ones.
 
 	// The PC-98 gfx driver's normal blitting opcode will scale everything up by 2. So that opcode does not get used for
 	// the hires glyphs.
 
-	// SCI0 interpreters don't actually render glyphs via the gfx driver. The QFG interpreter copies the glyph data directly
-	// into the video mem planes. For QFG, the glyph data gets xored with 0xff and copied into all 4 planes. So it will be
-	// black text on white background. Also, the interpreter divides the x-coordinate by 4 to find the right position in the
-	// vmem planes. It does not do any bit shifting to fix the x-coordinate. So the text will be aligned on byte boundaries
-	// in vmem which equals 4 pixel boundaries in lowres. We make that bounds adjustment in the driver, since the layout
-	// relies on it. PQ2 on the other hand uses the PC-98 text mode for text print instead of rendering it in graphics mode
-	// (many PC-98 games do that). In an emulator you can see easily recognize it, since the mouse cursor will move underneath
-	// the text. The use of the text mode has a similiar effect to x-coordinates as what happens with QFG: In text mode, the
-	// coordinates can only be set as text columns and lines, so the coordinates have to be divided and loose some precision
-	// ('& ~3' for x, and '& ~7' for y).
-
-	// SCI1 (KQ5/SQ4) has a gfx driver opcode to render the glyphs via the PC-98 GRCG. In the 16 colors drivers it uses a unique
-	// way to do that: The first 5 lines and the last 5 lines of the glyph get scaled 2x horizontally (= basically fat print), the
-	// middle 6 lines are drawn normally. It's the same for KQ5 and SQ4. This is also implemented in our on-top rendering in the
-	// driver. Unlike SCI0, the SCI1 gfx opcode for the text glyph rendering is actually able to properly x-shift the glyph data
-	// to the right x coordinate. However, my impression is that Sierra just fixed the x-bounds in the game scripts here.
+	// SCI0 PC-98 interpreters don't actually render SJIS ROM glyphs via the gfx driver. The QFG interpreter copies the
+	// glyph data directly into the video mem planes. For QFG, the glyph data gets xored with 0xff and copied into all 4
+	// planes. So it will be black text on white background. Also, the interpreter divides the x-coordinate by 4 to find
+	// the right position in the vmem planes. It does not do any bit shifting to fix the x-coordinate. So the text will
+	// be aligned on byte boundaries in vmem which equals 4 pixel boundaries in lowres. We make that bounds adjustment
+	// in the driver, since the layout relies on it. PQ2 on the other hand uses the PC-98 text mode for text print
+	// instead of rendering it in graphics mode (many PC-98 games do that). In an emulator you can see easily recognize
+	// it, since the mouse cursor will move underneath the text. The use of the text mode has a similiar effect to
+	// x-coordinates as what happens with QFG: In text mode, the coordinates can only be set as text columns and lines,
+	// so the coordinates have to be divided and loose some precision ('& ~3' for x, and '& ~7' for y).
+
+	// SCI1 PC-98 (KQ5/SQ4) has a gfx driver opcode to render the glyphs via the PC-98 GRCG. In the 16 colors drivers it
+	// uses a unique way to do that: The first 5 lines and the last 5 lines of the glyph get scaled 2x horizontally
+	// (= basically fat print), the middle 6 lines are drawn normally. It's the same for KQ5 and SQ4. This is also
+	// implemented in our on-top rendering in the driver. Unlike SCI0, the SCI1 gfx opcode for the text glyph rendering
+	// is actually able to properly x-shift the glyph data to the right x coordinate. However, my impression is that
+	// Sierra just fixed the x-bounds in the game scripts here.
 
 	memset(_hiresGlyphBuffer, 0xff, 256);
 	// we don't use outline, so color 0 is actually not used


Commit: bfdb490eaede93fd3d54c80d2e03eff0c7febf79
    https://github.com/scummvm/scummvm/commit/bfdb490eaede93fd3d54c80d2e03eff0c7febf79
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
SCI: fix bug 15309

(KQ1 SCI keeps crashing on recent dev builds)

This is about a missing bitmap buffer. It isn't always needed
for SCI0, but SCI_01 (= KQ1) requires it. For the PC-98 targets
(which are also SCI_01) this had already been fixed in this PR,
but not for other 01 targets.

Changed paths:
    engines/sci/graphics/gfxdrivers.cpp
    engines/sci/graphics/gfxdrivers.h
    engines/sci/graphics/screen.cpp


diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index cb0ae898230..c1b6c7f635e 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -66,8 +66,8 @@ bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
 	return false;
 }
 
-GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, 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) {
+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
@@ -160,10 +160,16 @@ void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
 		assert(_compositeBuffer);
 	}
 
-	if (_numColors > 16 || _pixelSize > 1) {
-		// Not needed for SCI0, except for rgb rendering
+	// 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) {
@@ -256,7 +262,7 @@ void GfxDefaultDriver::copyCurrentBitmap(byte *dest, uint32 size) const {
 
 	// 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::copyDataFromCurrentBitmap(): unexpected call");
+		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
@@ -869,7 +875,7 @@ void SCI0_HerculesDriver::setupRenderProc() {
 const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
 
 
-SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, rgbRendering), _greyScalePalette(nullptr) {
+SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, false, rgbRendering), _greyScalePalette(nullptr) {
 	_greyScalePalette = new byte[_numColors * 3]();
 }
 
@@ -1131,7 +1137,7 @@ void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
 
 const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
 
-/*SCI0_MacGfxDriver::SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, rgbRendering) {
+SCI0_MacGfxDriver::SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, true, rgbRendering) {
 	if (!_compositeBuffer)
 		_compositeBuffer = new byte[64 * 64 * _pixelSize]();
 	_cursorUsesScreenPalette = false;
@@ -1145,7 +1151,7 @@ void SCI0_MacGfxDriver::replaceMacCursor(const Graphics::Cursor*) {
 	error("SCI0_DOSPreVGADriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
 }
 
-SCI1_MacGfxDriver::SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, rgbRendering) {
+SCI1_MacGfxDriver::SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, false, rgbRendering) {
 	_cursorUsesScreenPalette = false;
 }
 
@@ -1207,7 +1213,7 @@ template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w,
 }
 
 UpscaledGfxDriver::UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering) :
-	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
+	GfxDefaultDriver(screenWidth << 1, screenHeight << 1, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
 	_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _fixedTextColor(-1), _cursorWidth(0), _cursorHeight(0) {
 	_virtualW = screenWidth;
 	_virtualH = screenHeight;
@@ -1462,12 +1468,6 @@ void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 		return;
 
 	_renderGlyph = &renderPC98GlyphSpecial;
-
-	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
-	if (_currentBitmap == nullptr) {
-		_currentBitmap = new byte[_virtualW * _virtualH]();
-		assert(_currentBitmap);
-	}
 }
 
 void PC98Gfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
@@ -1688,12 +1688,6 @@ void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, con
 void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
 	UpscaledGfxDriver::initScreen(format);
 
-	// Only needed for SCI1 and for RGB rendering. For 16 colors games the base class driver will usually not create this.
-	if (_currentBitmap == nullptr) {
-		_currentBitmap = new byte[_virtualW * _virtualH]();
-		assert(_currentBitmap);
-	}
-
 	_renderGlyph = &renderPC98GlyphFat;
 
 	assert(_convPalette);
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index b2e449833d6..9a35023791a 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -63,7 +63,7 @@ protected:
 
 class GfxDefaultDriver : public GfxDriver {
 public:
-	GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
+	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;
@@ -86,6 +86,7 @@ protected:
 	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;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 1a07b3e3965..c0158e50280 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -226,8 +226,8 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 		default:
 			if (g_sci->getLanguage() == Common::KO_KOR)
 				_gfxDrv = new UpscaledGfxDriver(_displayWidth, _displayHeight + extraHeight, 1, true, requestRGB);
-			else
-				_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, 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;
 		}
 	}


Commit: b815a5e80c78404487d4c963b1c1994dc3378f51
    https://github.com/scummvm/scummvm/commit/b815a5e80c78404487d4c963b1c1994dc3378f51
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
SCI: remove commented out code

Changed paths:
    engines/sci/graphics/gfxdrivers.cpp
    engines/sci/graphics/gfxdrivers.h
    engines/sci/graphics/screen.cpp


diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index c1b6c7f635e..afd610a7ea1 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -1137,61 +1137,6 @@ void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
 
 const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
 
-SCI0_MacGfxDriver::SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, true, rgbRendering) {
-	if (!_compositeBuffer)
-		_compositeBuffer = new byte[64 * 64 * _pixelSize]();
-	_cursorUsesScreenPalette = false;
-}
-
-SCI0_MacGfxDriver::~SCI0_MacGfxDriver() {
-}
-
-void SCI0_MacGfxDriver::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");
-}
-
-SCI1_MacGfxDriver::SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDefaultDriver(screenWidth, screenHeight, false, rgbRendering) {
-	_cursorUsesScreenPalette = false;
-}
-
-SCI1_MacGfxDriver::~SCI1_MacGfxDriver() {
-}
-
-void SCI1_MacGfxDriver::initScreen(const Graphics::PixelFormat *format) {
-	GfxDefaultDriver::initScreen(format);
-	if (!_compositeBuffer)
-		_compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
-}
-
-void SCI1_MacGfxDriver::replaceCursor(const void*, uint, uint, int, int, uint32) {
-	// This is not needed for SCI1 Mac versions of games.
-	error("SCI1_MacUpscaledGfxDriver::replaceCursor(const void*, uint, uint, int, int, uint32): Not implemented");
-}
-
-void SCI1_MacGfxDriver::replaceMacCursor(const Graphics::Cursor *c) {
-	GFXDRV_ASSERT_READY;
-
-	if (!c || !c->getSurface())
-		error("SCI1_MacUpscaledGfxDriver::replaceMacCursor(): Invalid cursor");
-
-	uint16 w = c->getWidth();
-	uint16 h = c->getHeight();
-	uint16 hotX = c->getHotspotX();
-	uint16 hotY = c->getHotspotY();
-
-	scale2x<byte>(_compositeBuffer, c->getSurface(
-	h <<= 1;), w, w, h);
-	w <<= 1;
-	hotX <<= 1;
-	hotY <<= 1;
-
-	CursorMan.replaceCursor(_compositeBuffer, w, h, hotX, hotY, c->getKeyColor(), false, nullptr, c->getMask());
-	if (c->getPalette())
-		CursorMan.replaceCursorPalette(c->getPalette(), c->getPaletteStartIndex(), c->getPaletteCount());
-}
-*/
-
 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;
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 9a35023791a..b03d2eed74a 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -216,24 +216,6 @@ private:
 	static const char *_driverFile;
 };
 
-/*class SCI0_MacGfxDriver final : public GfxDefaultDriver {
-public:
-	SCI0_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
-	~SCI0_MacGfxDriver() override;
-	void replaceMacCursor(const Graphics::Cursor *cursor) override;
-private:
-};
-
-class SCI1_MacGfxDriver final : public GfxDefaultDriver {
-public:
-	SCI1_MacGfxDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
-	~SCI1_MacGfxDriver() override;
-	void initScreen(const Graphics::PixelFormat *format) override;
-	void replaceCursor(const void*, uint, uint, int, int, uint32) override;
-	void replaceMacCursor(const Graphics::Cursor *cursor) override;
-private:
-};*/
-
 class UpscaledGfxDriver : public GfxDefaultDriver {
 public:
 	UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering);
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index c0158e50280..2def51d1ab4 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -203,12 +203,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
 
 	if (_gfxDrv == nullptr) {
 		switch (g_sci->getPlatform()) {
-		/*case Common::kPlatformMacintosh:
-			if (getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY)
-				_gfxDrv = new SCI0_MacGfxDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
-			else
-				_gfxDrv = new SCI1_MacGfxDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
-			break;*/
 		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,


Commit: c9c061b84eeeb17a8ef6624b2dcdeb45597812c2
    https://github.com/scummvm/scummvm/commit/c9c061b84eeeb17a8ef6624b2dcdeb45597812c2
Author: athrxx (athrxx at scummvm.org)
Date: 2024-08-06T08:47:25+03:00

Commit Message:
TEST: fix rendermodes test

Changed paths:
    test/common/rendermode.h


diff --git a/test/common/rendermode.h b/test/common/rendermode.h
index b96ae243a59..0814a951cac 100644
--- a/test/common/rendermode.h
+++ b/test/common/rendermode.h
@@ -19,8 +19,8 @@ class RenderModeTestSuite : public CxxTest::TestSuite {
 		TS_ASSERT_EQUALS(Common::parseRenderMode("ega"), Common::kRenderEGA);
 		TS_ASSERT_EQUALS(Common::parseRenderMode("Vga"), Common::kRenderVGA);
 		TS_ASSERT_EQUALS(Common::parseRenderMode("AmigA"), Common::kRenderAmiga);
-		TS_ASSERT_EQUALS(Common::parseRenderMode("pc9821"), Common::kRenderPC9821);
-		TS_ASSERT_EQUALS(Common::parseRenderMode("PC9801"), Common::kRenderPC9801);
+		TS_ASSERT_EQUALS(Common::parseRenderMode("pc98-256c"), Common::kRenderPC98_256c);
+		TS_ASSERT_EQUALS(Common::parseRenderMode("PC98-16c"), Common::kRenderPC98_16c);
 		TS_ASSERT_EQUALS(Common::parseRenderMode("0"), Common::kRenderDefault);
 	}
 
@@ -38,8 +38,8 @@ class RenderModeTestSuite : public CxxTest::TestSuite {
 		TS_ASSERT_EQUALS(Common::parseRenderMode("\t"), Common::kRenderDefault);
 		// This is the only interesting bit: if the function was really, really
 		// broken it could be tempted to test for +-0x20.
-		TS_ASSERT_EQUALS(Common::parseRenderMode("pc Y8 21 "), Common::kRenderDefault);
-		TS_ASSERT_EQUALS(Common::parseRenderMode(" PC\t9801 "), Common::kRenderDefault);
+		TS_ASSERT_EQUALS(Common::parseRenderMode("pc Y8 -256c "), Common::kRenderDefault);
+		TS_ASSERT_EQUALS(Common::parseRenderMode(" PC\t98-16c "), Common::kRenderDefault);
 		TS_ASSERT_EQUALS(Common::parseRenderMode("0"), Common::kRenderDefault);
 	}
 
@@ -53,8 +53,8 @@ class RenderModeTestSuite : public CxxTest::TestSuite {
 		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("vga")), "vga", 3);
 		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("Ega")), "ega", 3);
 		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("AmiGa")), "amiga", 5);
-		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("PC9821")), "pc9821", 6);
-		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("PC9801")), "pc9801", 6);
+		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("PC98-256C")), "pc98-256c", 9);
+		TS_ASSERT_SAME_DATA(Common::getRenderModeCode(Common::parseRenderMode("PC98-16C")), "pc98-16c", 8);
 		// Slightly more interesting:
 		// Make sure that we get a null pointer for 0 (and not the "0" string or stuff)
 		char *null_p = 0;
@@ -73,8 +73,8 @@ class RenderModeTestSuite : public CxxTest::TestSuite {
 		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderVGA), GUIO_RENDERVGA);
 		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderAmiga), GUIO_RENDERAMIGA);
 		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderFMTowns), GUIO_RENDERFMTOWNS);
-		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderPC9821), GUIO_RENDERPC9821);
-		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderPC9801), GUIO_RENDERPC9801);
+		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderPC98_256c), GUIO_RENDERPC98_256C);
+		TS_ASSERT_EQUALS(Common::renderMode2GUIO(Common::kRenderPC98_16c), GUIO_RENDERPC98_16C);
 		// renderMode2GUIO is supposed to return an empty string
 		// if given kRenderDefault as an argument
 		Common::String empty;




More information about the Scummvm-git-logs mailing list