[Scummvm-git-logs] scummvm master -> 975ba55f8b7156fe821b24e822263fa1cc724725
bluegr
noreply at scummvm.org
Thu Jul 11 05:02:55 UTC 2024
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
fc1bc6a9fd SCI: move rgb and custom palette rendering into GfxDriver classes
bc6573237d SCI: add SCI 1 video mode detection for EGA and VGA grey scale
975ba55f8b SCI: add SCI 1 video modes for EGA and VGA grey scale
Commit: fc1bc6a9fd7018c74b4875d26ea43952beb1f427
https://github.com/scummvm/scummvm/commit/fc1bc6a9fd7018c74b4875d26ea43952beb1f427
Author: athrxx (athrxx at scummvm.org)
Date: 2024-07-11T08:02:49+03:00
Commit Message:
SCI: move rgb and custom palette rendering into GfxDriver classes
This enables the rgb rendering for all video modes and cleans up the
engine a lot, since the engine can now be more or less agnostic of the
screen pixel format.
Changed paths:
engines/sci/engine/kvideo.cpp
engines/sci/graphics/gfxdrivers.cpp
engines/sci/graphics/gfxdrivers.h
engines/sci/graphics/palette.cpp
engines/sci/graphics/screen.cpp
engines/sci/graphics/screen.h
engines/sci/sci.cpp
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 74372731931..5423a6e24ac 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -19,11 +19,11 @@
*
*/
-#include "engines/util.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/graphics/helpers.h"
#include "sci/graphics/cursor.h"
+#include "sci/graphics/gfxdrivers.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/screen.h"
#include "sci/util.h"
@@ -87,9 +87,9 @@ void playVideo(Video::VideoDecoder &videoDecoder) {
const SciSpan<const byte> input((const byte *)frame->getPixels(), frame->w * frame->h * bytesPerPixel);
// TODO: Probably should do aspect ratio correction in KQ6
g_sci->_gfxScreen->scale2x(input, *scaleBuffer, videoDecoder.getWidth(), videoDecoder.getHeight(), bytesPerPixel);
- g_sci->_gfxScreen->copyVideoFrameToScreen(scaleBuffer->getUnsafeDataAt(0, pitch * height), pitch, rect, bytesPerPixel == 1);
+ g_sci->_gfxScreen->copyVideoFrameToScreen(scaleBuffer->getUnsafeDataAt(0, pitch * height), pitch, rect);
} else {
- g_sci->_gfxScreen->copyVideoFrameToScreen((const byte *)frame->getPixels(), frame->pitch, rect, bytesPerPixel == 1);
+ g_sci->_gfxScreen->copyVideoFrameToScreen((const byte *)frame->getPixels(), frame->pitch, rect);
}
if (videoDecoder.hasDirtyPalette()) {
@@ -122,9 +122,6 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
if (reshowCursor)
g_sci->_gfxCursor->kernelHide();
- uint16 screenWidth = g_system->getWidth();
- uint16 screenHeight = g_system->getHeight();
-
Common::ScopedPtr<Video::VideoDecoder> videoDecoder;
bool switchedGraphicsMode = false;
@@ -143,7 +140,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
for (it = supportedFormats.begin(); it != supportedFormats.end(); ++it) {
if (it->bytesPerPixel == 2) {
const Graphics::PixelFormat format = *it;
- initGraphics(screenWidth, screenHeight, &format);
+ g_sci->_gfxScreen->gfxDriver()->initScreen(&format);
switchedGraphicsMode = true;
break;
}
@@ -199,7 +196,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
// HACK: Switch back to 8bpp if we played a true color video.
// We also won't be copying the screen to the SCI screen...
if (switchedGraphicsMode)
- initGraphics(screenWidth, screenHeight);
+ g_sci->_gfxScreen->gfxDriver()->initScreen();
else if (is8bit) {
g_sci->_gfxScreen->kernelSyncWithFramebuffer();
g_sci->_gfxPalette16->kernelSyncScreenPalette();
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 0eb323ffbdc..4e69a8bc6c4 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -22,25 +22,54 @@
#include "common/events.h"
#include "common/system.h"
+#include "engines/util.h"
+
#include "graphics/cursorman.h"
#include "graphics/paletteman.h"
#include "sci/graphics/gfxdrivers.h"
#include "sci/resource/resource.h"
#include "sci/sci.h"
-#include "sci/version.h"
namespace Sci {
+#define GFXDRV_ASSERT_READY \
+ if (!_ready) \
+ error("%s: initScreen() must be called before using this method", __FUNCTION__)
+#define GFXDRV_ASSERT_ALIGNED \
+ assert(!(w & _hAlign) && !(x & _hAlign))
+
Common::Point GfxDriver::getMousePos() const {
return g_system->getEventManager()->getMousePos();
}
void GfxDriver::clearRect(const Common::Rect &r) const {
+ GFXDRV_ASSERT_READY;
g_system->fillScreen(r, 0);
}
-GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight) : GfxDriver(screenWidth, screenHeight, 0, 1) {
+void GfxDriver::copyCurrentPalette(byte *dest, int start, int num) const {
+ assert(dest);
+ assert(start + num <= 256);
+ g_system->getPaletteManager()->grabPalette(dest, start, num);
+}
+
+bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
+ Common::String missing;
+ while (listSize-- && *driverNames) {
+ if (Common::File::exists(*driverNames))
+ return true;
+ if (!missing.empty())
+ missing += " or ";
+ missing += "'" + Common::String(*driverNames) + "'";
+ ++driverNames;
+ }
+ warning("Driver file %s not found. Starting game in EGA mode", missing.c_str());
+ return false;
+}
+
+GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering) : GfxDriver(screenWidth, screenHeight, 0, 1),
+ _srcPixelSize(1), _requestRGBMode(rgbRendering), _compositeBuffer(nullptr), _currentBitmap(nullptr), _internalPalette(nullptr), _currentPalette(nullptr) {
switch (g_sci->getResMan()->getViewType()) {
case kViewEga:
_numColors = 16; // QFG PC-98 with 8 colors also reports 16 here
@@ -63,52 +92,277 @@ GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight) : Gf
error("GfxDefaultDriver: Unknown view type");
}
-void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num) {
- g_system->getPaletteManager()->setPalette(colors, start, num);
+GfxDefaultDriver::~GfxDefaultDriver() {
+ delete[] _compositeBuffer;
+ delete[] _currentBitmap;
+ delete[] _internalPalette;
+ delete[] _currentPalette;
+}
+
+void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
+ Graphics::PixelFormat format8bt(Graphics::PixelFormat::createFormatCLUT8());
+ initGraphics(_screenW, _screenH, srcRGBFormat ? srcRGBFormat : (_requestRGBMode ? nullptr : &format8bt));
+ _format = g_system->getScreenFormat();
+
+ int srcPixelSize = srcRGBFormat ? _format.bytesPerPixel : 1;
+ if (srcPixelSize != _srcPixelSize || _pixelSize != _format.bytesPerPixel) {
+ delete[] _compositeBuffer;
+ delete[] _currentBitmap;
+ delete[] _internalPalette;
+ delete[] _currentPalette;
+ _compositeBuffer = _currentBitmap = _internalPalette = _currentPalette = nullptr;
+ }
+
+ _pixelSize = _format.bytesPerPixel;
+ _srcPixelSize = srcPixelSize;
+
+ if (_requestRGBMode && _pixelSize == 1)
+ warning("GfxDefaultDriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+ if (_pixelSize != _srcPixelSize) {
+ uint32 bufferSize = _screenW * _screenH * _pixelSize;
+ _compositeBuffer = new byte[bufferSize]();
+ assert(_compositeBuffer);
+ }
+
+ if (_numColors > 16 || _pixelSize > 1) {
+ // Not needed for SCI0, except for rgb rendering
+ _currentBitmap = new byte[_screenW * _screenH * _srcPixelSize]();
+ assert(_currentBitmap);
+ _currentPalette = new byte[256 * 3]();
+ assert(_currentPalette);
+ if (_pixelSize != _srcPixelSize) {
+ _internalPalette = new byte[256 * _pixelSize]();
+ assert(_internalPalette);
+ }
+ }
+
+ _ready = true;
+}
+
+void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+ GFXDRV_ASSERT_READY;
+ if (_pixelSize > 1) {
+ updatePalette(colors, start, num, (_srcPixelSize == _pixelSize) || (palMods != nullptr && palModMapping != nullptr));
+ if (update)
+ copyRectToScreen(_currentBitmap, _screenW, 0, 0, _screenW, _screenH, palMods, palModMapping);
+ CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
+ } else {
+ g_system->getPaletteManager()->setPalette(colors, start, num);
+ }
+}
+
+
+void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h) {
+ if (!dst)
+ return;
+
+ if (w == srcPitch && w == dstPitch) {
+ memcpy(dst + y * w, src, w * h);
+ } else {
+ const byte *s = src;
+ byte *d = dst + y * dstPitch + x;
+ for (int i = 0; i < h; ++i) {
+ memcpy(d, s, w);
+ s += srcPitch;
+ d += dstPitch;
+ }
+ }
}
-void GfxDefaultDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) {
+void GfxDefaultDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+ GFXDRV_ASSERT_READY;
+ GFXDRV_ASSERT_ALIGNED; // We can't fix the boundaries here, it has to happen before this call
+
+ if (src != _currentBitmap)
+ updateBitmapBuffer(_currentBitmap, _screenW * _srcPixelSize, src, pitch, x * _srcPixelSize, y, w * _srcPixelSize, h);
+
+ if (_pixelSize != _srcPixelSize) {
+ generateOutput(_compositeBuffer, src, pitch, w, h, palMods, palModMapping + y * pitch + x);
+ src = _compositeBuffer;
+ pitch = w * _pixelSize;
+ }
+
g_system->copyRectToScreen(src, pitch, x, y, w, h);
}
void GfxDefaultDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
}
-SCI0_DOSPreVGADriver::SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, int horizontalAlignment) : GfxDriver(screenW, screenH, numColors, horizontalAlignment), _palNeedUpdate(true), _colors(nullptr), _compositeBuffer(nullptr) {
- _compositeBuffer = new byte[screenW * screenH]();
+void GfxDefaultDriver::copyCurrentBitmap(byte *dest, uint32 size) const {
+ GFXDRV_ASSERT_READY;
+ assert(dest);
+ assert(size <= (uint32)(_screenW * _screenH));
+
+ // SCI 0 should not make calls to this method (except when using palette mods), but we have to know if it does...
+ if (!_currentBitmap)
+ error("GfxDefaultDriver::copyDataFromCurrentBitmap(): unexpected call");
+
+ // I have changed the implementation a bit from what the engine did before. For non-rgb rendering
+ // it would call OSystem::lockScreen() and then memcpy the data from there (which avoided the need
+ // for the extra bitmap buffer). However, OSystem::lockScreen() is meant more as an update method
+ // for the screen, the call to OSystem::unlockScreen() will turn the whole screen dirty (to be
+ // updated on the next OSysten::updateScreen() call. This is not what we need here, so I rather use
+ // the extra bitmap buffer (which is required for rgb rendering anyway).
+ memcpy(dest, _currentBitmap, size);
}
-SCI0_DOSPreVGADriver::~SCI0_DOSPreVGADriver() {
- delete[] _compositeBuffer;
+void GfxDefaultDriver::copyCurrentPalette(byte *dest, int start, int num) const {
+ GFXDRV_ASSERT_READY;
+
+ if (_pixelSize == 1) {
+ GfxDriver::copyCurrentPalette(dest, start, num);
+ return;
+ }
+
+ assert(dest);
+ assert(_currentPalette);
+ assert(start + num <= 256);
+ memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
}
-bool SCI0_DOSPreVGADriver::checkDriver(const char *const *driverNames, int listSize) {
- Common::String missing;
- while (listSize-- && *driverNames) {
- if (Common::File::exists(*driverNames))
- return true;
- if (!missing.empty())
- missing += " or ";
- missing += "'" + Common::String(*driverNames) + "'";
- ++driverNames;
+template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f) {
+ T *dst = &reinterpret_cast<T*>(dest)[start];
+ for (uint i = 0; i < num; ++i) {
+ *dst++ = f.RGBToColor(src[0], src[1], src[2]);
+ src += 3;
+ }
+}
+
+void GfxDefaultDriver::updatePalette(const byte *colors, uint start, uint num, bool skipRGBPalette) {
+ memcpy(_currentPalette + start * 3, colors, num * 3);
+ // If palette mods are on we don't need to update the internal palette,
+ // since the colors have to be generated on the fly anyway.
+ if (skipRGBPalette)
+ return;
+ if (_pixelSize == 4)
+ updateRGBPalette<uint32>(_internalPalette, colors, start, num, _format);
+ else if (_pixelSize == 2)
+ updateRGBPalette<uint16>(_internalPalette, colors, start, num, _format);
+ else
+ error("GfxDefaultDriver::updatePalette(): Unsupported pixel size %d", _pixelSize);
+}
+
+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;
}
- warning("Driver file %s not found. Starting game in EGA mode", missing.c_str());
- return false;
+}
+
+#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 *pal, Graphics::PixelFormat &f, const PaletteMod *mods, const byte *modMapping) {
+ T *d = reinterpret_cast<T*>(dst);
+ const byte *s1 = src;
+ const byte *s2 = modMapping;
+ pitch -= w;
+
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ const byte *col = &pal[*s1++ * 3];
+ byte m = *s2++;
+ if (m)
+ *d++ = f.RGBToColor(applyMod(col[0], mods[m].r), applyMod(col[1], mods[m].g), applyMod(col[2], mods[m].b));
+ else
+ *d++ = f.RGBToColor(col[0], col[1], col[2]);
+ }
+ 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, _format, palMods, palModMapping);
+ else if (_pixelSize == 4)
+ renderMod<uint32>(dst, src, pitch, w, h, _currentPalette, _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);
+ }
+}
+
+SCI0_DOSPreVGADriver::SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, int horizontalAlignment, bool rgbRendering) :
+ GfxDriver(screenW, screenH, numColors, horizontalAlignment), _requestRGBMode(rgbRendering), _colors(nullptr), _compositeBuffer(nullptr), _internalPalette(nullptr) {
+}
+
+SCI0_DOSPreVGADriver::~SCI0_DOSPreVGADriver() {
+ delete[] _compositeBuffer;
+ delete[] _internalPalette;
}
void SCI0_DOSPreVGADriver::assignPalette(const byte *colors) {
_colors = colors;
}
-void SCI0_DOSPreVGADriver::setPalette(const byte*, uint, uint) {
- if (!_palNeedUpdate || !_colors)
+void SCI0_DOSPreVGADriver::initScreen(const Graphics::PixelFormat*) {
+ Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
+ initGraphics(_screenW, _screenH, _requestRGBMode ? nullptr : &format);
+ format = g_system->getScreenFormat();
+ _pixelSize = format.bytesPerPixel;
+
+ if (_requestRGBMode && _pixelSize == 1)
+ warning("SCI0_DOSPreVGADriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+ assert(_colors);
+ if (_pixelSize == 1) {
+ g_system->getPaletteManager()->setPalette(_colors, 0, _numColors);
+ } else {
+ byte *rgbpal = new byte[_numColors * _pixelSize]();
+ assert(rgbpal);
+
+ if (_pixelSize == 2)
+ updateRGBPalette<uint16>(rgbpal, _colors, 0, _numColors, format);
+ else if (_pixelSize == 4)
+ updateRGBPalette<uint32>(rgbpal, _colors, 0, _numColors, format);
+ else
+ error("SCI0_DOSPreVGADriver::initScreen(): Unsupported screen format");
+ _internalPalette = rgbpal;
+ CursorMan.replaceCursorPalette(_colors, 0, _numColors);
+ }
+
+ _compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
+ assert(_compositeBuffer);
+
+ setupRenderProc();
+
+ _ready = true;
+}
+
+void SCI0_DOSPreVGADriver::copyCurrentBitmap(byte*, uint32) const {
+ // This is not needed for SCI0
+ error("SCI0_DOSPreVGADriver::copyCurrentBitmap(): Not implemented");
+}
+
+void SCI0_DOSPreVGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
+ GFXDRV_ASSERT_READY;
+
+ if (_pixelSize == 1) {
+ GfxDriver::copyCurrentPalette(dest, start, num);
return;
- _palNeedUpdate = false;;
- g_system->getPaletteManager()->setPalette(_colors, 0, _numColors);
+ }
+
+ assert(dest);
+ memcpy(dest + start * 3, _colors + start * 3, MIN<int>(num, _numColors) * 3);
}
-SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard) : SCI0_DOSPreVGADriver(4, 320, 200, 1), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard) {
+SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering) : SCI0_DOSPreVGADriver(4, 320, 200, 1, rgbRendering), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard) {
static const byte cgaColors[48] = {
/*
// Canonical CGA palette
@@ -207,39 +461,25 @@ SCI0_CGADriver::~SCI0_CGADriver() {
delete[] _cgaPatterns;
}
-void SCI0_CGADriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) {
- // We can't really properly fix the boundaries here, we have to do that before calling this function.
- assert(!(w & _hAlign));
- assert(!(x & _hAlign));
+void SCI0_CGADriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) {
+ GFXDRV_ASSERT_READY;
+ GFXDRV_ASSERT_ALIGNED; // We can't fix the boundaries here, it has to happen before this call
byte *dst = _compositeBuffer;
- pitch -= w;
int ty = y;
for (int i = 0; i < h; ++i) {
- int tx = x & 3;
- ++ty;
- for (int ii = 0; ii < (w >> 1); ++ii) {
- uint16 pattern = _cgaPatterns[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
- src += 2;
- byte sh = (ty & 3) << 1;
- byte lo = ((pattern & 0xff) >> sh) | ((pattern & 0xff) << (8 - sh));
- byte hi = (pattern >> (8 + sh)) | ((pattern >> 8) << (8 - sh));
- *dst++ = (lo >> (6 - (tx << 1))) & 3;
- *dst++ = (hi >> (4 - (tx << 1))) & 3;
- tx ^= 2;
- }
+ _renderLine(dst, src, w, x & 3, ++ty, _cgaPatterns, _internalPalette);
src += pitch;
}
- g_system->copyRectToScreen(_compositeBuffer, w, x, y, w, h);
+ g_system->copyRectToScreen(_compositeBuffer, w * _pixelSize, x, y, w, h);
}
void SCI0_CGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
- // Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
- // has already been generated by the engine. Of course, that code could be moved to the EGA
- // driver class and implemented again for each mode, but I don't see the benefit. Instead,
- // we simply convert the colors as needed...
+ GFXDRV_ASSERT_READY;
+ // Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor
+ // that has already been generated by the engine. We simply convert the colors as needed...
assert(keycolor == 1);
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d = _compositeBuffer;
@@ -249,6 +489,42 @@ void SCI0_CGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotsp
CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
}
+
+template <typename T> void cgaRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const uint16 *patterns, const byte *pal) {
+ T *d = reinterpret_cast<T*>(dst);
+ const T *p = reinterpret_cast<const T*>(pal);
+ w >>= 1;
+
+ for (int i = 0; i < w; ++i) {
+ uint16 pattern = patterns[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
+ src += 2;
+ byte sh = (ty & 3) << 1;
+ byte lo = ((pattern & 0xff) >> sh) | ((pattern & 0xff) << (8 - sh));
+ byte hi = (pattern >> (8 + sh)) | ((pattern >> 8) << (8 - sh));
+ if (sizeof(T) == 1) {
+ *d++ = (lo >> (6 - (tx << 1))) & 3;
+ *d++ = (hi >> (4 - (tx << 1))) & 3;
+ } else {
+ *d++ = p[(lo >> (6 - (tx << 1))) & 3];
+ *d++ = p[(hi >> (4 - (tx << 1))) & 3];
+ }
+ tx ^= 2;
+ }
+
+ dst = reinterpret_cast<byte*>(d);
+}
+
+void SCI0_CGADriver::setupRenderProc() {
+ static const LineProc lineProcs[] = {
+ &cgaRenderLine<byte>,
+ &cgaRenderLine<uint16>,
+ &cgaRenderLine<uint32>
+ };
+
+ assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+ _renderLine = lineProcs[_pixelSize >> 1];
+}
+
const char *SCI0_CGADriver::_driverFile = "CGA320C.DRV";
const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
@@ -295,7 +571,7 @@ const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
return result;
}
-SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor) : SCI0_DOSPreVGADriver(2, 640, 400, 1), _monochromePatterns(nullptr), _earlyVersion(false) {
+SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering) : SCI0_DOSPreVGADriver(2, 640, 400, 1, rgbRendering), _monochromePatterns(nullptr), _earlyVersion(false) {
_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
@@ -310,54 +586,28 @@ SCI0_CGABWDriver::~SCI0_CGABWDriver() {
delete[] _monochromePatterns;
}
-void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) {
- // We can't really properly fix the boundaries here, we have to do that before calling this function.
- assert(!(w & _hAlign));
- assert(!(x & _hAlign));
+void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) {
+ GFXDRV_ASSERT_READY;
+ GFXDRV_ASSERT_ALIGNED; // We can't fix the boundaries here, it has to happen before this call
- byte *dst1 = _compositeBuffer;
- byte *dst2 = _compositeBuffer + (w << 1);
+ byte *dst = _compositeBuffer;
int ty = y & 7;
- pitch -= w;
+ if (_earlyVersion)
+ ++ty;
for (int i = 0; i < h; ++i) {
- int tx = x & 3;
- if (_earlyVersion) {
- ++ty;
- for (int ii = 0; ii < (w >> 1); ++ii) {
- uint16 p16 = reinterpret_cast<const uint16*>(_monochromePatterns)[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
- src += 2;
- byte sh = (ty & 3) << 1;
- byte lo = ((p16 & 0xff) >> sh) | ((p16 & 0xff) << (8 - sh));
- byte hi = (p16 >> (8 + sh)) | ((p16 >> 8) << (8 - sh));
- *dst1++ = *dst2++ = ((lo >> (6 - (tx << 1))) >> 1) & 1;
- *dst1++ = *dst2++ = (lo >> (6 - (tx << 1))) & 1;
- *dst1++ = *dst2++ = ((hi >> (4 - (tx << 1))) >> 1) & 1;
- *dst1++ = *dst2++ = (hi >> (4 - (tx << 1))) & 1;
- tx ^= 2;
- }
- } else {
- for (int ii = 0; ii < w; ++ii) {
- byte p = _monochromePatterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
- *dst1++ = *dst2++ = (p >> 1) & 1;
- *dst1++ = *dst2++ = p & 1;
- tx = (tx + 1) & 3;
- }
- ty = (ty + 1) & 7;
- }
+ _renderLine(dst, src, w, x & 3, ty, _monochromePatterns, _internalPalette);
+ ty = (ty + 1) & 7;
src += pitch;
- dst1 += (w << 1);
- dst2 += (w << 1);
}
- g_system->copyRectToScreen(_compositeBuffer, w << 1, x << 1, y << 1, w << 1, h << 1);
+ g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, x << 1, y << 1, w << 1, h << 1);
}
void SCI0_CGABWDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
- // has already been generated by the engine. Of course, that code could be moved to the EGA
- // driver class and implemented again for each mode, but I don't see the benefit. Instead,
- // we simply convert the colors as needed and scale the cursor...
+ // has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
assert(keycolor == 1);
keycolor = 0x0f;
w <<= 1;
@@ -390,9 +640,77 @@ void SCI0_CGABWDriver::clearRect(const Common::Rect &r) const {
GfxDriver::clearRect(r2);
}
+template <typename T> void cgabwRenderLine_v1(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+ const T *p = reinterpret_cast<const T*>(pal);
+ const uint16 *patterns16 = reinterpret_cast<const uint16*>(patterns);
+ T *d1 = reinterpret_cast<T*>(dst);
+ T *d2 = d1 + (w << 1);
+ w >>= 1;
+
+ for (int i = 0; i < w; ++i) {
+ uint16 pt = patterns16[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
+ src += 2;
+ byte sh = (ty & 3) << 1;
+ byte lo = ((pt & 0xff) >> sh) | ((pt & 0xff) << (8 - sh));
+ byte hi = (pt >> (8 + sh)) | ((pt >> 8) << (8 - sh));
+ if (sizeof(T) == 1) {
+ *d1++ = *d2++ = ((lo >> (6 - (tx << 1))) >> 1) & 1;
+ *d1++ = *d2++ = (lo >> (6 - (tx << 1))) & 1;
+ *d1++ = *d2++ = ((hi >> (4 - (tx << 1))) >> 1) & 1;
+ *d1++ = *d2++ = (hi >> (4 - (tx << 1))) & 1;
+ } else {
+ *d1++ = *d2++ = p[((lo >> (6 - (tx << 1))) >> 1) & 1];
+ *d1++ = *d2++ = p[(lo >> (6 - (tx << 1))) & 1];
+ *d1++ = *d2++ = p[((hi >> (4 - (tx << 1))) >> 1) & 1];
+ *d1++ = *d2++ = p[(hi >> (4 - (tx << 1))) & 1];
+ }
+ tx ^= 2;
+ }
+
+ dst = reinterpret_cast<byte*>(d2);
+}
+
+template <typename T> void cgabwRenderLine_v2(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+ const T *p = reinterpret_cast<const T*>(pal);
+ T *d1 = reinterpret_cast<T*>(dst);
+ T *d2 = d1 + (w << 1);
+
+ for (int i = 0; i < w; ++i) {
+ byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
+ if (sizeof(T) == 1) {
+ *d1++ = *d2++ = (pt >> 1) & 1;
+ *d1++ = *d2++ = pt & 1;
+ } else {
+ *d1++ = *d2++ = p[(pt >> 1) & 1];
+ *d1++ = *d2++ = p[pt & 1];
+ }
+ tx = (tx + 1) & 3;
+ }
+
+ dst = reinterpret_cast<byte*>(d2);
+}
+
+void SCI0_CGABWDriver::setupRenderProc() {
+ static const LineProc lineProcs[] = {
+ &cgabwRenderLine_v1<byte>,
+ &cgabwRenderLine_v1<uint16>,
+ &cgabwRenderLine_v1<uint32>,
+ &cgabwRenderLine_v2<byte>,
+ &cgabwRenderLine_v2<uint16>,
+ &cgabwRenderLine_v2<uint32>
+ };
+
+ int t = _pixelSize >> 1;
+ if (!_earlyVersion)
+ t += 3;
+
+ assert(t < ARRAYSIZE(lineProcs));
+ _renderLine = lineProcs[t];
+}
+
const char *SCI0_CGABWDriver::_driverFiles[2] = { "CGA320BW.DRV", "CGA320M.DRV" };
-SCI0_HerculesDriver::SCI0_HerculesDriver(uint32 monochromeColor, bool cropImage) : SCI0_DOSPreVGADriver(2, cropImage ? 640 : 720, cropImage ? 300 : 350, 0),
+SCI0_HerculesDriver::SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage) : SCI0_DOSPreVGADriver(2, cropImage ? 640 : 720, cropImage ? 300 : 350, 0, rgbRendering),
_centerX(cropImage ? 0 : 40), _centerY(cropImage ? 0 : 25), _monochromePatterns(nullptr) {
_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
@@ -409,10 +727,9 @@ SCI0_HerculesDriver::~SCI0_HerculesDriver() {
delete[] _monochromePatterns;
}
-void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) {
- // We can't really properly fix the boundaries here, we have to do that before calling this function.
- assert(!(w & _hAlign));
- assert(!(x & _hAlign));
+void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) {
+ GFXDRV_ASSERT_READY;
+ GFXDRV_ASSERT_ALIGNED; // We can't fix the boundaries here, it has to happen before this call
byte *dst = _compositeBuffer;
byte sw = y & 1;
@@ -422,14 +739,7 @@ void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int pitch, int x, in
for (int i = 0; i < h; ++i) {
const byte *src2 = src;
- int tx = x & 3;
- for (int ii = 0; ii < w; ++ii) {
- byte p = _monochromePatterns[((*src2++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
- *dst++ = (p >> 1) & 1;
- *dst++ = p & 1;
- tx = (tx + 1) & 3;
- }
-
+ _renderLine(dst, src2, w, x & 3, ty, _monochromePatterns, _internalPalette);
ty = (ty + 1) & 7;
++rh;
@@ -444,14 +754,13 @@ void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int pitch, int x, in
}
}
- g_system->copyRectToScreen(_compositeBuffer, w << 1, (x << 1) + _centerX, y + _centerY, w << 1, rh);
+ g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, (x << 1) + _centerX, y + _centerY, w << 1, rh);
}
void SCI0_HerculesDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
- // has already been generated by the engine. Of course, that code could be moved to the EGA
- // driver class and implemented again for each mode, but I don't see the benefit. Instead,
- // we simply convert the colors as needed and scale the cursor...
+ // has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
assert(keycolor == 1);
keycolor = 0x0f;
int alt = 0;
@@ -488,6 +797,39 @@ void SCI0_HerculesDriver::clearRect(const Common::Rect &r) const {
GfxDriver::clearRect(r2);
}
+template <typename T> void herculesRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
+ T *d = reinterpret_cast<T*>(dst);
+ const T *p = reinterpret_cast<const T*>(pal);
+
+ for (int i = 0; i < w; ++i) {
+ byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
+ if (sizeof(T) == 1) {
+ *d++ = (pt >> 1) & 1;
+ *d++ = pt & 1;
+ } else {
+ *d++ = p[(pt >> 1) & 1];
+ *d++ = p[pt & 1];
+ }
+ tx = (tx + 1) & 3;
+ }
+
+ dst = reinterpret_cast<byte*>(d);
+}
+
+void SCI0_HerculesDriver::setupRenderProc() {
+ static const LineProc lineProcs[] = {
+ &herculesRenderLine<byte>,
+ &herculesRenderLine<uint16>,
+ &herculesRenderLine<uint32>
+ };
+
+ assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+ _renderLine = lineProcs[_pixelSize >> 1];
+}
+
const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
+#undef GFXDRV_ASSERT_READY
+#undef GFXDRV_ASEERT_ALIGNED
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 89010329671..7d616c9cda8 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -23,102 +23,134 @@
#define SCI_GRAPHICS_GFXDRIVERS_H
#include "common/rect.h"
+#include "graphics//pixelformat.h"
namespace Sci {
+struct PaletteMod;
+
class GfxDriver {
public:
- GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors, int horizontalAlignment) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _hAlign(horizontalAlignment) {}
+ GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors, int horizontalAlignment) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _hAlign(horizontalAlignment), _ready(false), _pixelSize(1) {}
virtual ~GfxDriver() {}
-
- uint16 screenWidth() const { return _screenW; }
- uint16 screenHeight() const { return _screenH; }
- uint16 numColors() const { return _numColors; }
- byte hAlignment() const { return _hAlign; }
-
- virtual bool allowRGBRendering() const = 0;
- virtual void setPalette(const byte *colors, uint start, uint num) = 0;
- virtual void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) = 0;
+ virtual void initScreen(const Graphics::PixelFormat *srcRGBFormat = nullptr) = 0; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
+ virtual void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) = 0;
+ virtual void copyRectToScreen(const byte *src, int pitch, int x, int y, 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 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 bool supportsPalIntensity() const = 0;
+ uint16 numColors() const { return _numColors; }
+ byte hAlignment() const { return _hAlign; }
+ byte pixelSize() const { return _pixelSize; }
protected:
+ bool _ready;
+ static bool checkDriver(const char *const *driverNames, int listSize);
const uint16 _screenW;
const uint16 _screenH;
uint16 _numColors;
+ byte _pixelSize;
const byte _hAlign;
};
-class GfxDefaultDriver final : public GfxDriver {
+class GfxDefaultDriver : public GfxDriver {
public:
- GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight);
- ~GfxDefaultDriver() override {}
- bool allowRGBRendering() const override { return true; }
- void setPalette(const byte *colors, uint start, uint num) override;
- void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) override;
+ GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool rgbRendering);
+ ~GfxDefaultDriver() override;
+ void initScreen(const Graphics::PixelFormat *srcRGBFormat) override; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
+ void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
+ void copyRectToScreen(const byte *src, int pitch, int x, int y, 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 copyCurrentBitmap(byte *dest, uint32 size) const override;
+ void copyCurrentPalette(byte *dest, int start, int num) const override;
+ bool supportsPalIntensity() const override { return true; }
+protected:
+ void updatePalette(const byte *colors, uint start, uint num, bool skipRGBPalette);
+ byte *_compositeBuffer;
+ byte *_currentBitmap;
+ byte *_currentPalette;
+ byte *_internalPalette;
+ Graphics::PixelFormat _format;
+ byte _srcPixelSize;
+ const bool _requestRGBMode;
+private:
+ void generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
};
class SCI0_DOSPreVGADriver : public GfxDriver {
public:
- SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, int horizontalAlignment);
+ SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, int horizontalAlignment, bool rgbRendering);
~SCI0_DOSPreVGADriver() override;
- bool allowRGBRendering() const override { return false; }
- void setPalette(const byte*, uint, uint) override;
+ void initScreen(const Graphics::PixelFormat*) override;
+ void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) {}
+ void copyCurrentBitmap(byte*, uint32) const override;
+ void copyCurrentPalette(byte *dest, int start, int num) const override;
+ bool supportsPalIntensity() const override { return false; }
protected:
- static bool checkDriver(const char *const *driverNames, int listSize);
void assignPalette(const byte *colors);
byte *_compositeBuffer;
+ const byte *_internalPalette;
private:
- bool _palNeedUpdate;
+ virtual void setupRenderProc() = 0;
+ const bool _requestRGBMode;
const byte *_colors;
};
class SCI0_CGADriver final : public SCI0_DOSPreVGADriver {
public:
- SCI0_CGADriver(bool emulateCGAModeOnEGACard);
+ SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering);
~SCI0_CGADriver() override;
- void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) override;
+ void copyRectToScreen(const byte *src, int pitch, int x, int y, 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); }
private:
+ void setupRenderProc() override;
uint16 *_cgaPatterns;
byte _palette[12];
const bool _disableMode5;
+ typedef void (*LineProc)(byte*&, const byte*, int, int, int, const uint16*, const byte*);
+ LineProc _renderLine;
static const char *_driverFile;
};
class SCI0_CGABWDriver final : public SCI0_DOSPreVGADriver {
public:
- SCI0_CGABWDriver(uint32 monochromeColor);
+ SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering);
~SCI0_CGABWDriver() override;
- void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) override;
+ void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getMousePos() const override;
void clearRect(const Common::Rect &r) const override;
static bool validateMode() { return checkDriver(_driverFiles, 2); }
private:
+ void setupRenderProc() override;
byte _monochromePalette[6];
const byte *_monochromePatterns;
bool _earlyVersion;
+ typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
+ LineProc _renderLine;
static const char *_driverFiles[2];
};
class SCI0_HerculesDriver final : public SCI0_DOSPreVGADriver {
public:
- SCI0_HerculesDriver(uint32 monochromeColor, bool cropImage);
+ SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage);
~SCI0_HerculesDriver() override;
- void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h) override;
+ void copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getMousePos() const override;
void clearRect(const Common::Rect &r) const override;
static bool validateMode() { return checkDriver(&_driverFile, 1); }
private:
+ void setupRenderProc() override;
const uint16 _centerX;
const uint16 _centerY;
byte _monochromePalette[6];
const byte *_monochromePatterns;
+ typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
+ LineProc _renderLine;
static const char *_driverFile;
};
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 995c6ea8fd0..96f8bc67ede 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -537,10 +537,10 @@ void GfxPalette::kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag)
}
void GfxPalette::kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette) {
- memset(&_sysPalette.intensity[0] + fromColor, intensity, toColor - fromColor);
- if (setPalette) {
+ if (_screen->gfxDriver()->supportsPalIntensity())
+ memset(&_sysPalette.intensity[0] + fromColor, intensity, toColor - fromColor);
+ if (setPalette)
setOnScreen();
- }
}
int16 GfxPalette::kernelFindColor(uint16 r, uint16 g, uint16 b, bool force16BitColorMatch) {
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 532aa065351..c0bb3b0e3e6 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -148,18 +148,43 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
break;
}
+ int extraHeight = 0;
+ if (g_sci->hasMacIconBar()) {
+ // For SCI1.1 Mac games with the custom icon bar, we need to expand the screen
+ // to accommodate for the icon bar. Of course, both KQ6 and Freddy Pharkas differ in size.
+ // We add 2 to the height of the icon bar to add a buffer between the screen and the
+ // icon bar (as did the original interpreter).
+ switch (g_sci->getGameId()) {
+ case GID_KQ6:
+ extraHeight = 26 + 2;
+ break;
+ case GID_FREDDYPHARKAS:
+ extraHeight = 28 + 2;
+ break;
+ default:
+ error("Unknown SCI1.1 Mac game");
+ }
+
+ if (_upscaledHires == GFX_SCREEN_UPSCALED_640x400) {
+ extraHeight *= 2;
+ }
+ }
+
+ bool enablePaletteMods = ConfMan.hasKey("palette_mods") && ConfMan.getBool("palette_mods");
+ 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);
+ _gfxDrv = new SCI0_CGADriver(false, requestRGB);
break;
case Common::kRenderCGA_BW:
- _gfxDrv = new SCI0_CGABWDriver(0xffffff);
+ _gfxDrv = new SCI0_CGABWDriver(0xffffff, requestRGB);
break;
case Common::kRenderHercA:
case Common::kRenderHercG:
- _gfxDrv = new SCI0_HerculesDriver(renderMode == Common::kRenderHercG ? 0x66ff66 : 0xffbf66, false);
+ _gfxDrv = new SCI0_HerculesDriver(renderMode == Common::kRenderHercG ? 0x66ff66 : 0xffbf66, requestRGB, false);
break;
default:
break;
@@ -167,7 +192,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
}
if (_gfxDrv == nullptr)
- _gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight);
+ _gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, requestRGB);
assert(_gfxDrv);
_displayPixels = _displayWidth * _displayHeight;
@@ -208,60 +233,17 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
}
// Set up palette mods if requested
- if (ConfMan.getBool("palette_mods")) {
+ if (enablePaletteMods)
setupCustomPaletteMods(this);
- ConfMan.setBool("rgb_rendering", true);
- }
// Initialize the actual screen
- const Graphics::PixelFormat *format = &format8;
- if (_gfxDrv->allowRGBRendering() && ConfMan.getBool("rgb_rendering"))
- format = nullptr; // Backend's preferred mode; RGB if available
+ _gfxDrv->initScreen();
- if (g_sci->hasMacIconBar()) {
- // For SCI1.1 Mac games with the custom icon bar, we need to expand the screen
- // to accommodate for the icon bar. Of course, both KQ6 and Freddy Pharkas differ in size.
- // We add 2 to the height of the icon bar to add a buffer between the screen and the
- // icon bar (as did the original interpreter).
- int macIconBarBuffer = 0;
- switch (g_sci->getGameId()) {
- case GID_KQ6:
- macIconBarBuffer = 26 + 2;
- break;
- case GID_FREDDYPHARKAS:
- macIconBarBuffer = 28 + 2;
- break;
- default:
- error("Unknown SCI1.1 Mac game");
- }
-
- if (_upscaledHires == GFX_SCREEN_UPSCALED_640x400) {
- macIconBarBuffer *= 2;
- }
-
- initGraphics(_displayWidth, _displayHeight + macIconBarBuffer, format);
- } else
- initGraphics(_gfxDrv->screenWidth(), _gfxDrv->screenHeight(), format);
-
-
- _format = g_system->getScreenFormat();
-
- // If necessary, allocate buffers for RGB mode
- if (_format.bytesPerPixel != 1) {
- _displayedScreen = (byte *)calloc(_displayPixels, 1);
- _rgbScreen = (byte *)calloc(_format.bytesPerPixel*_displayPixels, 1);
- _palette = new byte[3*256];
-
- if (_paletteModsEnabled)
- _paletteMapScreen = (byte *)calloc(_displayPixels, 1);
- else
- _paletteMapScreen = nullptr;
- } else {
- _displayedScreen = nullptr;
- _palette = nullptr;
- _rgbScreen = nullptr;
+ if (_gfxDrv->pixelSize() != 1 && _paletteModsEnabled)
+ _paletteMapScreen = (byte *)calloc(_displayPixels, 1);
+ else
_paletteMapScreen = nullptr;
- }
+
_backupScreen = nullptr;
}
@@ -270,132 +252,16 @@ GfxScreen::~GfxScreen() {
free(_priorityScreen);
free(_controlScreen);
free(_displayScreen);
-
free(_paletteMapScreen);
- free(_displayedScreen);
- free(_rgbScreen);
- delete[] _palette;
delete[] _backupScreen;
delete _gfxDrv;
}
-void GfxScreen::convertToRGB(const Common::Rect &rect) {
- assert(_format.bytesPerPixel != 1);
-
- for (int y = rect.top; y < rect.bottom; ++y) {
-
- const byte *in = _displayedScreen + y * _displayWidth + rect.left;
- byte *out = _rgbScreen + (y * _displayWidth + rect.left) * _format.bytesPerPixel;
-
- // TODO: Reduce code duplication here
-
- if (_format.bytesPerPixel == 2) {
-
- if (_paletteMapScreen) {
- const byte *mod = _paletteMapScreen + y * _displayWidth + rect.left;
- for (int x = 0; x < rect.width(); ++x) {
- byte i = *in;
- byte r = _palette[3*i + 0];
- byte g = _palette[3*i + 1];
- byte b = _palette[3*i + 2];
-
- if (*mod) {
- r = MIN(r * (128 + _paletteMods[*mod].r) / 128, 255);
- g = MIN(g * (128 + _paletteMods[*mod].g) / 128, 255);
- b = MIN(b * (128 + _paletteMods[*mod].b) / 128, 255);
- }
-
- uint16 c = (uint16)_format.RGBToColor(r, g, b);
- WRITE_UINT16(out, c);
- in += 1;
- out += 2;
- mod += 1;
- }
- } else {
- for (int x = 0; x < rect.width(); ++x) {
- byte i = *in;
- byte r = _palette[3*i + 0];
- byte g = _palette[3*i + 1];
- byte b = _palette[3*i + 2];
- uint16 c = (uint16)_format.RGBToColor(r, g, b);
- WRITE_UINT16(out, c);
- in += 1;
- out += 2;
- }
- }
-
- } else {
- assert(_format.bytesPerPixel == 4);
-
- if (_paletteMapScreen) {
- const byte *mod = _paletteMapScreen + y * _displayWidth + rect.left;
- for (int x = 0; x < rect.width(); ++x) {
- byte i = *in;
- byte r = _palette[3*i + 0];
- byte g = _palette[3*i + 1];
- byte b = _palette[3*i + 2];
-
- if (*mod) {
- r = MIN(r * (128 + _paletteMods[*mod].r) / 128, 255);
- g = MIN(g * (128 + _paletteMods[*mod].g) / 128, 255);
- b = MIN(b * (128 + _paletteMods[*mod].b) / 128, 255);
- }
-
- uint32 c = _format.RGBToColor(r, g, b);
- WRITE_UINT32(out, c);
- in += 1;
- out += 4;
- mod += 1;
- }
- } else {
- for (int x = 0; x < rect.width(); ++x) {
- byte i = *in;
- byte r = _palette[3*i + 0];
- byte g = _palette[3*i + 1];
- byte b = _palette[3*i + 2];
- uint32 c = _format.RGBToColor(r, g, b);
- WRITE_UINT32(out, c);
- in += 1;
- out += 4;
- }
- }
- }
- }
-}
-
-void GfxScreen::displayRectRGB(const Common::Rect &rect, int x, int y) {
- // Display rect from _activeScreen to screen location x, y.
- // Clipping is assumed to be done already.
-
- Common::Rect targetRect;
- targetRect.left = x;
- targetRect.setWidth(rect.width());
- targetRect.top = y;
- targetRect.setHeight(rect.height());
-
- // 1. Update _displayedScreen
- for (int i = 0; i < rect.height(); ++i) {
- int offset = (rect.top + i) * _displayWidth + rect.left;
- int targetOffset = (targetRect.top + i) * _displayWidth + targetRect.left;
- memcpy(_displayedScreen + targetOffset, _activeScreen + offset, rect.width());
- }
-
- // 2. Convert to RGB
- convertToRGB(targetRect);
-
- // 3. Copy to screen
- g_system->copyRectToScreen(_rgbScreen + (targetRect.top * _displayWidth + targetRect.left) * _format.bytesPerPixel, _displayWidth * _format.bytesPerPixel, targetRect.left, targetRect.top, targetRect.width(), targetRect.height());
-}
-
void GfxScreen::displayRect(const Common::Rect &rect, int x, int y) {
// Display rect from _activeScreen to screen location x, y.
// Clipping is assumed to be done already.
-
- if (_format.bytesPerPixel == 1) {
- _gfxDrv->copyRectToScreen(_activeScreen + rect.top * _displayWidth + rect.left, _displayWidth, x, y, rect.width(), rect.height());
- } else {
- displayRectRGB(rect, x, y);
- }
+ _gfxDrv->copyRectToScreen(_activeScreen + rect.top * _displayWidth + rect.left,
+ _displayWidth, x, y, rect.width(), rect.height(), _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
}
@@ -406,12 +272,6 @@ void GfxScreen::clearForRestoreGame() {
memset(_priorityScreen, 0, _pixels);
memset(_controlScreen, 0, _pixels);
memset(_displayScreen, 0, _displayPixels);
- if (_displayedScreen) {
- memset(_displayedScreen, 0, _displayPixels);
- memset(_rgbScreen, 0, _format.bytesPerPixel*_displayPixels);
- if (_paletteMapScreen)
- memset(_paletteMapScreen, 0, _displayPixels);
- }
memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
_fontIsUpscaled = false;
copyToScreen();
@@ -422,30 +282,14 @@ void GfxScreen::copyToScreen() {
displayRect(r, 0, 0);
}
-void GfxScreen::copyVideoFrameToScreen(const byte *buffer, int pitch, const Common::Rect &rect, bool is8bit) {
- if (_format.bytesPerPixel == 1 || !is8bit) {
- g_system->copyRectToScreen(buffer, pitch, rect.left, rect.top, rect.width(), rect.height());
- } else {
- for (int i = 0; i < rect.height(); ++i) {
- int offset = i * pitch;
- int targetOffset = (rect.top + i) * _displayWidth + rect.left;
- memcpy(_displayedScreen + targetOffset, buffer + offset, rect.width());
- }
- convertToRGB(rect);
- g_system->copyRectToScreen(_rgbScreen + (rect.top * _displayWidth + rect.left) * _format.bytesPerPixel, _displayWidth * _format.bytesPerPixel, rect.left, rect.top, rect.width(), rect.height());
- }
+void GfxScreen::copyVideoFrameToScreen(const byte *buffer, int pitch, const Common::Rect &rect) {
+ uint8 align = _gfxDrv->hAlignment();
+ Common::Rect r(rect.left & ~align, rect.top, (rect.right + align) & ~align, rect.bottom);
+ _gfxDrv->copyRectToScreen(buffer, pitch, r.left, r.top, r.width(), r.height(), _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
}
void GfxScreen::kernelSyncWithFramebuffer() {
- if (_format.bytesPerPixel == 1) {
- Graphics::Surface *screen = g_system->lockScreen();
- const byte *pix = (const byte *)screen->getPixels();
- for (int y = 0; y < _displayHeight; ++y)
- memcpy(_displayScreen + y * _displayWidth, pix + y * screen->pitch, _displayWidth);
- g_system->unlockScreen();
- } else {
- memcpy(_displayScreen, _displayedScreen, _displayPixels);
- }
+ _gfxDrv->copyCurrentBitmap(_displayScreen, _displayPixels);
}
void GfxScreen::copyRectToScreen(const Common::Rect &rect) {
@@ -479,7 +323,9 @@ void GfxScreen::copyDisplayRectToScreen(const Common::Rect &rect) {
void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) {
if (!_upscaledHires) {
- displayRect(rect, x, y);
+ uint8 align = _gfxDrv->hAlignment();
+ Common::Rect r(rect.left & ~align, rect.top, (rect.right + align) & ~align, rect.bottom);
+ displayRect(r, x & ~align, y);
} else {
int rectHeight = _upscaledHeightMapping[rect.bottom] - _upscaledHeightMapping[rect.top];
int rectWidth = _upscaledWidthMapping[rect.right] - _upscaledWidthMapping[rect.left];
@@ -1074,43 +920,19 @@ int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) {
}
void GfxScreen::grabPalette(byte *buffer, uint start, uint num) const {
- assert(start + num <= 256);
- if (_format.bytesPerPixel == 1) {
- g_system->getPaletteManager()->grabPalette(buffer, start, num);
- } else {
- memcpy(buffer, _palette + 3*start, 3*num);
- }
+ _gfxDrv->copyCurrentPalette(buffer, start, num);
}
void GfxScreen::setPalette(const byte *buffer, uint start, uint num, bool update) {
assert(start + num <= 256);
- if (_format.bytesPerPixel == 1) {
- _gfxDrv->setPalette(buffer, start, num);
- } else {
- memcpy(_palette + 3*start, buffer, 3*num);
- if (update) {
- // directly paint from _displayedScreen, not from _activeScreen
- Common::Rect r(0, 0, _displayWidth, _displayHeight);
- convertToRGB(r);
- g_system->copyRectToScreen(_rgbScreen, _displayWidth * _format.bytesPerPixel, 0, 0, _displayWidth, _displayHeight);
- }
- // CHECKME: Inside or outside the if (update)?
- // (The !update case only happens inside transitions.)
- CursorMan.replaceCursorPalette(_palette, 0, 256);
- }
+ _gfxDrv->setPalette(buffer, start, num, update, _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
}
void GfxScreen::bakCreateBackup() {
assert(!_backupScreen);
- _backupScreen = new byte[_format.bytesPerPixel * _displayPixels];
- if (_format.bytesPerPixel == 1) {
- Graphics::Surface *screen = g_system->lockScreen();
- memcpy(_backupScreen, screen->getPixels(), _displayPixels);
- g_system->unlockScreen();
- } else {
- memcpy(_backupScreen, _rgbScreen, _format.bytesPerPixel * _displayPixels);
- }
+ _backupScreen = new byte[_displayPixels];
+ _gfxDrv->copyCurrentBitmap(_backupScreen, _displayPixels);
}
void GfxScreen::bakDiscard() {
@@ -1121,9 +943,9 @@ void GfxScreen::bakDiscard() {
void GfxScreen::bakCopyRectToScreen(const Common::Rect &rect, int16 x, int16 y) {
assert(_backupScreen);
- const byte *ptr = _backupScreen;
- ptr += _format.bytesPerPixel * (rect.left + rect.top * _displayWidth);
- g_system->copyRectToScreen(ptr, _format.bytesPerPixel * _displayWidth, x, y, rect.width(), rect.height());
+ uint8 align = _gfxDrv->hAlignment();
+ Common::Rect r(rect.left & ~align, rect.top, (rect.right + align) & ~align, rect.bottom);
+ _gfxDrv->copyRectToScreen(_backupScreen + r.left + r.top * _displayWidth, _displayWidth, x, y, r.width(), r.height(), _paletteModsEnabled ? _paletteMods : nullptr, _paletteMapScreen);
}
void GfxScreen::setPaletteMods(const PaletteMod *mods, unsigned int count) {
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 4c7a38c7bf7..5bff720ec7f 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -96,7 +96,7 @@ public:
void bakDiscard();
// video frame displaying
- void copyVideoFrameToScreen(const byte *buffer, int pitch, const Common::Rect &rect, bool is8bit);
+ void copyVideoFrameToScreen(const byte *buffer, int pitch, const Common::Rect &rect);
// Vector drawing
private:
@@ -173,8 +173,6 @@ private:
uint16 _displayHeight;
uint _displayPixels;
- Graphics::PixelFormat _format;
-
byte _colorWhite;
byte _colorDefaultVectorData;
@@ -207,14 +205,11 @@ private:
Graphics::Surface _displayScreenSurface;
/**
- * CGA and Hercules support
+ * Support for CGA and Hercules and other graphic modes that the original
+ * interpreters allowed. It also performs the rgb rendering if needed.
*/
GfxDriver *_gfxDrv;
- // Screens for RGB mode support
- byte *_displayedScreen;
- byte *_rgbScreen;
-
// For RGB per-view/pic palette mods
byte *_paletteMapScreen;
byte _curPaletteMapValue;
@@ -223,10 +218,7 @@ private:
byte *_backupScreen; // for bak* functions
- void convertToRGB(const Common::Rect &rect);
- void displayRectRGB(const Common::Rect &rect, int x, int y);
void displayRect(const Common::Rect &rect, int x, int y);
- byte *_palette;
ResourceManager *_resMan;
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index dffba4b481f..29a9d823a25 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -322,24 +322,20 @@ Common::Error SciEngine::run() {
Common::RenderMode renderMode = Common::kRenderDefault;
bool undither = ConfMan.getBool("disable_dithering");
-
- if (getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
- if (ConfMan.hasKey("render_mode"))
- renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
-
- // Check if the selected render mode is available for the game. This is quite specific for
- // each SCI0 game. Sometime it is only EGA, sometimes only CGA b/w without CGA 4 colors, etc.
- // Also set default mode if undithering is enabled.
- if ((renderMode == Common::kRenderEGA && undither) ||
- (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;
-
- // Disable undithering for CGA and Hercules modes
- if (renderMode != Common::kRenderDefault)
- undither = false;
- }
+ if (ConfMan.hasKey("render_mode"))
+ renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
+
+ // Check if the selected render mode is available for the game. This is quite specific for each game.
+ // Sometime it is only EGA, sometimes only CGA b/w without CGA 4 colors, etc. Also set default mode if undithering is enabled.
+ if ((renderMode == Common::kRenderEGA && ((getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY) && undither)) ||
+ (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;
+
+ // Disable undithering for CGA and Hercules modes
+ if (renderMode != Common::kRenderDefault)
+ undither = false;
// Initialize the game screen
_gfxScreen = new GfxScreen(_resMan, renderMode);
Commit: bc6573237d184606b627b1141077e51c514ba8aa
https://github.com/scummvm/scummvm/commit/bc6573237d184606b627b1141077e51c514ba8aa
Author: athrxx (athrxx at scummvm.org)
Date: 2024-07-11T08:02:49+03:00
Commit Message:
SCI: add SCI 1 video mode detection for EGA and VGA grey scale
Changed paths:
common/gui_options.cpp
common/gui_options.h
common/rendermode.cpp
common/rendermode.h
engines/sci/detection_internal.h
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index e5280d7f1d4..61a9209b807 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -79,6 +79,7 @@ const struct GameOpt {
{ GUIO_RENDERCPC, "cpc" },
{ GUIO_RENDERZX, "zx" },
{ GUIO_RENDERC64, "c64" },
+ { GUIO_RENDERVGAGREY, "vgaGray" },
{ GUIO_GAMEOPTIONS1, "gameOption1" },
{ GUIO_GAMEOPTIONS2, "gameOption2" },
diff --git a/common/gui_options.h b/common/gui_options.h
index 4da34344b4a..912e6f6587e 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -70,6 +70,7 @@
#define GUIO_RENDERCPC "\x27"
#define GUIO_RENDERZX "\x28"
#define GUIO_RENDERC64 "\x29"
+#define GUIO_RENDERVGAGREY "\x2A"
#define GUIO_LINKSPEECHTOSFX "\x30"
#define GUIO_LINKMUSICTOSFX "\x31"
diff --git a/common/rendermode.cpp b/common/rendermode.cpp
index ba07e9a6769..8710fd8be99 100644
--- a/common/rendermode.cpp
+++ b/common/rendermode.cpp
@@ -53,6 +53,7 @@ const RenderModeDescription g_renderModes[] = {
{ "cpc", "Amstrad CPC", kRenderCPC },
{ "zx", "ZX Spectrum", kRenderZX },
{ "c64", "Commodore 64", kRenderC64 },
+ { "vgaGrey", "VGA Grey Scale", kRenderVGAGrey },
{nullptr, nullptr, kRenderDefault}
};
@@ -82,7 +83,8 @@ static const RenderGUIOMapping s_renderGUIOMapping[] = {
{ kRenderCGA_BW, GUIO_RENDERCGABW },
{ kRenderCPC, GUIO_RENDERCPC },
{ kRenderZX, GUIO_RENDERZX },
- { kRenderC64, GUIO_RENDERC64 }
+ { kRenderC64, GUIO_RENDERC64 },
+ { kRenderVGAGrey, GUIO_RENDERVGAGREY }
};
DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Hercules Green", "lowres")
diff --git a/common/rendermode.h b/common/rendermode.h
index 0e56c033e85..4a40bd347f4 100644
--- a/common/rendermode.h
+++ b/common/rendermode.h
@@ -64,7 +64,8 @@ enum RenderMode {
kRenderCGA_BW = 15,
kRenderCPC = 16,
kRenderZX = 17,
- kRenderC64 = 18
+ kRenderC64 = 18,
+ kRenderVGAGrey = 19
};
struct RenderModeDescription {
diff --git a/engines/sci/detection_internal.h b/engines/sci/detection_internal.h
index 2c9fdee58ab..fbd6892ddb5 100644
--- a/engines/sci/detection_internal.h
+++ b/engines/sci/detection_internal.h
@@ -134,7 +134,10 @@ static Common::String customizeGuiOptions(Common::Path gamePath, Common::String
{ SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, "CGA320BW.DRV", GUIO_RENDERCGABW },
{ SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, "CGA320M.DRV", GUIO_RENDERCGABW },
{ SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, "HERCMONO.DRV", GUIO_RENDERHERCAMBER },
- { SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, "HERCMONO.DRV", GUIO_RENDERHERCGREEN }
+ { 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 }
};
Common::FSNode node(gamePath);
Commit: 975ba55f8b7156fe821b24e822263fa1cc724725
https://github.com/scummvm/scummvm/commit/975ba55f8b7156fe821b24e822263fa1cc724725
Author: athrxx (athrxx at scummvm.org)
Date: 2024-07-11T08:02:49+03:00
Commit Message:
SCI: add SCI 1 video modes for EGA and VGA grey scale
Changed paths:
engines/sci/graphics/gfxdrivers.cpp
engines/sci/graphics/gfxdrivers.h
engines/sci/graphics/screen.cpp
engines/sci/sci.cpp
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 4e69a8bc6c4..429b97088af 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -829,6 +829,249 @@ void SCI0_HerculesDriver::setupRenderProc() {
const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
+
+SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, rgbRendering), _greyScalePalette(nullptr) {
+ _greyScalePalette = new byte[_numColors * 3]();
+}
+
+SCI1_VGAGreyScaleDriver::~SCI1_VGAGreyScaleDriver() {
+ delete[] _greyScalePalette;
+}
+
+void SCI1_VGAGreyScaleDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
+ GFXDRV_ASSERT_READY;
+ byte *d = _greyScalePalette;
+ for (uint i = 0; i < num; ++i) {
+ // In the driver files I inspected there were never any other color distributions than this.
+ // So I guess it is safe to hardcode that instead of loading it from the driver file.
+ d[0] = d[1] = d[2] = (colors[0] * 77 + colors[1] * 150 + colors[2] * 28) >> 8;
+ colors += 3;
+ d += 3;
+ }
+
+ GfxDefaultDriver::setPalette(_greyScalePalette, start, num, update, palMods, palModMapping);
+}
+
+const char *SCI1_VGAGreyScaleDriver::_driverFile = "VGA320BW.DRV";
+
+SCI1_EGADriver::SCI1_EGADriver(bool rgbRendering) : GfxDriver(320, 200, 256, 1), _requestRGBMode(rgbRendering), _egaColorPatterns(nullptr), _egaMatchTable(nullptr),
+ _currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _ready(false) {
+ Common::File drv;
+ if (!drv.open(_driverFile))
+ error("SCI1_EGADriver: 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) || drv.readUint32LE() != 0xFEDCBA98 || !drv.skip(4))
+ error("SCI1_EGADriver: Driver file '%s' unknown version", _driverFile);
+
+ drv.skip(drv.readByte() == 0x90 ? 20 : 19);
+
+ drv.seek(drv.readUint16LE());
+ byte *buff = new byte[128];
+ drv.read(buff, 128);
+
+ uint16 tableOffs = 0;
+ for (int i = 0; i < 120 && !tableOffs; ++i) {
+ uint32 c = READ_BE_UINT32(buff + i);
+ if (c == 0x8BD82E8A) {
+ if (buff[i + 4] == 0x87)
+ tableOffs = READ_LE_UINT16(buff + i + 5);
+ } else if (c == 0xD0E8D0E8) {
+ for (int ii = 4; ii < 14; ++ii) {
+ if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
+ c = READ_BE_UINT32(buff + i + ii + 2);
+ if ((c & 0xFFFFFF) == 0x83F83F)
+ _colAdjust = c >> 24;
+ }
+ }
+ }
+ }
+ delete[] buff;
+
+ if (!tableOffs)
+ error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
+
+ drv.seek(tableOffs);
+ byte *table = new byte[512]();
+ drv.read(table, 512);
+ _egaMatchTable = table;
+
+ if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
+ error("SCI1_EGADriver: Driver file '%s' unknown version", _driverFile);
+
+ drv.close();
+}
+
+SCI1_EGADriver::~SCI1_EGADriver() {
+ delete[] _egaMatchTable;
+ delete[] _egaColorPatterns;
+ delete[] _compositeBuffer;
+ delete[] _currentBitmap;
+ delete[] _currentPalette;
+ delete[] _internalPalette;
+}
+
+template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal) {
+ const T *p = reinterpret_cast<const T*>(pal);
+ T *d1 = reinterpret_cast<T*>(dst);
+ T *d2 = d1 + (w << 1);
+
+ for (int i = 0; i < w; ++i) {
+ byte pt = patterns[*src++];
+ if (sizeof(T) == 1) {
+ *d1++ = *d2++ = pt >> 4;
+ *d1++ = *d2++ = pt & 0x0f;
+ } else {
+ *d1++ = *d2++ = p[pt >> 4];
+ *d1++ = *d2++ = p[pt & 0x0f];
+ }
+ }
+ dst = reinterpret_cast<byte*>(d2);
+}
+
+void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
+ Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
+ initGraphics(_screenW << 1, _screenH << 1, _requestRGBMode ? nullptr : &format);
+ format = g_system->getScreenFormat();
+ _pixelSize = format.bytesPerPixel;
+
+ if (_requestRGBMode && _pixelSize == 1)
+ warning("SCI1_EGADriver::initScreen(): RGB rendering not available in this ScummVM build");
+
+ static const byte egaColors[48] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+ 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+ };
+
+ if (_pixelSize == 1) {
+ g_system->getPaletteManager()->setPalette(egaColors, 0, ARRAYSIZE(egaColors) / 3);
+ } else {
+ byte *rgbpal = new byte[_numColors * _pixelSize]();
+ assert(rgbpal);
+
+ if (_pixelSize == 2)
+ updateRGBPalette<uint16>(rgbpal, egaColors, 0, ARRAYSIZE(egaColors) / 3, format);
+ else if (_pixelSize == 4)
+ updateRGBPalette<uint32>(rgbpal, egaColors, 0, ARRAYSIZE(egaColors) / 3, format);
+ else
+ error("SCI1_EGADriver::initScreen(): Unsupported screen format");
+ _internalPalette = rgbpal;
+ CursorMan.replaceCursorPalette(egaColors, 0, ARRAYSIZE(egaColors) / 3);
+ }
+
+ _compositeBuffer = new byte[(_screenW << 1) * (_screenH << 1) * _pixelSize]();
+ assert(_compositeBuffer);
+ _currentBitmap = new byte[_screenW * _screenH]();
+ assert(_currentBitmap);
+ _currentPalette = new byte[256 * 3]();
+ assert(_currentPalette);
+ _egaColorPatterns = new byte[256]();
+ assert(_egaColorPatterns);
+
+ static const LineProc lineProcs[] = {
+ &ega640RenderLine<byte>,
+ &ega640RenderLine<uint16>,
+ &ega640RenderLine<uint32>
+ };
+
+ assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
+ _renderLine = lineProcs[_pixelSize >> 1];
+
+ _ready = true;
+}
+
+void SCI1_EGADriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) {
+ GFXDRV_ASSERT_READY;
+ memcpy(_currentPalette + start * 3, colors, num * 3);
+ byte *d = &_egaColorPatterns[start];
+ for (uint i = 0; i < num; ++i) {
+ *d++ = _egaMatchTable[((MIN<byte>((colors[0] >> 2) + _colAdjust, 63) & 0x38) << 3) | (MIN<byte>((colors[1] >> 2) + _colAdjust, 63) & 0x38) | (MIN<byte>((colors[2] >> 2) + _colAdjust, 63) >> 3)];
+ colors += 3;
+ }
+ if (update)
+ copyRectToScreen(_currentBitmap, _screenW, 0, 0, _screenW, _screenH, nullptr, nullptr);
+}
+
+void SCI1_EGADriver::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h, const PaletteMod*, const byte*) {
+ GFXDRV_ASSERT_READY;
+ GFXDRV_ASSERT_ALIGNED; // We can't fix the boundaries here, it has to happen before this call
+
+ if (src != _currentBitmap)
+ updateBitmapBuffer(_currentBitmap, _screenW, src, pitch, x, y, w, h);
+
+ byte *dst = _compositeBuffer;
+ for (int i = 0; i < h; ++i) {
+ _renderLine(dst, src, w, _egaColorPatterns, _internalPalette);
+ src += pitch;
+ }
+
+ g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, x << 1, y << 1, w << 1, h << 1);
+}
+
+void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
+ const byte *s = reinterpret_cast<const byte*>(cursor);
+ int dstPitch = (w << 1);
+ byte *d1 = _compositeBuffer;
+ byte *d2 = _compositeBuffer + dstPitch;
+ uint32 newKeyColor = 0xFF;
+
+ for (uint i = 0; i < h; ++i) {
+ for (uint ii = 0; ii < w; ++ii) {
+ byte col = *s++;
+ if (col == keycolor) {
+ *d1++ = *d2++ = newKeyColor;
+ *d1++ = *d2++ = newKeyColor;
+ } else {
+ byte pt = _egaColorPatterns[col];
+ *d1++ = *d2++ = pt >> 4;
+ *d1++ = *d2++ = pt & 0x0f;
+ }
+ }
+ d1 += dstPitch;
+ d2 += dstPitch;
+ }
+
+ keycolor = newKeyColor;
+
+ CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
+}
+
+void SCI1_EGADriver::copyCurrentBitmap(byte *dest, uint32 size) const {
+ GFXDRV_ASSERT_READY;
+ assert(dest);
+ assert(size <= (uint32)(_screenW * _screenH));
+ memcpy(dest, _currentBitmap, size);
+}
+
+void SCI1_EGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
+ GFXDRV_ASSERT_READY;
+ assert(dest);
+ assert(start + num <= 256);
+ memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
+}
+
+Common::Point SCI1_EGADriver::getMousePos() const {
+ Common::Point res = GfxDriver::getMousePos();
+ res.x >>= 1;
+ res.y >>= 1;
+ return res;
+}
+
+void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
+ Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
+ GfxDriver::clearRect(r2);
+}
+
+const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
+
#undef GFXDRV_ASSERT_READY
#undef GFXDRV_ASEERT_ALIGNED
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 7d616c9cda8..4bb5ba10bfd 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -154,6 +154,46 @@ private:
static const char *_driverFile;
};
+class SCI1_VGAGreyScaleDriver final : public GfxDefaultDriver {
+public:
+ SCI1_VGAGreyScaleDriver(bool rgbRendering);
+ ~SCI1_VGAGreyScaleDriver() override;
+ void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
+ static bool validateMode() { return checkDriver(&_driverFile, 1); }
+private:
+ byte *_greyScalePalette;
+ static const char *_driverFile;
+};
+
+class SCI1_EGADriver final : public GfxDriver {
+public:
+ SCI1_EGADriver(bool rgbRendering);
+ ~SCI1_EGADriver() override;
+ void initScreen(const Graphics::PixelFormat*) override;
+ void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) override;
+ void copyRectToScreen(const byte *src, int pitch, int x, int y, 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 copyCurrentBitmap(byte *dest, uint32 size) const override;
+ void copyCurrentPalette(byte *dest, int start, int num) const override;
+ 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); }
+private:
+ bool _ready;
+ byte *_compositeBuffer;
+ byte *_currentBitmap;
+ byte *_currentPalette;
+ byte *_egaColorPatterns;
+ uint8 _colAdjust;
+ const byte *_internalPalette;
+ const byte *_egaMatchTable;
+ const bool _requestRGBMode;
+ typedef void (*LineProc)(byte*&, const byte*, int, const byte*, const byte*);
+ LineProc _renderLine;
+ 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 c0bb3b0e3e6..4a25fe6414c 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -189,6 +189,17 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
default:
break;
}
+ } else {
+ switch (renderMode) {
+ case Common::kRenderEGA:
+ _gfxDrv = new SCI1_EGADriver(requestRGB);
+ break;
+ case Common::kRenderVGAGrey:
+ _gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
+ break;
+ default:
+ break;
+ }
}
if (_gfxDrv == nullptr)
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 29a9d823a25..49baa8fab72 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -327,7 +327,9 @@ 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.
- if ((renderMode == Common::kRenderEGA && ((getSciVersion() <= SCI_VERSION_0_LATE || getSciVersion() == SCI_VERSION_1_EGA_ONLY) && undither)) ||
+ 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()))
More information about the Scummvm-git-logs
mailing list