[Scummvm-git-logs] scummvm master -> bf48bc41b3f3154b9c0c490ed8ec58bc286098b9
bluegr
noreply at scummvm.org
Wed Oct 9 05:35:19 UTC 2024
This automated email contains information about 13 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
6e59e0a1ac SCI: add some asserts to gfx drivers
70414a0b87 SCI: make upscaled gfx driver for KQ6 Windows
71a2af0de2 SCI: more accurate KQ6 video handling
5f66d7161a SCI: add 16 colors mode for KQ6 Win
c11193f35c COMMON: add Windows 16 colors render mode option
a4380b8e0b SCI: add appropriate render options for KQ6 Win
80258d543a SCI: re-enable hi-res option for KQ6 Windows
2ec9c46373 SCI: replace workaround to clear hires portrait rects
de32f308b4 SCI: remove GFX_SCREEN_MASK_DISPLAY
6be167aeaf SCI: cleanup
523add9022 SCI: revert unneeded video code changes
c0492ece44 SCI: work around original script/interpreter bug
bf48bc41b3 SCI: (KQ6 Gfx driver) - fix monochrome mouse cursors in non-rgb rendering mode
Commit: 6e59e0a1acaa90cef0873b67f50b79bb2f61f97d
https://github.com/scummvm/scummvm/commit/6e59e0a1acaa90cef0873b67f50b79bb2f61f97d
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: add some asserts to gfx drivers
(I have noticed that the (not really well supported) Mac SCI0 versions
with 480x300 pixels resolution may pass invalid (negative) withs or
heights)
Changed paths:
engines/sci/graphics/gfxdrivers.cpp
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 30b334cc1b1..48d9cebf079 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -237,6 +237,7 @@ void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch,
void GfxDefaultDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
+ assert (h >= 0 && w >= 0);
src += (srcY * pitch + srcX * _srcPixelSize);
if (src != _currentBitmap)
@@ -517,7 +518,7 @@ void SCI0_CGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int p
int ty = destY;
for (int i = 0; i < h; ++i) {
- _renderLine(dst, src, w, destX & 3, ++ty, _cgaPatterns, _internalPalette);
+ _renderLine(dst, src, w, srcX & 3, ++ty, _cgaPatterns, _internalPalette);
src += pitch;
}
@@ -651,7 +652,7 @@ void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int
src += (srcY * pitch + srcX);
for (int i = 0; i < h; ++i) {
- _renderLine(dst, src, w, destX & 3, ty, _monochromePatterns, _internalPalette);
+ _renderLine(dst, src, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
ty = (ty + 1) & 7;
src += pitch;
}
@@ -802,7 +803,7 @@ void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int srcX, int srcY,
for (int i = 0; i < h; ++i) {
const byte *src2 = src;
- _renderLine(dst, src2, w, destX & 3, ty, _monochromePatterns, _internalPalette);
+ _renderLine(dst, src2, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
ty = (ty + 1) & 7;
++rh;
@@ -1088,6 +1089,7 @@ void SCI1_EGADriver::setPalette(const byte *colors, uint start, uint num, bool u
void SCI1_EGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
+ assert (h >= 0 && w >= 0);
src += (srcY * pitch + srcX);
@@ -1240,6 +1242,8 @@ void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, boo
void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
+ assert (h >= 0 && w >= 0);
+
src += (srcY * pitch + srcX * _srcPixelSize);
if (src != _currentBitmap)
updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
@@ -1729,6 +1733,7 @@ void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format)
void SCI1_PC98Gfx8ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
+ assert (h >= 0 && w >= 0);
byte diff = srcX & 7;
srcX &= ~7;
Commit: 70414a0b87c2e0ce119d7c75fbfdff61556d5100
https://github.com/scummvm/scummvm/commit/70414a0b87c2e0ce119d7c75fbfdff61556d5100
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: make upscaled gfx driver for KQ6 Windows
- The main purpose was to move more upscaling logic out of the engine
- This actually contains various precision fixes to the hires code. There
were some comments about being unsure what to. I think I managed to
fix all of these, although it shouldn't make a visible difference, since the
workarounds worked as intended.
- It does actually fix a very minor glitch: The portraits were vertically off
by one pixel. And the opening AVI video is probably also vertically fixed
by one pixel (although the video display in the original is not done very
well, anyway).
Changed paths:
engines/sci/engine/kgraphics.cpp
engines/sci/engine/kmisc.cpp
engines/sci/graphics/gfxdrivers.cpp
engines/sci/graphics/gfxdrivers.h
engines/sci/graphics/paint16.cpp
engines/sci/graphics/paint16.h
engines/sci/graphics/portrait.cpp
engines/sci/graphics/portrait.h
engines/sci/graphics/screen.cpp
engines/sci/graphics/screen.h
engines/sci/graphics/view.cpp
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 36d5a50453b..a79950e52e1 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -273,7 +273,7 @@ reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
- return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
+ return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask, false);
}
reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv) {
@@ -309,8 +309,9 @@ reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
// argv[4] is the map (1 for visual, etc.)
// argc == 6 on upscaled hires
- bool hiresMode = (argc > 5) ? true : false;
- g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
+ // The original interpreter skips the update if either argc > 5 or argv[5] != 0.
+ if (argc <= 5 || argv[5].isNull())
+ g_sci->_gfxPaint16->kernelGraphUpdateBox(rect);
return s->r_acc;
}
@@ -328,7 +329,9 @@ reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
- return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
+ uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
+ s->r_acc = g_sci->_gfxScreen->gfxDriver()->supportsHiResGraphics() ? g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask, true) : NULL_REG;
+ return s->r_acc;
}
reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index a294b6ada52..2b02ab77b91 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -39,6 +39,8 @@
#ifdef ENABLE_SCI32
#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
+#include "sci/graphics/screen.h"
+#include "sci/graphics/gfxdrivers.h"
#endif
#include "sci/graphics/maciconbar.h"
#include "sci/console.h"
@@ -771,7 +773,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
enum Operation {
kPlatformUnknown = 0,
kPlatformGetPlatform = 4,
- kPlatformUnknown5 = 5,
+ kPlatformIsSmallWindow = 5,
kPlatformIsHiRes = 6,
kPlatformWin311OrHigher = 7
};
@@ -803,9 +805,8 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, kSciPlatformMacintosh);
else
return make_reg(0, kSciPlatformDOS);
- case kPlatformUnknown5:
- // KQ6: subop 5 needs to return the opposite of subop 6 to get hires graphics
- return make_reg(0, !isWindows);
+ case kPlatformIsSmallWindow:
+ return make_reg(0, !g_sci->_gfxScreen->gfxDriver()->supportsHiResGraphics());
case kPlatformIsHiRes:
case kPlatformWin311OrHigher:
return make_reg(0, isWindows);
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 48d9cebf079..368b86a1aab 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -704,6 +704,10 @@ void SCI0_CGABWDriver::clearRect(const Common::Rect &r) const {
GfxDriver::clearRect(r2);
}
+Common::Point SCI0_CGABWDriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point(pos.x << 1, pos.y << 1);
+}
+
template <typename T> void cgabwRenderLine_v1(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
const T *p = reinterpret_cast<const T*>(pal);
const uint16 *patterns16 = reinterpret_cast<const uint16*>(patterns);
@@ -869,6 +873,10 @@ void SCI0_HerculesDriver::clearRect(const Common::Rect &r) const {
GfxDriver::clearRect(r2);
}
+Common::Point SCI0_HerculesDriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
+}
+
template <typename T> void herculesRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
T *d = reinterpret_cast<T*>(dst);
const T *p = reinterpret_cast<const T*>(pal);
@@ -1171,6 +1179,10 @@ void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
GfxDriver::clearRect(r2);
}
+Common::Point SCI1_EGADriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point(pos.x << 1, pos.y << 1);
+}
+
const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h) {
@@ -1193,11 +1205,15 @@ 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, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
- _scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _cursorWidth(0), _cursorHeight(0) {
- _virtualW = screenWidth;
- _virtualH = screenHeight;
+UpscaledGfxDriver::UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering) :
+ UpscaledGfxDriver(640, 400, textAlignX, scaleCursor, rgbRendering) {
+}
+
+UpscaledGfxDriver::UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering) :
+ GfxDefaultDriver(scaledW, scaledH, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
+ _scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _cursorWidth(0), _cursorHeight(0), _hScaleMult(2), _vScaleMult(2), _vScaleDiv(1) {
+ _virtualW = 320;
+ _virtualH = 200;
}
UpscaledGfxDriver::~UpscaledGfxDriver() {
@@ -1248,12 +1264,17 @@ void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, in
if (src != _currentBitmap)
updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
+ int realWidth = 0;
+ int realHeight = 0;
+
// We need to scale and color convert the bitmap in separate functions, because we want
// to keep the scaled non-color-modified bitmap for palette updates in rgb rendering mode.
- byte *scb = _scaledBitmap + (destY << 1) * _screenW * _srcPixelSize + (destX << 1) * _srcPixelSize;
- _renderScaled(scb, src, pitch, w, h);
+ renderBitmap(src, pitch, destX, destY, w, h, realWidth, realHeight);
- updateScreen(destX << 1, destY << 1, w << 1, h << 1, palMods, palModMapping);
+ Common::Point p(destX, destY);
+ p = getRealCoords(p);
+
+ updateScreen(p.x, p.y, realWidth, realHeight, palMods, palModMapping);
}
void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
@@ -1269,24 +1290,28 @@ void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int ho
Common::Point UpscaledGfxDriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
- res.x >>= 1;
- res.y >>= 1;
+ res.x /= _hScaleMult;
+ res.y = res.y * _vScaleDiv / _vScaleMult;
return res;
}
void UpscaledGfxDriver::setMousePos(const Common::Point &pos) const {
- g_system->warpMouse(pos.x << 1, pos.y << 1);
+ g_system->warpMouse(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
}
void UpscaledGfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
- g_system->setShakePos(shakeXOffset << 1, shakeYOffset << 1);
+ g_system->setShakePos(shakeXOffset * _hScaleMult, shakeYOffset * _vScaleMult / _vScaleDiv);
}
void UpscaledGfxDriver::clearRect(const Common::Rect &r) const {
- Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
+ Common::Rect r2(r.left * _hScaleMult, r.top * _vScaleMult / _vScaleDiv, r.right * _hScaleMult, r.bottom * _vScaleMult / _vScaleDiv);
GfxDriver::clearRect(r2);
}
+Common::Point UpscaledGfxDriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
+}
+
void UpscaledGfxDriver::drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
hiresDestX &= ~(_textAlignX - 1);
@@ -1326,8 +1351,184 @@ void UpscaledGfxDriver::adjustCursorBuffer(uint16 newWidth, uint16 newHeight) {
}
}
+void UpscaledGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
+ byte *scb = _scaledBitmap + (dy << 1) * _screenW * _srcPixelSize + (dx << 1) * _srcPixelSize;
+ _renderScaled(scb, src, pitch, w, h);
+ realWidth = w << 1;
+ realHeight = h << 1;
+}
+
+KQ6WinGfxDriver::KQ6WinGfxDriver(bool scaleCursor, bool smallWindow,bool rgbRendering) :
+ UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, scaleCursor && !smallWindow, rgbRendering), _smallWindow(smallWindow),
+ _renderLine(nullptr), _renderLine2(nullptr), _flags(0), _colorMap(nullptr), _vScaleMult2(smallWindow ? 1 : 2) {
+ _virtualW = 320;
+ _virtualH = 200;
+ if (smallWindow)
+ _hScaleMult = 1;
+ _vScaleMult = _smallWindow ? 6 : 11;
+ _vScaleDiv = 5;
+}
+
+void largeWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
+ int dstPitch = pitch;
+ int dstPitch2 = pitch - (w << 1);
+ byte *d1 = dst;
+ byte *d2 = d1 + dstPitch;
+
+ if (ty == 5) {
+ byte *d3 = d2 + dstPitch;
+ for (int i = 0; i < w; ++i) {
+ d1[0] = d1[1] = d2[0] = d2[1] = d3[0] = d3[1] = *src++;
+ d1 += 2;
+ d2 += 2;
+ d3 += 2;
+ }
+ dst = d3 + dstPitch2;
+ } else {
+ for (int i = 0; i < w; ++i) {
+ d1[0] = d1[1] = d2[0] = d2[1] = *src++;
+ d1 += 2;
+ d2 += 2;
+ }
+ dst = d2 + dstPitch2;
+ }
+}
+
+void largeWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
+ int dstPitch = pitch;
+ int dstPitch2 = pitch - (w << 1);
+ byte *d1 = dst;
+ byte *d2 = d1 + dstPitch;
+
+ for (int i = 0; i < w; ++i) {
+ d1[0] = d1[1] = d2[0] = d2[1] = *src++;
+ d1 += 2;
+ d2 += 2;
+ }
+ dst = d2 + dstPitch2;
+}
+
+void smallWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
+ int dstPitch = pitch;
+ int dstPitch2 = pitch - w;
+ byte *d1 = dst;
+
+ if (ty == 5) {
+ byte *d2 = d1 + dstPitch;
+ for (int i = 0; i < w; ++i)
+ *d1++ = *d2++ = *src++;
+ dst = d2 + dstPitch2;
+ } else {
+ for (int i = 0; i < w; ++i)
+ *d1++ = *src++;
+ dst = d1 + dstPitch2;
+ }
+}
+
+void smallWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
+ int dstPitch = pitch - w;
+ byte *d1 = dst;
+
+ for (int i = 0; i < w; ++i)
+ *d1++ = *src++;
+ dst = d1 + dstPitch;
+}
+
+void hiresRenderLine(byte *&dst, const byte *src, int pitch, int w, const byte *colorMap) {
+ if (!colorMap) {
+ memcpy(dst, src, w);
+ } else {
+ byte *d = dst;
+ for (int i = 0; i < w; ++i)
+ *d++ = colorMap[*src++];
+ }
+ dst += pitch;
+}
+
+void renderLineDummy(byte *&, const byte* , int, int, const byte*) {
+}
+
+void KQ6WinGfxDriver::initScreen(const Graphics::PixelFormat *format) {
+ UpscaledGfxDriver::initScreen(format);
+ _renderLine = _smallWindow ? &smallWindowRenderLine : &largeWindowRenderLine;
+ _renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
+}
+
+void KQ6WinGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
+ GFXDRV_ASSERT_READY;
+ assert (h >= 0 && w >= 0);
+
+ if (!(_flags & (kHiResMode | kMovieMode))) {
+ UpscaledGfxDriver::copyRectToScreen(src, srcX, srcY, pitch, destX, destY, w, h, palMods, palModMapping);
+ return;
+ }
+
+ if (_flags & kMovieMode) {
+ destX = (_screenW >> 1) - (w & ~1) * _hScaleMult / 2;
+ destY = (_screenH >> 1) - (h & ~1) * _vScaleMult2 / 2;
+ }
+
+ src += (srcY * pitch + srcX * _srcPixelSize);
+ byte *dst = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
+
+ for (int i = 0; i < h; ++i) {
+ _renderLine2(dst, src, _screenW, w, _colorMap);
+ src += pitch;
+ }
+
+ if (_flags & kMovieMode) {
+ w *= _hScaleMult;
+ h *= _vScaleMult2;
+ }
+
+ updateScreen(destX, destY, w, h, palMods, palModMapping);
+}
+
+Common::Point KQ6WinGfxDriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult2 + (pos.y + 4) / 5);
+}
+
+void KQ6WinGfxDriver::setFlags(uint32 flags) {
+ flags ^= (_flags & flags);
+ if (!flags)
+ return;
+
+ if (flags & kMovieMode)
+ _renderLine2 = _smallWindow ? &smallWindowRenderLineMovie : &largeWindowRenderLineMovie;
+
+ _flags |= flags;
+}
+
+void KQ6WinGfxDriver::clearFlags(uint32 flags) {
+ flags &= _flags;
+ if (!flags)
+ return;
+
+ if (flags & kMovieMode)
+ _renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
+
+ _flags &= ~flags;
+}
+
+void KQ6WinGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
+ assert(_renderLine);
+
+ byte *dst = _scaledBitmap + (dy * _vScaleMult2 + (dy + 4) / 5) * _screenW * _srcPixelSize + dx *_hScaleMult * _srcPixelSize;
+ const byte *dstart = dst;
+ dy = (dy + 4) % 5;
+
+ while (h--) {
+ _renderLine(dst, src, _screenW, w, ++dy);
+ dy %= 5;
+ src += pitch;
+ }
+
+ realWidth = w * _hScaleMult;
+ realHeight = (dst - dstart) / _screenW;
+}
+
PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette) :
- UpscaledGfxDriver(320, 200, textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
+ UpscaledGfxDriver(textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
_cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
// Palette taken from driver file (identical for all versions of the
// driver I have seen so far, also same for SCI0 and SCI1)
@@ -1515,7 +1716,7 @@ byte PC98Gfx16ColorsDriver::remapTextColor(byte color) const {
}
SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering) :
- UpscaledGfxDriver(320, 200, 8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _useTextMode(useTextModeForSJISChars), _convPalette(nullptr) {
+ UpscaledGfxDriver(8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _useTextMode(useTextModeForSJISChars), _convPalette(nullptr) {
byte *col = new byte[8 * 3]();
_convPalette = col;
@@ -1616,7 +1817,7 @@ byte SCI0_PC98Gfx8ColorsDriver::remapTextColor(byte color) const {
const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
-SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(320, 200, 1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
+SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
Common::File drv;
if (!drv.open(_driverFile))
error("SCI1_PC98Gfx8ColorsDriver: Failed to open '%s'", _driverFile);
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index c0d35a8f44c..02286a78e6d 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -36,6 +36,11 @@ struct PaletteMod;
class GfxDriver {
public:
+ enum DrawFlags : uint32 {
+ kHiResMode = 1 << 0,
+ kMovieMode = 1 << 1
+ };
+
GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _ready(false), _pixelSize(1) {}
virtual ~GfxDriver() {}
virtual void initScreen(const Graphics::PixelFormat *srcRGBFormat = nullptr) = 0; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
@@ -51,10 +56,16 @@ public:
virtual void copyCurrentPalette(byte *dest, int start, int num) const;
virtual void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) = 0;
virtual byte remapTextColor(byte color) const { return color; }
+ virtual void setColorMap(const byte *colorMap) {}
+ virtual Common::Point getRealCoords(Common::Point &pos) const { return pos; }
+ virtual void setFlags(uint32 flags) {}
+ virtual void clearFlags(uint32 flags) {}
virtual bool supportsPalIntensity() const = 0;
+ virtual bool supportsHiResGraphics() const = 0;
virtual bool driverBasedTextRendering() const = 0;
uint16 numColors() const { return _numColors; }
byte pixelSize() const { return _pixelSize; }
+
protected:
bool _ready;
static bool checkDriver(const char *const *driverNames, int listSize);
@@ -77,6 +88,7 @@ public:
void copyCurrentPalette(byte *dest, int start, int num) const override;
void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
bool supportsPalIntensity() const override { return true; }
+ bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
protected:
void updatePalette(const byte *colors, uint start, uint num);
@@ -110,6 +122,7 @@ public:
void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
void copyCurrentPalette(byte *dest, int start, int num) const override;
bool supportsPalIntensity() const override { return false; }
+ bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
protected:
void assignPalette(const byte *colors);
@@ -148,6 +161,7 @@ public:
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(_driverFiles, 2); }
private:
void setupRenderProc() override;
@@ -169,6 +183,7 @@ public:
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
private:
void setupRenderProc() override;
@@ -208,7 +223,9 @@ public:
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
bool supportsPalIntensity() const override { return false; }
+ bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
private:
@@ -227,7 +244,7 @@ private:
class UpscaledGfxDriver : public GfxDefaultDriver {
public:
- UpscaledGfxDriver(uint16 screenWidth, uint16 screenHeight, uint16 textAlignX, bool scaleCursor, bool rgbRendering);
+ UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering);
~UpscaledGfxDriver() override;
void initScreen(const Graphics::PixelFormat *format) override;
void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
@@ -237,9 +254,11 @@ public:
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) override; // For HiRes fonts. PC-98 versions bypass the video driver for this and render directly on top of the vram.
bool driverBasedTextRendering() const override { return true; }
protected:
+ UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering);
void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
void adjustCursorBuffer(uint16 newWidth, uint16 newHeight);
typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int);
@@ -247,14 +266,41 @@ protected:
typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
ScaledRenderProc _renderScaled;
uint16 _textAlignX;
+ uint16 _hScaleMult;
+ uint16 _vScaleMult;
+ uint16 _vScaleDiv;
byte *_scaledBitmap;
private:
+ virtual void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight);
const bool _scaleCursor;
uint16 _cursorWidth;
uint16 _cursorHeight;
bool _needCursorBuffer;
};
+class KQ6WinGfxDriver : public UpscaledGfxDriver {
+public:
+ KQ6WinGfxDriver(bool scaleCursor, bool smallWindow, bool rgbRendering);
+ ~KQ6WinGfxDriver() override {}
+ void initScreen(const Graphics::PixelFormat *format) override;
+ void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
+ void setColorMap(const byte *colorMap) { _colorMap = colorMap; }
+ void setFlags(uint32 flags) override;
+ void clearFlags(uint32 flags) override;
+ bool supportsHiResGraphics() const override { return !_smallWindow; }
+private:
+ typedef void (*LineProc)(byte*&, const byte*, int, int, int);
+ LineProc _renderLine;
+ typedef void (*LineProcSpec)(byte*&, const byte*, int, int, const byte*);
+ LineProcSpec _renderLine2;
+ void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) override;
+ uint32 _flags;
+ const byte *_colorMap;
+ const bool _smallWindow;
+ uint16 _vScaleMult2;
+};
+
class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
public:
enum SjisFontStyle {
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index db6dbb7a3ab..6b1a1164100 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -33,6 +33,7 @@
#include "sci/graphics/picture.h"
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
+#include "sci/graphics/gfxdrivers.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/portrait.h"
#include "sci/graphics/text16.h"
@@ -134,55 +135,27 @@ void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common:
// screen. Hires-cels are available only SCI 1.1+.
void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, reg_t upscaledHiresHandle, uint16 scaleX, uint16 scaleY) {
GfxView *view = _cache->getView(viewId);
- Common::Rect celRect, curPortRect, clipRect, clipRectTranslated;
- Common::Point curPortPos;
- bool upscaledHiresHack = false;
+ if (!view)
+ return;
- if (view) {
- if ((leftPos == 0) && (topPos == 0)) {
- // HACK: in kq6, we get leftPos&topPos == 0 SOMETIMES, that's why we
- // need to get coordinates from upscaledHiresHandle. I'm not sure if
- // this is what we are supposed to do or if there is some other bug
- // that actually makes coordinates to be 0 in the first place.
- byte *memoryPtr = nullptr;
- memoryPtr = _segMan->getHunkPointer(upscaledHiresHandle);
- if (memoryPtr) {
- Common::Rect upscaledHiresRect;
- _screen->bitsGetRect(memoryPtr, &upscaledHiresRect);
- leftPos = upscaledHiresRect.left;
- topPos = upscaledHiresRect.top;
- upscaledHiresHack = true;
- }
- }
+ byte *memoryPtr = _segMan->getHunkPointer(upscaledHiresHandle);
+ if (!memoryPtr)
+ error("drawHiresCelAndShow: Invalid hires handle");
- celRect.left = leftPos;
- celRect.top = topPos;
- celRect.right = celRect.left + view->getWidth(loopNo, celNo);
- celRect.bottom = celRect.top + view->getHeight(loopNo, celNo);
- // adjust curPort to upscaled hires
- clipRect = celRect;
- curPortRect = _ports->_curPort->rect;
- view->adjustToUpscaledCoordinates(curPortRect.top, curPortRect.left);
- view->adjustToUpscaledCoordinates(curPortRect.bottom, curPortRect.right);
- curPortRect.bottom++;
- curPortRect.right++;
- clipRect.clip(curPortRect);
- if (clipRect.isEmpty()) // nothing to draw
- return;
-
- clipRectTranslated = clipRect;
- if (!upscaledHiresHack) {
- curPortPos.x = _ports->_curPort->left; curPortPos.y = _ports->_curPort->top;
- view->adjustToUpscaledCoordinates(curPortPos.y, curPortPos.x);
- clipRectTranslated.top += curPortPos.y; clipRectTranslated.bottom += curPortPos.y;
- clipRectTranslated.left += curPortPos.x; clipRectTranslated.right += curPortPos.x;
- }
+ Common::Rect upscaledHiresRect;
+ _screen->bitsGetRect(memoryPtr, &upscaledHiresRect);
+ Common::Point topLeft(upscaledHiresRect.left, upscaledHiresRect.top);
+ Common::Point bottomRight(upscaledHiresRect.right, upscaledHiresRect.bottom);
- view->draw(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo, true);
- if (!_screen->_picNotValidSci11) {
- _screen->copyDisplayRectToScreen(clipRectTranslated);
- }
- }
+ topLeft = _screen->gfxDriver()->getRealCoords(topLeft);
+ bottomRight = _screen->gfxDriver()->getRealCoords(bottomRight);
+
+ Common::Rect celRect(view->getWidth(loopNo, celNo), view->getHeight(loopNo, celNo));
+ Common::Rect clipRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
+ celRect.translate(leftPos + topLeft.x, topPos + topLeft.y);
+ clipRect.clip(celRect);
+
+ view->draw(celRect, clipRect, clipRect, loopNo, celNo, priority, paletteNo, true);
}
void GfxPaint16::clearScreen(byte color) {
@@ -300,6 +273,7 @@ void GfxPaint16::frameRect(const Common::Rect &rect) {
void GfxPaint16::bitsShow(const Common::Rect &rect) {
Common::Rect workerRect(rect.left, rect.top, rect.right, rect.bottom);
+
workerRect.clip(_ports->_curPort->rect);
if (workerRect.isEmpty()) // nothing to show
return;
@@ -312,29 +286,16 @@ void GfxPaint16::bitsShow(const Common::Rect &rect) {
_screen->copyRectToScreen(workerRect);
}
-
-void GfxPaint16::bitsShowHires(const Common::Rect &rect) {
- _screen->copyDisplayRectToScreen(rect);
-}
-
-reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask) {
+reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask, bool hiresFlag) {
reg_t memoryId;
byte *memoryPtr;
int size;
Common::Rect workerRect(rect.left, rect.top, rect.right, rect.bottom);
- workerRect.clip(_ports->_curPort->rect);
- if (workerRect.isEmpty()) // nothing to save
- return NULL_REG;
-
- if (screenMask == GFX_SCREEN_MASK_DISPLAY) {
- // The coordinates we are given are actually up-to-including right/bottom - we extend accordingly
- workerRect.bottom++;
- workerRect.right++;
- // Adjust rect to upscaled hires, but dont adjust according to port
- _screen->adjustToUpscaledCoordinates(workerRect.top, workerRect.left);
- _screen->adjustToUpscaledCoordinates(workerRect.bottom, workerRect.right);
- } else {
+ if (!hiresFlag) { // KQ6CD Win only does this if not called from the special kGraph 15 case (= kGraphSaveUpscaledHiresBox)
+ workerRect.clip(_ports->_curPort->rect);
+ if (workerRect.isEmpty()) // nothing to save
+ return NULL_REG;
_ports->offsetRect(workerRect);
}
@@ -402,9 +363,8 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
}
void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY, bool hiresMode, reg_t upscaledHiresHandle) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for
- // upscaled hires here
- if ((!hiresMode) || (!_screen->getUpscaledHires())) {
+ // some calls are hiresMode even under kq6 DOS, that's why we check for hires caps here
+ if (!hiresMode || !_screen->gfxDriver()->supportsHiResGraphics()) {
drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, scaleX, scaleY);
} else {
drawHiresCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, upscaledHiresHandle);
@@ -436,25 +396,16 @@ void GfxPaint16::kernelGraphDrawLine(Common::Point startPoint, Common::Point end
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
-reg_t GfxPaint16::kernelGraphSaveBox(const Common::Rect &rect, uint16 screenMask) {
- return bitsSave(rect, screenMask);
-}
-
-reg_t GfxPaint16::kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect) {
- return bitsSave(rect, GFX_SCREEN_MASK_DISPLAY);
+reg_t GfxPaint16::kernelGraphSaveBox(const Common::Rect &rect, uint16 screenMask, bool hiresFlag) {
+ return bitsSave(rect, screenMask, hiresFlag);
}
void GfxPaint16::kernelGraphRestoreBox(reg_t handle) {
bitsRestore(handle);
}
-void GfxPaint16::kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for
- // upscaled hires here
- if ((!hiresMode) || (!_screen->getUpscaledHires()))
- bitsShow(rect);
- else
- bitsShowHires(rect);
+void GfxPaint16::kernelGraphUpdateBox(const Common::Rect &rect) {
+ bitsShow(rect);
}
void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) {
@@ -648,7 +599,6 @@ void GfxPaint16::kernelPortraitShow(const Common::String &resourceName, Common::
// adjust given coordinates to curPort (but dont adjust coordinates on upscaledHires_Save_Box and give us hires coordinates
// on kDrawCel, yeah this whole stuff makes sense)
position.x += _ports->getPort()->left; position.y += _ports->getPort()->top;
- _screen->adjustToUpscaledCoordinates(position.y, position.x);
myPortrait->doit(position, resourceId, noun, verb, cond, seq);
delete myPortrait;
}
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index c290a76d353..c5da5f15ebd 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -57,8 +57,7 @@ public:
void frameRect(const Common::Rect &rect);
void bitsShow(const Common::Rect &r);
- void bitsShowHires(const Common::Rect &rect);
- reg_t bitsSave(const Common::Rect &rect, byte screenFlags);
+ reg_t bitsSave(const Common::Rect &rect, byte screenFlags, bool hiresFlag = false);
void bitsGetRect(reg_t memoryHandle, Common::Rect *destRect);
void bitsRestore(reg_t memoryHandle);
void bitsFree(reg_t memoryHandle);
@@ -71,10 +70,9 @@ public:
void kernelGraphFillBox(const Common::Rect &rect, uint16 colorMask, int16 color, int16 priority, int16 control);
void kernelGraphFrameBox(const Common::Rect &rect, int16 color);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
- reg_t kernelGraphSaveBox(const Common::Rect &rect, uint16 flags);
- reg_t kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect);
+ reg_t kernelGraphSaveBox(const Common::Rect &rect, uint16 flags, bool hiresFlag);
void kernelGraphRestoreBox(reg_t handle);
- void kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode);
+ void kernelGraphUpdateBox(const Common::Rect &rect);
void kernelGraphRedrawBox(Common::Rect rect);
reg_t kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv);
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 06a19cc81b0..5e4f28c95f8 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -26,6 +26,7 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/state.h"
+#include "sci/graphics/gfxdrivers.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/portrait.h"
@@ -245,8 +246,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
_palette->set(&_portraitPalette, false, true);
// Draw base bitmap
- drawBitmap(0);
- bitsShow();
+ drawBitmap(0, true);
// Start playing audio...
_audio->stopAudio();
@@ -341,9 +341,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
raveLipSyncBitmapNr--;
if (raveLipSyncBitmapNr < _bitmaps.size()) {
- drawBitmap(0);
- drawBitmap(raveLipSyncBitmapNr);
- bitsShow();
+ drawBitmap(0, false);
+ drawBitmap(raveLipSyncBitmapNr, true);
} else {
warning("kPortrait: rave lip sync data tried to draw non-existent bitmap %d", raveLipSyncBitmapNr);
}
@@ -391,9 +390,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Display animation bitmap
if (syncCue < _bitmapCount) {
if (syncCue)
- drawBitmap(0); // Draw base bitmap first to get valid animation frame
- drawBitmap(syncCue);
- bitsShow();
+ drawBitmap(0, false); // Draw base bitmap first to get valid animation frame
+ drawBitmap(syncCue, true);
} else {
warning("kPortrait: sync information tried to draw non-existent %d", syncCue);
}
@@ -402,8 +400,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
#endif
// Reset the portrait bitmap to "closed mouth" state (rave.dll seems to do the same)
- drawBitmap(0);
- bitsShow();
+ drawBitmap(0, true);
if (userAbort) {
_audio->stopAudio();
}
@@ -479,28 +476,18 @@ SciSpan<const byte> Portrait::raveGetLipSyncData(const uint16 raveID) {
return SciSpan<const byte>();
}
-void Portrait::drawBitmap(uint16 bitmapNr) {
+void Portrait::drawBitmap(uint16 bitmapNr, bool show) {
uint16 bitmapHeight = _bitmaps[bitmapNr].height;
uint16 bitmapWidth = _bitmaps[bitmapNr].width;
- Common::Point bitmapPosition = _position;
-
- bitmapPosition.x += _bitmaps[bitmapNr].displaceX;
- bitmapPosition.y += _bitmaps[bitmapNr].displaceY;
+ Common::Point pos = _screen->gfxDriver()->getRealCoords(_position);
+ pos.x += _bitmaps[bitmapNr].displaceX;
+ pos.y += _bitmaps[bitmapNr].displaceY;
const byte *data = _bitmaps[bitmapNr].rawBitmap.getUnsafeDataAt(0, bitmapWidth * bitmapHeight);
- for (int y = 0; y < bitmapHeight; y++) {
- for (int x = 0; x < bitmapWidth; x++) {
- _screen->putPixelOnDisplay(bitmapPosition.x + x, bitmapPosition.y + y, _portraitPalette.mapping[*data++]);
- }
- data += _bitmaps[bitmapNr].extraBytesPerLine;
- }
-}
+ _screen->copyHiResRectToScreen(data, bitmapWidth + _bitmaps[bitmapNr].extraBytesPerLine, pos.x, pos.y, bitmapWidth, bitmapHeight, _portraitPalette.mapping);
-void Portrait::bitsShow() {
- Common::Rect bitmapRect = Common::Rect(_width, _height);
- bitmapRect.moveTo(_position.x, _position.y);
- _screen->copyDisplayRectToScreen(bitmapRect);
- g_system->updateScreen();
+ if (show)
+ g_system->updateScreen();
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index 23ddde238da..530ca588bdb 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -49,8 +49,7 @@ public:
private:
void init();
- void drawBitmap(uint16 bitmapNr);
- void bitsShow();
+ void drawBitmap(uint16 bitmapNr, bool show);
int16 raveGetTicks(Resource *resource, uint *offset);
uint16 raveGetID(Resource *resource, uint *offset);
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 44880760b0d..b54c083b05b 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -54,15 +54,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
_curPaletteMapValue = 0;
_paletteModsEnabled = false;
- // King's Quest 6 has hires content in the Windows version which we also
- // allow to be optionally enabled in the DOS version.
- if (g_sci->getGameId() == GID_KQ6) {
- if ((g_sci->getPlatform() == Common::kPlatformWindows) ||
- (g_sci->getPlatform() == Common::kPlatformDOS && g_sci->useHiresGraphics())) {
- _upscaledHires = GFX_SCREEN_UPSCALED_640x440;
- }
- }
-
if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
if (getSciVersion() <= SCI_VERSION_01) {
// Macintosh SCI0 games used 480x300, while the scripts were running at 320x200
@@ -120,15 +111,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
for (int i = 0; i <= _scriptWidth; i++)
_upscaledWidthMapping[i] = i * 2;
break;
- case GFX_SCREEN_UPSCALED_640x440:
- // used by King's Quest 6 on Windows
- _displayWidth = 640;
- _displayHeight = 440;
- for (int i = 0; i <= _scriptHeight; i++)
- _upscaledHeightMapping[i] = (i * 11) / 5;
- for (int i = 0; i <= _scriptWidth; i++)
- _upscaledWidthMapping[i] = i * 2;
- break;
default:
if (!_displayWidth)
_displayWidth = _width;
@@ -217,9 +199,22 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
else
_gfxDrv = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, requestRGB, true);
break;
+
+ case Common::kPlatformWindows:
+ case Common::kPlatformDOS:
+ // King's Quest 6 has hires content in the Windows version which we also allow to be optionally enabled in the DOS version
+ // and which we also optionally allow to be disabled in the Windows version (the Windows version has support in the original
+ // interpreter code for a small 320 x 240 window on desktops with resolutions of less than 640 x 480, but I haven't managed
+ // to produce it in a Win95 VM; the windows setting don't seem to allow less than 640 x 480, so I don't know if it is actually
+ // possible to set it up).
+ if (g_sci->getGameId() == GID_KQ6 && (g_sci->getPlatform() == Common::kPlatformWindows || g_sci->useHiresGraphics())) {
+ _gfxDrv = new KQ6WinGfxDriver(ConfMan.getBool("windows_cursors") == false, !g_sci->useHiresGraphics(), requestRGB);
+ break;
+ }
+ // fallthrough
default:
if (g_sci->getLanguage() == Common::KO_KOR)
- _gfxDrv = new UpscaledGfxDriver(_displayWidth, _displayHeight + extraHeight, 1, true, requestRGB);
+ _gfxDrv = new UpscaledGfxDriver(1, true, requestRGB);
else // The driver has to be told if is SCI_VERSION_01, since that cannot be determined from the number of colors.
_gfxDrv = new GfxDefaultDriver(_displayWidth, _displayHeight + extraHeight, getSciVersion() < SCI_VERSION_01, requestRGB);
break;
@@ -347,11 +342,14 @@ void GfxScreen::copyRectToScreen(const Common::Rect &rect) {
* This copies a rect to screen w/o scaling adjustment and is only meant to be
* used on hires graphics used in upscaled hires mode.
*/
-void GfxScreen::copyDisplayRectToScreen(const Common::Rect &rect) {
- if (!_upscaledHires)
- error("copyDisplayRectToScreen: not in upscaled hires mode");
+void GfxScreen::copyHiResRectToScreen(const byte *srcBuffer, int pitch, int x, int y, int w, int h, const byte *colorMap) {
+ if (!_gfxDrv->supportsHiResGraphics())
+ error("%s(): Hires graphics display is not supported by the active gfx driver", __FUNCTION__);
- displayRect(rect, rect.left, rect.top);
+ _gfxDrv->setFlags(GfxDriver::kHiResMode);
+ _gfxDrv->setColorMap(colorMap);
+ _gfxDrv->copyRectToScreen(srcBuffer, 0, 0, pitch, x, y, w, h, nullptr, nullptr);
+ _gfxDrv->clearFlags(GfxDriver::kHiResMode);
}
void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) {
@@ -620,8 +618,8 @@ int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
byteCount += pixels; // _controlScreen
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
- if (!_upscaledHires)
- error("bitsGetDataSize() called w/o being in upscaled hires mode");
+ //if (!_upscaledHires)
+ // error("bitsGetDataSize() called w/o being in upscaled hires mode");
byteCount += pixels; // _displayScreen (coordinates actually are given to us for hires displayScreen)
if (_paletteMapScreen)
byteCount += pixels; // _paletteMapScreen
@@ -646,8 +644,8 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) {
bitsSaveScreen(rect, _controlScreen, _width, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
- if (!_upscaledHires)
- error("bitsSave() called w/o being in upscaled hires mode");
+ //if (!_upscaledHires)
+ // error("bitsSave() called w/o being in upscaled hires mode");
bitsSaveScreen(rect, _displayScreen, _displayWidth, memoryPtr);
if (_paletteMapScreen)
bitsSaveScreen(rect, _paletteMapScreen, _displayWidth, memoryPtr);
@@ -710,8 +708,8 @@ void GfxScreen::bitsRestore(const byte *memoryPtr) {
bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
- if (!_upscaledHires)
- error("bitsRestore() called w/o being in upscaled hires mode");
+ //if (!_upscaledHires)
+ // error("bitsRestore() called w/o being in upscaled hires mode");
bitsRestoreScreen(rect, memoryPtr, _displayScreen, _displayWidth);
if (_paletteMapScreen)
bitsRestoreScreen(rect, memoryPtr, _paletteMapScreen, _displayWidth);
@@ -720,7 +718,7 @@ void GfxScreen::bitsRestore(const byte *memoryPtr) {
// from screen. Some lowres showBits() call is used for that and it's not covering the whole area
// We would need to find out inside the kq6 windows interpreter, but this here works already and seems not to have
// any side-effects. The whole hires is hacked into the interpreter, so maybe this is even right.
- copyDisplayRectToScreen(rect);
+ //copyDisplayRectToScreen(rect);
}
}
@@ -951,10 +949,6 @@ void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
x /= 2;
y /= 2;
break;
- case GFX_SCREEN_UPSCALED_640x440:
- x /= 2;
- y = (y * 5) / 11;
- break;
default:
break;
}
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index b27804faa6e..114a90e8207 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -43,8 +43,7 @@ enum {
enum GfxScreenUpscaledMode {
GFX_SCREEN_UPSCALED_DISABLED = 0,
GFX_SCREEN_UPSCALED_480x300 = 1,
- GFX_SCREEN_UPSCALED_640x400 = 2,
- GFX_SCREEN_UPSCALED_640x440 = 3
+ GFX_SCREEN_UPSCALED_640x400 = 2
};
enum GfxScreenMasks {
@@ -87,7 +86,7 @@ public:
void copyToScreen();
void kernelSyncWithFramebuffer();
void copyRectToScreen(const Common::Rect &rect);
- void copyDisplayRectToScreen(const Common::Rect &rect);
+ void copyHiResRectToScreen(const byte *srcBuffer, int pitch, int x, int y, int w, int h, const byte *colorMap);
void copyRectToScreen(const Common::Rect &rect, int16 x, int16 y);
// functions to manipulate a backup copy of the screen (for transitions)
@@ -275,7 +274,6 @@ public:
break;
case GFX_SCREEN_UPSCALED_640x400:
- case GFX_SCREEN_UPSCALED_640x440:
putScaledPixelOnDisplay(x, y, color);
break;
default:
@@ -320,8 +318,7 @@ public:
void vectorPutPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_640x400:
- case GFX_SCREEN_UPSCALED_640x440:
- // For regular upscaled modes forward to the regular putPixel
+ // For regular upscaled modes forward to the regular putPixel
putPixel(x, y, drawMask, color, priority, control);
return;
break;
@@ -372,18 +369,6 @@ public:
_displayScreen[displayOffset + _displayWidth + 1] = color;
break;
- case GFX_SCREEN_UPSCALED_640x440: {
- int16 startY = (y * 11) / 5;
- int16 endY = ((y + 1) * 11) / 5;
- displayOffset = (startY * _displayWidth) + x * 2;
-
- for (int16 curY = startY; curY < endY; curY++) {
- _displayScreen[displayOffset] = color;
- _displayScreen[displayOffset + 1] = color;
- displayOffset += _displayWidth;
- }
- break;
- }
default:
break;
}
@@ -412,8 +397,7 @@ public:
case GFX_SCREEN_UPSCALED_DISABLED:
_displayScreen[offset] = color;
break;
- case GFX_SCREEN_UPSCALED_640x400:
- case GFX_SCREEN_UPSCALED_640x440: {
+ case GFX_SCREEN_UPSCALED_640x400: {
// to 1-> 4 pixels upscaling for all of those, so that fonts won't look weird
int displayOffset = (_upscaledHeightMapping[startingY] + y * 2) * _displayWidth + x * 2;
_displayScreen[displayOffset] = color;
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 61a6b458613..25b84f65cf3 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.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/palette.h"
#include "sci/graphics/remap.h"
@@ -828,16 +829,8 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
}
}
} else if (upscaledHires) {
- // UpscaledHires means view is hires and is supposed to
- // get drawn onto lowres screen.
- for (int y = 0; y < height; y++, bitmapData += celWidth) {
- for (int x = 0; x < width; x++) {
- const byte color = bitmapData[x];
- const int x2 = clipRectTranslated.left + x;
- const int y2 = clipRectTranslated.top + y;
- _screen->putPixelOnDisplay(x2, y2, palette->mapping[color]);
- }
- }
+ // upscaledHires means view is hires and needs no scaling
+ _screen->copyHiResRectToScreen(bitmapData, celWidth, clipRect.left, clipRect.top, width, height, palette->mapping);
} else {
for (int y = 0; y < height; y++, bitmapData += celWidth) {
for (int x = 0; x < width; x++) {
Commit: 71a2af0de27c6368abfc41808d17a633b7739cca
https://github.com/scummvm/scummvm/commit/71a2af0de27c6368abfc41808d17a633b7739cca
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: more accurate KQ6 video handling
I was just exploring this when trying to debug a glitch (which was
not connected to this). The changes do literally nothing to the
output. So the only purpose is to have a better documentation
of what the scripts actually do here.
Changed paths:
engines/sci/engine/kvideo.cpp
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 5423a6e24ac..ba07723e258 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -48,7 +48,15 @@
namespace Sci {
-void playVideo(Video::VideoDecoder &videoDecoder) {
+struct MovieConfig {
+ MovieConfig() : x(0), y(0), w(0), h(0), mode(0), pauseToken(), playFrom(0), playTo(0) {}
+ Common::String filename;
+ reg_t pauseToken;
+ int16 x, y, w, h;
+ uint16 mode, playFrom, playTo;
+};
+
+void playVideo(Video::VideoDecoder &videoDecoder, MovieConfig *conf) {
videoDecoder.start();
Common::SpanOwner<SciSpan<byte> > scaleBuffer;
@@ -70,6 +78,21 @@ void playVideo(Video::VideoDecoder &videoDecoder) {
uint16 x = (screenWidth - width) / 2;
uint16 y = (screenHeight - height) / 2;
+ if (conf) {
+ // This is only for documentary purposes, since the original KQ6 Win interpreter does that.
+ // But the videos will be centered on the screen anyway, without actually using these.
+ x = conf->x;
+ y = conf->y;
+ if (conf->w > 0 && conf->h > 0) {
+ width = conf->w;
+ height = conf->h;
+ }
+ if (conf->playFrom)
+ videoDecoder.seek(Audio::Timestamp(conf->playFrom, 1000));
+ if (conf->playTo)
+ videoDecoder.setEndTime(Audio::Timestamp(conf->playTo, 1000));
+ }
+
bool skipVideo = false;
if (videoDecoder.hasDirtyPalette()) {
@@ -113,6 +136,12 @@ void playVideo(Video::VideoDecoder &videoDecoder) {
}
}
+void playVideo(Video::VideoDecoder &videoDecoder) { // For the debugger
+ playVideo(videoDecoder, nullptr);
+}
+
+MovieConfig *_movieConf = nullptr;
+
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
reg_t retval = s->r_acc;
@@ -125,6 +154,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
Common::ScopedPtr<Video::VideoDecoder> videoDecoder;
bool switchedGraphicsMode = false;
+ bool syncLastFrame = true;
if (argv[0].isPointer()) {
Common::Path filename(s->_segMan->getString(argv[0]));
@@ -142,6 +172,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
const Graphics::PixelFormat format = *it;
g_sci->_gfxScreen->gfxDriver()->initScreen(&format);
switchedGraphicsMode = true;
+ syncLastFrame = false;
break;
}
}
@@ -170,37 +201,89 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
// Windows AVI: Only used by KQ6 CD for the Sierra logo and intro cartoon.
// The first parameter is a subop. Some of the subops set the accumulator.
// The interpreter implements subops 0-6. KQ6 only calls 0, 1, 2, 3, 6.
- // Subop 0 plays the AVI; it is the only one that needs to be implemented.
switch (argv[0].toUint16()) {
case 0: {
- Common::String filename = s->_segMan->getString(argv[1]);
+ _movieConf = new MovieConfig();
+ _movieConf->filename = s->_segMan->getString(argv[1]);
+ retval = NULL_REG;
+ break;
+ }
+ case 1:
+ if (_movieConf) {
+ int16 *dest[4] = { &_movieConf->x, &_movieConf->y, &_movieConf->w, &_movieConf->h };
+ for (int i = 1; i < argc; ++i)
+ *dest[i - 1] = argv[i].toSint16();
+ }
+ retval = NULL_REG;
+ break;
+ case 2:
+ if (_movieConf) {
+ switch (argc) {
+ case 5:
+ _movieConf->playTo = argv[4].toUint16();
+ // fallthrough
+ case 4:
+ _movieConf->playFrom = argv[3].toUint16();
+ // fallthrough
+ case 3:
+ _movieConf->pauseToken = argv[2];
+ // fallthrough
+ case 2:
+ _movieConf->mode = argv[1].toUint16();
+ break;
+ default:
+ break;
+ }
+ }
+
+ // For KQ6, this changes the vertical 200/440 upscaling to 200/400, since this is the expected behavior. Also,
+ // the calculation of the scaled x/y coordinates works slightly differently compared to the normal gfx rendering.
+ g_sci->_gfxScreen->gfxDriver()->setFlags(GfxDriver::kMovieMode);
+
videoDecoder.reset(new Video::AVIDecoder());
- if (!videoDecoder->loadFile(filename.c_str())) {
- warning("Failed to open movie file %s", filename.c_str());
+ if (!videoDecoder->loadFile(_movieConf->filename.c_str())) {
+ warning("Failed to open movie file %s", _movieConf->filename.c_str());
videoDecoder.reset();
}
- retval = TRUE_REG;
+
+ syncLastFrame = false;
+ retval = NULL_REG;
+
break;
- }
default:
+ // This will trigger on case 6 which is the close/deinit function. We don't need that.
debug(kDebugLevelVideo, "Unhandled kShowMovie subop %d", argv[0].toUint16());
}
}
if (videoDecoder) {
- bool is8bit = videoDecoder->getPixelFormat().bytesPerPixel == 1;
+ if (videoDecoder->getPixelFormat().bytesPerPixel > 1)
+ syncLastFrame = false;
- playVideo(*videoDecoder);
+ playVideo(*videoDecoder, _movieConf);
// 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)
g_sci->_gfxScreen->gfxDriver()->initScreen();
- else if (is8bit) {
+ else if (syncLastFrame) {
g_sci->_gfxScreen->kernelSyncWithFramebuffer();
g_sci->_gfxPalette16->kernelSyncScreenPalette();
}
+
+ g_sci->_gfxScreen->gfxDriver()->clearFlags(GfxDriver::kMovieMode);
+
+ if (_movieConf) {
+ // Unfreeze the engine. KQ6 Windows does not run the video modally, since it happens via OS functions.
+ // So the engine is put into a waiting state until signalled to continue. The orignal interpreter does
+ // that from the window proc after receiving the mci finishing message. We can/should do it here, since we
+ // do play the video modally.
+ if (!_movieConf->pauseToken.isNull())
+ invokeSelector(s, _movieConf->pauseToken, g_sci->getKernel()->findSelector("cue"), argc, argv);
+ delete _movieConf;
+ _movieConf = nullptr;
+ }
}
if (reshowCursor)
Commit: 5f66d7161a80ae04c8e551289c883467952d6e18
https://github.com/scummvm/scummvm/commit/5f66d7161a80ae04c8e551289c883467952d6e18
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: add 16 colors mode for KQ6 Win
The original interpreter supports that, although it is slightly
broken. It will show black boxes for the hires graphics, since
the mode isn't properly checked. I have fixed that here to
show the the lores graphics.
Changed paths:
engines/sci/graphics/gfxdrivers.cpp
engines/sci/graphics/gfxdrivers.h
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 368b86a1aab..0a0f37ad433 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -935,64 +935,14 @@ 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), _renderLine(nullptr) {
- 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);
-
- uint32 pos = (drv.pos() + 1) & ~1;
-
- drv.seek(pos);
- drv.seek(drv.readUint16LE());
- uint32 colResponse = drv.readUint32LE();
- _numColors = (colResponse >> 8) & 0xffff;
- if (_numColors < 16 || _numColors > 256 || (colResponse & 0xff0000ff) != 0xC30000B8)
- error("SCI1_EGADriver: Failed to retrieve color info from '%s'", _driverFile);
-
- drv.seek(pos + 20);
- drv.seek(drv.readUint16LE());
- byte *buff = new byte[128];
- drv.read(buff, 128);
-
- uint16 tableOffs = 0;
- for (int i = 0; i < 120 && !tableOffs; ++i) {
- uint32 c = READ_BE_UINT32(buff + i);
- if (c == 0x8BD82E8A) {
- if (buff[i + 4] == 0x87)
- tableOffs = READ_LE_UINT16(buff + i + 5);
- } else if (c == 0xD0E8D0E8) {
- for (int ii = 4; ii < 14; ++ii) {
- if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
- c = READ_BE_UINT32(buff + i + ii + 2);
- if ((c & 0xFFFFFF) == 0x83F83F)
- _colAdjust = c >> 24;
- }
- }
- }
- }
- delete[] buff;
-
- if (!tableOffs)
- error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
-
- drv.seek(tableOffs);
- byte *table = new byte[512]();
- drv.read(table, 512);
- _egaMatchTable = table;
-
- if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
- error("SCI1_EGADriver: Driver file '%s' unknown version", _driverFile);
-
- drv.close();
+ _currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _renderLine(nullptr), _vScaleMult(2), _vScaleDiv(1) {
+ static const byte egaColors[48] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+ 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+ };
+ _convPalette = egaColors;
}
SCI1_EGADriver::~SCI1_EGADriver() {
@@ -1004,7 +954,7 @@ SCI1_EGADriver::~SCI1_EGADriver() {
delete[] _internalPalette;
}
-template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal) {
+template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool) {
const T *p = reinterpret_cast<const T*>(pal);
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + (w << 1);
@@ -1023,21 +973,17 @@ template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w,
}
void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
+ if (!_ready)
+ loadData();
+
Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
- initGraphics(_screenW << 1, _screenH << 1, _requestRGBMode ? nullptr : &format);
+ initGraphics(_screenW << 1, _screenH * _vScaleMult / _vScaleDiv, _requestRGBMode ? nullptr : &format);
format = g_system->getScreenFormat();
_pixelSize = format.bytesPerPixel;
if (_requestRGBMode && _pixelSize == 1)
warning("SCI1_EGADriver::initScreen(): RGB rendering not available in this ScummVM build");
- 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
- };
-
delete[] _egaColorPatterns;
delete[] _compositeBuffer;
delete[] _currentBitmap;
@@ -1047,22 +993,22 @@ void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
_egaColorPatterns = _compositeBuffer = _currentBitmap = _currentPalette = nullptr;
if (_pixelSize == 1) {
- g_system->getPaletteManager()->setPalette(egaColors, 0, ARRAYSIZE(egaColors) / 3);
+ g_system->getPaletteManager()->setPalette(_convPalette, 0, 16);
} else {
byte *rgbpal = new byte[_numColors * _pixelSize]();
assert(rgbpal);
if (_pixelSize == 2)
- updateRGBPalette<uint16>(rgbpal, egaColors, 0, ARRAYSIZE(egaColors) / 3, format);
+ updateRGBPalette<uint16>(rgbpal, _convPalette, 0, 16, format);
else if (_pixelSize == 4)
- updateRGBPalette<uint32>(rgbpal, egaColors, 0, ARRAYSIZE(egaColors) / 3, format);
+ updateRGBPalette<uint32>(rgbpal, _convPalette, 0, 16, format);
else
error("SCI1_EGADriver::initScreen(): Unsupported screen format");
_internalPalette = rgbpal;
- CursorMan.replaceCursorPalette(egaColors, 0, ARRAYSIZE(egaColors) / 3);
+ CursorMan.replaceCursorPalette(_convPalette, 0, 16);
}
- _compositeBuffer = new byte[(_screenW << 1) * (_screenH << 1) * _pixelSize]();
+ _compositeBuffer = new byte[(_screenW << 1) * (_screenH * _vScaleMult / _vScaleDiv) * _pixelSize]();
assert(_compositeBuffer);
_currentBitmap = new byte[_screenW * _screenH]();
assert(_currentBitmap);
@@ -1104,13 +1050,13 @@ void SCI1_EGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int p
if (src != _currentBitmap)
updateBitmapBuffer(_currentBitmap, _screenW, src, pitch, destX, destY, w, h);
- byte *dst = _compositeBuffer;
- for (int i = 0; i < h; ++i) {
- _renderLine(dst, src, w, _egaColorPatterns, _internalPalette);
- src += pitch;
- }
+ uint16 realWidth, realHeight;
+ renderBitmap(_compositeBuffer, src, pitch, destY, w, h, _egaColorPatterns, _internalPalette, realWidth, realHeight);
- g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, destX << 1, destY << 1, w << 1, h << 1);
+ Common::Point pos(destX, destY);
+ pos = getRealCoords(pos);
+
+ g_system->copyRectToScreen(_compositeBuffer, realWidth * _pixelSize, pos.x, pos.y, realWidth, realHeight);
}
void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
@@ -1162,25 +1108,94 @@ void SCI1_EGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int
Common::Point SCI1_EGADriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
res.x >>= 1;
- res.y >>= 1;
+ res.y = res.y * _vScaleDiv / _vScaleMult;
return res;
}
void SCI1_EGADriver::setMousePos(const Common::Point &pos) const {
- g_system->warpMouse(pos.x << 1, pos.y << 1);
+ g_system->warpMouse(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
}
void SCI1_EGADriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
- g_system->setShakePos(shakeXOffset << 1, shakeYOffset << 1);
+ g_system->setShakePos(shakeXOffset << 1, shakeYOffset * _vScaleMult / _vScaleDiv);
}
void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
- Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
+ Common::Rect r2(r.left << 1, r.top * _vScaleMult / _vScaleDiv, r.right << 1, r.bottom * _vScaleMult / _vScaleDiv);
GfxDriver::clearRect(r2);
}
Common::Point SCI1_EGADriver::getRealCoords(Common::Point &pos) const {
- return Common::Point(pos.x << 1, pos.y << 1);
+ return Common::Point(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
+}
+
+void SCI1_EGADriver::loadData() {
+ 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);
+
+ uint32 pos = (drv.pos() + 1) & ~1;
+
+ drv.seek(pos);
+ drv.seek(drv.readUint16LE());
+ uint32 colResponse = drv.readUint32LE();
+ _numColors = (colResponse >> 8) & 0xffff;
+ if (_numColors < 16 || _numColors > 256 || (colResponse & 0xff0000ff) != 0xC30000B8)
+ error("SCI1_EGADriver: Failed to retrieve color info from '%s'", _driverFile);
+
+ drv.seek(pos + 20);
+ drv.seek(drv.readUint16LE());
+ byte *buff = new byte[128];
+ drv.read(buff, 128);
+
+ uint16 tableOffs = 0;
+ for (int i = 0; i < 120 && !tableOffs; ++i) {
+ uint32 c = READ_BE_UINT32(buff + i);
+ if (c == 0x8BD82E8A) {
+ if (buff[i + 4] == 0x87)
+ tableOffs = READ_LE_UINT16(buff + i + 5);
+ } else if (c == 0xD0E8D0E8) {
+ for (int ii = 4; ii < 14; ++ii) {
+ if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
+ c = READ_BE_UINT32(buff + i + ii + 2);
+ if ((c & 0xFFFFFF) == 0x83F83F)
+ _colAdjust = c >> 24;
+ }
+ }
+ }
+ }
+ delete[] buff;
+
+ if (!tableOffs)
+ error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
+
+ drv.seek(tableOffs);
+ byte *table = new byte[512]();
+ drv.read(table, 512);
+ _egaMatchTable = table;
+
+ if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
+ error("SCI1_EGADriver: Driver file '%s' unknown version", _driverFile);
+
+ drv.close();
+}
+
+void SCI1_EGADriver::renderBitmap(byte *dst, const byte *src, int pitch, int, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
+ for (int i = 0; i < h; ++i) {
+ _renderLine(dst, src, w, patterns, palette, 0);
+ src += pitch;
+ }
+ realWidth = w << 1;
+ realHeight = h << 1;
}
const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
@@ -1358,8 +1373,8 @@ void UpscaledGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy,
realHeight = h << 1;
}
-KQ6WinGfxDriver::KQ6WinGfxDriver(bool scaleCursor, bool smallWindow,bool rgbRendering) :
- UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, scaleCursor && !smallWindow, rgbRendering), _smallWindow(smallWindow),
+KQ6WinGfxDriver::KQ6WinGfxDriver(bool dosStyleCursors, bool smallWindow,bool rgbRendering) :
+ UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, dosStyleCursors && !smallWindow, rgbRendering), _dosStyleCursors(dosStyleCursors), _smallWindow(smallWindow),
_renderLine(nullptr), _renderLine2(nullptr), _flags(0), _colorMap(nullptr), _vScaleMult2(smallWindow ? 1 : 2) {
_virtualW = 320;
_virtualH = 200;
@@ -1484,6 +1499,109 @@ void KQ6WinGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int
updateScreen(destX, destY, w, h, palMods, palModMapping);
}
+byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors) {
+ byte color[3] = { (rgbTriplet >> 16) & 0xFF, (rgbTriplet >> 8) & 0xFF, rgbTriplet & 0xFF };
+ int min = 65025;
+ byte match = 0;
+ for (int i = 0; i < numColors && min; ++i) {
+ const byte *rgb = &palette[i * 3];
+ int t = (color[0] - rgb[0]) * (color[0] - rgb[0]) + (color[1] - rgb[1]) * (color[1] - rgb[1]) + (color[2] - rgb[2]) * (color[2] - rgb[2]);
+ if (t < min) {
+ min = t;
+ match = i;
+ }
+ }
+ return match;
+}
+
+void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor) {
+ const byte *s = reinterpret_cast<const byte*>(src);
+ uint16 min = 65025;
+ uint16 max = 0;
+
+ byte newKeyColor = 0;
+ while (newKeyColor == blackColor || newKeyColor == whiteColor)
+ ++newKeyColor;
+
+ for (uint i = 0; i < w * h; ++i) {
+ byte col = *s++;
+ if (col == keycolor)
+ continue;
+ const byte *rgb = &palette[col * 3];
+ uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+ if (t > max)
+ max = t;
+ if (t < min)
+ min = t;
+ }
+
+#if 0
+ // The original interpreter will accidentally let the value overflow like this,
+ // making most cursors completely white. I have fixed it.
+ uint16 med = (uint16)(min + max) >> 1;
+#else
+ uint16 med = (min + max) >> 1;
+#endif
+ uint16 lim1 = max - (max - min) / 3;
+ uint16 lim2 = min + max - lim1;
+ s = reinterpret_cast<const byte*>(src);
+
+ if (w < 17 && h < 17) {
+ // Small cursors (like the insignia ring in KQ6) get scaled and dithered.
+ byte *dst2 = dst + (w << 1);
+ for (uint i = 0; i < h; ++i) {
+ for (uint ii = 0; ii < w; ++ii) {
+ byte col = *s++;
+ if (col == keycolor) {
+ *dst++ = *dst2++ = newKeyColor;
+ *dst++ = *dst2++ = newKeyColor;
+ continue;
+ }
+ const byte *rgb = &palette[col * 3];
+ uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+
+ dst[0] = dst2[1] = t > lim2 ? whiteColor : blackColor;
+ dst2[0] = dst[1] = t > lim1 ? whiteColor : blackColor;
+ dst += 2;
+ dst2 += 2;
+ };
+ dst += (w << 1);
+ dst2 += (w << 1);
+ }
+ w <<= 1;
+ h <<= 1;
+ hotX <<= 1;
+ hotY <<= 1;
+ } else {
+ for (uint i = 0; i < w * h; ++i) {
+ byte col = *s++;
+ if (col == keycolor) {
+ *dst++ = newKeyColor;
+ continue;
+ }
+ const byte *rgb = &palette[col * 3];
+ uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
+ *dst++ = t > med ? whiteColor : blackColor;
+ }
+ }
+ keycolor = newKeyColor;
+}
+
+void KQ6WinGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
+ if (_dosStyleCursors) {
+ // The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
+ // cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
+ // But I think it is on purpose that we have the cursors look like in the DOS version as a default.
+ UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
+ return;
+ }
+ byte col1 = findColorInPalette(0x00000000, _currentPalette, _numColors);
+ byte col2 = findColorInPalette(0x00FFFFFF, _currentPalette, _numColors);
+ renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor);
+ CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
+}
+
Common::Point KQ6WinGfxDriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult2 + (pos.y + 4) / 5);
}
@@ -1527,6 +1645,143 @@ void KQ6WinGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, i
realHeight = (dst - dstart) / _screenW;
}
+KQ6WinGfx16ColorsDriver::KQ6WinGfx16ColorsDriver(bool altCursor, bool enhancedDithering, bool rgbRendering) : SCI1_EGADriver(rgbRendering), _enhancedDithering(enhancedDithering), _altCursor(altCursor), _renderLine2(nullptr) {
+ static const byte win16Colors[48] = {
+ 0x00, 0x00, 0x00, 0xA8, 0x00, 0x57, 0x00, 0xA8, 0x57, 0xA8, 0xA8, 0x57,
+ 0x00, 0x00, 0xA8, 0xA8, 0x57, 0xA8, 0x57, 0xA8, 0xA8, 0x87, 0x88, 0x8F,
+ 0xC0, 0xC7, 0xC8, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ _convPalette = win16Colors;
+ _vScaleMult = 11;
+ _vScaleDiv = 5;
+}
+
+KQ6WinGfx16ColorsDriver::~KQ6WinGfx16ColorsDriver() {
+ _egaMatchTable = nullptr; // prevent invalid deletion in SCI1_EGADriver::~SCI1_EGADriver()
+}
+
+template <typename T, bool extScale> void win16ColRenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool swap) {
+ const T *p = reinterpret_cast<const T*>(pal);
+ T *d1 = reinterpret_cast<T*>(dst);
+ T *d2 = d1 + (w << 1);
+ T *d3 = d2 + (w << 1);
+ T *&d3r = swap ? d2 : d1;
+
+ if (swap)
+ SWAP(d1, d2);
+
+ for (int i = 0; i < w; ++i) {
+ byte pt = patterns[*src++];
+ if (sizeof(T) == 1) {
+ *d1++ = d2[1] = pt & 0x0F;
+ *d1++ = *d2++ = pt >> 4;
+ } else {
+ *d1++ = d2[1] = p[pt & 0x0F];
+ *d1++ = *d2++ = p[pt >> 4];
+ }
+ d2++;
+
+ if (extScale) {
+ *d3++ = *(d3r - 2);
+ *d3++ = *(d3r - 1);
+ }
+ }
+
+ dst = reinterpret_cast<byte*>(extScale ? d3 : (swap ? d1 : d2));
+}
+
+void KQ6WinGfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
+ SCI1_EGADriver::initScreen(format);
+
+ static const LineProc lineProcs[] = {
+ &win16ColRenderLine<byte, false>,
+ &win16ColRenderLine<byte, true>,
+ &win16ColRenderLine<uint16, false>,
+ &win16ColRenderLine<uint16, true>,
+ &win16ColRenderLine<uint32, false>,
+ &win16ColRenderLine<uint32, true>
+ };
+
+ assert((_pixelSize | 1) < ARRAYSIZE(lineProcs));
+ _renderLine = lineProcs[_pixelSize & ~1];
+ _renderLine2 = lineProcs[_pixelSize | 1];
+}
+
+void KQ6WinGfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
+ GFXDRV_ASSERT_READY;
+ // The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
+ // cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
+ byte col1 = findColorInPalette(0x00000000, _convPalette, _numColors);
+ byte col2 = findColorInPalette(0x00FFFFFF, _convPalette, _numColors);
+ renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor);
+ CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
+}
+
+Common::Point KQ6WinGfx16ColorsDriver::getRealCoords(Common::Point &pos) const {
+ return Common::Point(pos.x << 1, (pos.y << 1) + (pos.y + 4) / 5);
+}
+
+void KQ6WinGfx16ColorsDriver::loadData() {
+ _egaMatchTable = _win16ColorsDitherPatterns;
+ _numColors = 16;
+}
+
+void KQ6WinGfx16ColorsDriver::renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
+ const byte *dst0 = dst;
+ byte mod = (y + 4) % 5;
+ byte swap = _enhancedDithering ? ((y + 4) / 5) & 1 : 0;
+ for (int i = 0; i < h; ++i) {
+ if (++mod == 5) {
+ _renderLine2(dst, src, w, patterns, palette, swap);
+ if (_enhancedDithering)
+ swap ^= 1;
+ mod = 0;
+ } else {
+ _renderLine(dst, src, w, patterns, palette, swap);
+ }
+ src += pitch;
+ }
+ realWidth = w << 1;
+ realHeight = (dst - dst0) / (realWidth * _pixelSize);
+}
+
+const byte KQ6WinGfx16ColorsDriver::_win16ColorsDitherPatterns[512] = {
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc, 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc, 0x02, 0x02, 0x02, 0x06, 0x06, 0x46, 0x6c, 0x6c,
+ 0x02, 0x02, 0x02, 0x06, 0x06, 0x2c, 0x6c, 0xce, 0x22, 0x22, 0x22, 0x26, 0x26, 0x66, 0x4e, 0xce,
+ 0x2a, 0x2a, 0x2a, 0x6a, 0x6a, 0x2e, 0x6e, 0x6e, 0xaa, 0xaa, 0xaa, 0xaa, 0xae, 0xae, 0xee, 0xee,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc, 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc, 0x02, 0x02, 0x02, 0x06, 0x06, 0x46, 0x6c, 0x6c,
+ 0x02, 0x02, 0x02, 0x06, 0x06, 0x2c, 0x6c, 0xce, 0x22, 0x22, 0x22, 0x26, 0x26, 0x66, 0x4e, 0xce,
+ 0x2a, 0x2a, 0x2a, 0x6a, 0x6a, 0x2e, 0x6e, 0x6e, 0xaa, 0xaa, 0xaa, 0xaa, 0xae, 0xae, 0xae, 0xee,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc, 0x00, 0x00, 0x00, 0x04, 0x04, 0x44, 0x4c, 0xcc,
+ 0x00, 0x00, 0x00, 0x05, 0x04, 0x44, 0x4c, 0xcc, 0x02, 0x02, 0x02, 0x06, 0x06, 0x46, 0x6c, 0x6c,
+ 0x02, 0x02, 0x02, 0x06, 0x06, 0x2c, 0x6c, 0xce, 0x22, 0x22, 0x22, 0x26, 0x26, 0x66, 0x4e, 0xce,
+ 0x2a, 0x2a, 0x2a, 0x6a, 0x6a, 0x2e, 0x6e, 0x6e, 0xaa, 0xaa, 0xaa, 0xaa, 0xae, 0xae, 0xee, 0xee,
+ 0x01, 0x01, 0x01, 0x05, 0x05, 0x45, 0x5c, 0x5c, 0x01, 0x01, 0x01, 0x05, 0x05, 0x45, 0x5c, 0x5c,
+ 0x01, 0x01, 0x01, 0x05, 0x05, 0x1c, 0x5c, 0x5c, 0x03, 0x03, 0x03, 0x07, 0x07, 0x47, 0x7c, 0x7c,
+ 0x03, 0x03, 0x03, 0x07, 0x08, 0x3c, 0x7c, 0x8c, 0x23, 0x23, 0x23, 0x27, 0x27, 0x67, 0x5e, 0x5e,
+ 0x3a, 0x3a, 0x3a, 0x7a, 0x7a, 0x3e, 0x7e, 0x7e, 0xaa, 0xaa, 0xaa, 0x8a, 0x8a, 0xae, 0x8e, 0xee,
+ 0x01, 0x01, 0x01, 0x05, 0x05, 0x45, 0x5c, 0xcd, 0x01, 0x01, 0x01, 0x49, 0x05, 0x1c, 0x5c, 0xcd,
+ 0x11, 0x01, 0x01, 0x49, 0x05, 0x1c, 0x4d, 0xcd, 0x03, 0x03, 0x03, 0x07, 0x07, 0x47, 0x7c, 0x7c,
+ 0x13, 0x03, 0x03, 0x08, 0x08, 0x48, 0x48, 0x8c, 0x9a, 0x23, 0x1a, 0x4b, 0x28, 0x68, 0x4f, 0xcf,
+ 0x3a, 0x3a, 0x3a, 0x6b, 0x7a, 0x3e, 0x7e, 0x8e, 0xab, 0xab, 0xab, 0xab, 0xaf, 0xaf, 0xaf, 0xef,
+ 0x11, 0x11, 0x11, 0x15, 0x15, 0x55, 0x4d, 0xcd, 0x11, 0x11, 0x11, 0x15, 0x15, 0x55, 0x4d, 0xcd,
+ 0x11, 0x11, 0x11, 0x49, 0x49, 0x9c, 0x4d, 0xcd, 0x29, 0x29, 0x13, 0x17, 0x17, 0x2d, 0x6d, 0x6d,
+ 0x13, 0x13, 0x13, 0x18, 0x18, 0x58, 0x58, 0x8c, 0x33, 0x33, 0x33, 0x37, 0x37, 0x77, 0x4f, 0xcf,
+ 0x2b, 0x2b, 0x2b, 0x6b, 0x6b, 0x2f, 0x6f, 0x6f, 0xab, 0xab, 0xab, 0xab, 0xaf, 0xaf, 0xef, 0xef,
+ 0x19, 0x19, 0x19, 0x59, 0x59, 0x1d, 0x5d, 0x5d, 0x19, 0x19, 0x19, 0x59, 0x59, 0x1d, 0x5d, 0x5d,
+ 0x19, 0x19, 0x19, 0x59, 0x59, 0x1d, 0x5d, 0xdd, 0x39, 0x39, 0x39, 0x79, 0x79, 0x3d, 0x7d, 0x7d,
+ 0x39, 0x39, 0x39, 0x89, 0x89, 0x3d, 0x8d, 0x8d, 0x1b, 0x1b, 0x1b, 0x5b, 0x5b, 0x1f, 0x5f, 0x5f,
+ 0x3b, 0x3b, 0x3b, 0x7b, 0x7b, 0x3f, 0x88, 0x88, 0xbb, 0xab, 0xab, 0x8b, 0x8b, 0xaf, 0x8f, 0x8f,
+ 0x99, 0x99, 0x99, 0x99, 0x9d, 0x9d, 0xdd, 0xdd, 0x99, 0x99, 0x99, 0x99, 0x9d, 0x9d, 0xdd, 0xdd,
+ 0x99, 0x99, 0x99, 0x99, 0x9d, 0x9d, 0x7d, 0xdd, 0x39, 0x39, 0x39, 0x79, 0x89, 0x3d, 0x7d, 0x5d,
+ 0x9b, 0x9b, 0x89, 0x89, 0x89, 0x89, 0x8d, 0x8d, 0x9b, 0x9b, 0x9b, 0x9b, 0x9f, 0x9f, 0xdf, 0xdf,
+ 0x3b, 0x3b, 0x3b, 0x8b, 0x8b, 0x3f, 0x8f, 0x8f, 0xbb, 0xbb, 0xbb, 0xbb, 0xbf, 0xbf, 0xff, 0xff
+};
+
PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette) :
UpscaledGfxDriver(textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
_cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 02286a78e6d..5558b7e423f 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -207,7 +207,7 @@ private:
static const char *_driverFile;
};
-class SCI1_EGADriver final : public GfxDriver {
+class SCI1_EGADriver : public GfxDriver {
public:
SCI1_EGADriver(bool rgbRendering);
~SCI1_EGADriver() override;
@@ -228,17 +228,22 @@ public:
bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
-private:
+protected:
+ typedef void (*LineProc)(byte*&, const byte*, int, const byte*, const byte*, bool);
+ LineProc _renderLine;
+ const byte *_convPalette;
+ uint16 _vScaleMult, _vScaleDiv;
+ const byte *_egaMatchTable;
+ byte *_egaColorPatterns;
byte *_compositeBuffer;
- byte *_currentBitmap;
byte *_currentPalette;
- byte *_egaColorPatterns;
+private:
+ virtual void loadData();
+ virtual void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight);
+ byte *_currentBitmap;
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;
};
@@ -278,29 +283,50 @@ private:
bool _needCursorBuffer;
};
-class KQ6WinGfxDriver : public UpscaledGfxDriver {
+class KQ6WinGfxDriver final : public UpscaledGfxDriver {
public:
- KQ6WinGfxDriver(bool scaleCursor, bool smallWindow, bool rgbRendering);
+ KQ6WinGfxDriver(bool dosStyleCursors, bool smallWindow, bool rgbRendering);
~KQ6WinGfxDriver() override {}
void initScreen(const Graphics::PixelFormat *format) override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
+ void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getRealCoords(Common::Point &pos) const override;
void setColorMap(const byte *colorMap) { _colorMap = colorMap; }
void setFlags(uint32 flags) override;
void clearFlags(uint32 flags) override;
bool supportsHiResGraphics() const override { return !_smallWindow; }
-private:
+protected:
typedef void (*LineProc)(byte*&, const byte*, int, int, int);
LineProc _renderLine;
+private:
typedef void (*LineProcSpec)(byte*&, const byte*, int, int, const byte*);
LineProcSpec _renderLine2;
void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) override;
uint32 _flags;
const byte *_colorMap;
const bool _smallWindow;
+ const bool _dosStyleCursors;
uint16 _vScaleMult2;
};
+class KQ6WinGfx16ColorsDriver final : public SCI1_EGADriver {
+public:
+ // The original does not take into account the extra lines required for the 200->440 vertical scaling. There is a noticeable dithering glitch every 11th line, as the
+ // two pixels of the checkerbox pattern appear in the wrong order. I have implemented a fix for this which can be activated with the fixDithering parameter.
+ KQ6WinGfx16ColorsDriver(bool altCursor, bool fixDithering, bool rgbRendering);
+ ~KQ6WinGfx16ColorsDriver() override;
+ void initScreen(const Graphics::PixelFormat *format) override;
+ void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
+ Common::Point getRealCoords(Common::Point &pos) const override;
+private:
+ void loadData() override;
+ void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) override;
+ LineProc _renderLine2;
+ const bool _enhancedDithering;
+ const bool _altCursor;
+ static const byte _win16ColorsDitherPatterns[512];
+};
+
class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
public:
enum SjisFontStyle {
Commit: c11193f35cea97914c26927652619e0bd614db6e
https://github.com/scummvm/scummvm/commit/c11193f35cea97914c26927652619e0bd614db6e
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
COMMON: add Windows 16 colors render mode option
Changed paths:
common/gui_options.cpp
common/gui_options.h
common/rendermode.cpp
common/rendermode.h
engines/sci/graphics/gfxdrivers.cpp
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index 854b6a1496c..a4f5ff3ee09 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -81,6 +81,8 @@ const struct GameOpt {
{ GUIO_RENDERC64, "c64" },
{ GUIO_RENDERVGAGREY, "vgaGray" },
{ GUIO_RENDERPC98_8C, "pc98-8c" },
+ { GUIO_RENDERWIN_256C, "win256c" },
+ { GUIO_RENDERWIN_16C, "win16c" },
{ GUIO_GAMEOPTIONS1, "gameOption1" },
{ GUIO_GAMEOPTIONS2, "gameOption2" },
diff --git a/common/gui_options.h b/common/gui_options.h
index cc7065fc9e7..12301b65bf4 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -72,6 +72,8 @@
#define GUIO_RENDERC64 "\x29"
#define GUIO_RENDERVGAGREY "\x2A"
#define GUIO_RENDERPC98_8C "\x2B"
+#define GUIO_RENDERWIN_256C "\x2C"
+#define GUIO_RENDERWIN_16C "\x2D"
#define GUIO_LINKSPEECHTOSFX "\x30"
#define GUIO_LINKMUSICTOSFX "\x31"
diff --git a/common/rendermode.cpp b/common/rendermode.cpp
index 48084692424..2c252eb1e09 100644
--- a/common/rendermode.cpp
+++ b/common/rendermode.cpp
@@ -55,6 +55,8 @@ const RenderModeDescription g_renderModes[] = {
{ "zx", "ZX Spectrum", kRenderZX },
{ "c64", "Commodore 64", kRenderC64 },
{ "vgaGrey", _s("VGA Grey Scale"), kRenderVGAGrey },
+ { "win256c", _s("Windows (256 Colors)"), kRenderWin256c},
+ { "win16c", _s("Windows (16 Colors)"), kRenderWin16c},
{nullptr, nullptr, kRenderDefault}
};
@@ -87,6 +89,8 @@ static const RenderGUIOMapping s_renderGUIOMapping[] = {
{ kRenderC64, GUIO_RENDERC64 },
{ kRenderVGAGrey, GUIO_RENDERVGAGREY },
{ kRenderPC98_8c, GUIO_RENDERPC98_8C },
+ { kRenderWin256c, GUIO_RENDERWIN_16C },
+ { kRenderWin16c, GUIO_RENDERWIN_256C },
};
DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Hercules Green", "lowres")
diff --git a/common/rendermode.h b/common/rendermode.h
index 65b25c69a0b..f2e95adfd77 100644
--- a/common/rendermode.h
+++ b/common/rendermode.h
@@ -66,7 +66,9 @@ enum RenderMode {
kRenderZX = 17,
kRenderC64 = 18,
kRenderVGAGrey = 19,
- kRenderPC98_8c = 20
+ kRenderPC98_8c = 20,
+ kRenderWin256c = 21,
+ kRenderWin16c = 22
};
struct RenderModeDescription {
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 0a0f37ad433..61efe535bd3 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -1500,7 +1500,11 @@ void KQ6WinGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int
}
byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors) {
- byte color[3] = { (rgbTriplet >> 16) & 0xFF, (rgbTriplet >> 8) & 0xFF, rgbTriplet & 0xFF };
+ byte color[3];
+ for (int i = 2; i >= 0; --i) {
+ color[i] = rgbTriplet & 0xFF;
+ rgbTriplet >>= 8;
+ }
int min = 65025;
byte match = 0;
for (int i = 0; i < numColors && min; ++i) {
Commit: a4380b8e0b47af7e479408a536151d60796dcd87
https://github.com/scummvm/scummvm/commit/a4380b8e0b47af7e479408a536151d60796dcd87
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: add appropriate render options for KQ6 Win
Changed paths:
engines/sci/detection.cpp
engines/sci/detection_internal.cpp
engines/sci/detection_internal.h
engines/sci/graphics/gfxdrivers.h
engines/sci/graphics/screen.cpp
engines/sci/metaengine.cpp
engines/sci/sci.cpp
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index e713c51a180..8de94463371 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -231,7 +231,7 @@ DetectedGames SciMetaEngineDetection::detectGames(const Common::FSList &fslist,
if (game.gameId.equals(g->gameidStr))
break;
}
- game.setGUIOptions(customizeGuiOptions(fslist.begin()->getParent().getPath(), parseGameGUIOptions(game.getGUIOptions()), g->version));
+ game.setGUIOptions(customizeGuiOptions(fslist.begin()->getParent().getPath(), parseGameGUIOptions(game.getGUIOptions()), game.platform, g->gameidStr, g->version));
game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(game.language));
}
diff --git a/engines/sci/detection_internal.cpp b/engines/sci/detection_internal.cpp
index e3f5419c0db..8721968fab3 100644
--- a/engines/sci/detection_internal.cpp
+++ b/engines/sci/detection_internal.cpp
@@ -110,7 +110,7 @@ const GameIdStrToEnum gameIdStrToEnum[] = {
{ nullptr, nullptr, GID_ALL, false, SCI_VERSION_NONE }
};
-Common::String customizeGuiOptions(Common::Path gamePath, Common::String guiOptions, SciVersion version) {
+Common::String customizeGuiOptions(Common::Path gamePath, Common::String guiOptions, Common::Platform platform, Common::String idStr, SciVersion version) {
struct RMode {
SciVersion min;
SciVersion max;
@@ -130,9 +130,12 @@ Common::String customizeGuiOptions(Common::Path gamePath, Common::String guiOpti
{ 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 },
+ { SCI_VERSION_01, SCI_VERSION_01, "9801VID.DRV", GUIO_RENDERPC98_8C }
};
+ if (idStr.equals("kq6") && platform == Common::kPlatformWindows)
+ return guiOptions + GUIO_RENDERWIN_256C + GUIO_RENDERWIN_16C;
+
Common::FSNode node(gamePath);
if (!node.exists()) {
diff --git a/engines/sci/detection_internal.h b/engines/sci/detection_internal.h
index 58b5a6c4052..052a1483773 100644
--- a/engines/sci/detection_internal.h
+++ b/engines/sci/detection_internal.h
@@ -23,6 +23,7 @@
#define SCI_DETECTION_INTERNAL_H
#include "common/path.h"
+#include "common/platform.h"
#include "sci/detection.h"
@@ -38,7 +39,7 @@ struct GameIdStrToEnum {
extern const GameIdStrToEnum gameIdStrToEnum[];
-Common::String customizeGuiOptions(Common::Path gamePath, Common::String guiOptions, SciVersion version);
+Common::String customizeGuiOptions(Common::Path gamePath, Common::String guiOptions, Common::Platform platform, Common::String idStr, SciVersion version);
} // End of namespace Sci
diff --git a/engines/sci/graphics/gfxdrivers.h b/engines/sci/graphics/gfxdrivers.h
index 5558b7e423f..0d52491bc52 100644
--- a/engines/sci/graphics/gfxdrivers.h
+++ b/engines/sci/graphics/gfxdrivers.h
@@ -318,6 +318,7 @@ public:
void initScreen(const Graphics::PixelFormat *format) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getRealCoords(Common::Point &pos) const override;
+ static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows); }
private:
void loadData() override;
void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) override;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index b54c083b05b..c2d589cdf6e 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -159,14 +159,14 @@ GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _
_gfxDrv = new SCI0_HerculesDriver(renderMode == Common::kRenderHercG ? 0x66ff66 : 0xffbf66, requestRGB, false);
break;
case Common::kRenderEGA:
- // No support for this mode in the Korean version yet.
- if (getSciVersion() > SCI_VERSION_1_EGA_ONLY && g_sci->getLanguage() != Common::KO_KOR)
+ if (getSciVersion() > SCI_VERSION_1_EGA_ONLY)
_gfxDrv = new SCI1_EGADriver(requestRGB);
break;
case Common::kRenderVGAGrey:
- // No support for this mode in the Korean version yet.
- if (g_sci->getLanguage() != Common::KO_KOR)
- _gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
+ _gfxDrv = new SCI1_VGAGreyScaleDriver(requestRGB);
+ break;
+ case Common::kRenderWin16c:
+ _gfxDrv = new KQ6WinGfx16ColorsDriver(ConfMan.getBool("windows_cursors") == false, true, requestRGB);
break;
case Common::kRenderPC98_8c:
if (g_sci->getGameId() == GID_PQ2)
diff --git a/engines/sci/metaengine.cpp b/engines/sci/metaengine.cpp
index f004593d0d4..5183f086e61 100644
--- a/engines/sci/metaengine.cpp
+++ b/engines/sci/metaengine.cpp
@@ -215,7 +215,7 @@ Common::Error SciMetaEngine::createInstance(OSystem *syst, Engine **engine, cons
*engine = new SciEngine(syst, desc, g->gameidEnum);
// If the GUI options were updated, we catch this here and update them in the users config file transparently.
- Common::updateGameGUIOptions(customizeGuiOptions(ConfMan.getPath("path"), desc->guiOptions, g->version), getGameGUIOptionsDescriptionLanguage(desc->language));
+ Common::updateGameGUIOptions(customizeGuiOptions(ConfMan.getPath("path"), desc->guiOptions, desc->platform, g->gameidStr, g->version), getGameGUIOptionsDescriptionLanguage(desc->language));
return Common::kNoError;
}
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index de2cdffed6b..3ce06ddc64d 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -336,7 +336,9 @@ Common::Error SciEngine::run() {
((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))
+ (getSciVersion() > SCI_VERSION_1_LATE && !KQ6WinGfx16ColorsDriver::validateMode(p)) ||
+ (renderMode == Common::kRenderPC98_16c && undither) ||
+ (getLanguage() == Common::KO_KOR)) // No extra modes supported for the Korean fan-patched games
renderMode = Common::kRenderDefault;
// Disable undithering for CGA, Hercules and other unsuitable video modes
Commit: 80258d543a30166fd492642337f6a17580c5c521
https://github.com/scummvm/scummvm/commit/80258d543a30166fd492642337f6a17580c5c521
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: re-enable hi-res option for KQ6 Windows
(since the "small window" mode is now supported,
there needs to be an option to select that)
Changed paths:
engines/sci/detection_tables.h
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 0eade5fd2a3..f26689550b7 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -2263,8 +2263,9 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GAMEOPTION_MIDI_MODE, \
GAMEOPTION_RGB_RENDERING)
-#define GUIO_KQ6_CD_WINDOWS GUIO6(GUIO_NOASPECT, \
+#define GUIO_KQ6_CD_WINDOWS GUIO7(GUIO_NOASPECT, \
GAMEOPTION_WINDOWS_CURSORS, \
+ GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
GAMEOPTION_PREFER_DIGITAL_SFX, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_MIDI_MODE, \
Commit: 2ec9c46373fe2675287013f192e4156fd67ba003
https://github.com/scummvm/scummvm/commit/2ec9c46373fe2675287013f192e4156fd67ba003
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: replace workaround to clear hires portrait rects
As the GFX_SCREEN_MASK_DISPLAY flag is no longer
used, I have made a new workaround. Nowadays it
seems to be needed only for the mixed speech/text
mode (which didn't exist in the original game, but has
been added to ScummVM via script patches). The
problem is that the hires portrait frames will be drawn
at y = 5 in mixed speech/text mode. The window/port
rect starts at y=10, though. The engine is not meant to
update screen parts outside the port dimensions.
Another solution would be to improve the vertical
placement of the portraits with these patches, but
that seems to be more complicated.
Changed paths:
engines/sci/graphics/paint16.cpp
engines/sci/graphics/screen.cpp
engines/sci/graphics/screen.h
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 6b1a1164100..78f71001e40 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -274,11 +274,25 @@ void GfxPaint16::frameRect(const Common::Rect &rect) {
void GfxPaint16::bitsShow(const Common::Rect &rect) {
Common::Rect workerRect(rect.left, rect.top, rect.right, rect.bottom);
+ // WORKAROUND for vertically misplaced hires portraits in mixed speech/text mode in KQ6CD.
+ // See comment for the _activeHiresGfx flag in GfxScreen. Another solution would be to
+ // improve the script patches that implement the speech/text mode for better vertical
+ // placement of the portrait frames (inside the port rect).
+ bool needsOffsetRect = true;
+ if (_screen->hasActiveHiresView() && rect.left < 0 && rect.top < 0) {
+ _ports->offsetRect(workerRect);
+ _screen->toggleActiveHiresView(false);
+ needsOffsetRect = false;
+ }
+
workerRect.clip(_ports->_curPort->rect);
if (workerRect.isEmpty()) // nothing to show
return;
- _ports->offsetRect(workerRect);
+ // WORKAROUND, see comment above. Normally, the call to
+ // _ports->offsetRect(workerRect) would be unconditional here.
+ if (needsOffsetRect || rect.left >= 0 || rect.top >= 0)
+ _ports->offsetRect(workerRect);
// We adjust the left/right coordinates to even coordinates
workerRect.left &= 0xFFFE; // round down
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index c2d589cdf6e..6d1587b2044 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), _hiresGlyphBuffer(nullptr) {
+GfxScreen::GfxScreen(ResourceManager *resMan, Common::RenderMode renderMode) : _resMan(resMan), _hiresGlyphBuffer(nullptr), _activeHiresView(false) {
// Scale the screen, if needed
_upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
@@ -350,6 +350,7 @@ void GfxScreen::copyHiResRectToScreen(const byte *srcBuffer, int pitch, int x, i
_gfxDrv->setColorMap(colorMap);
_gfxDrv->copyRectToScreen(srcBuffer, 0, 0, pitch, x, y, w, h, nullptr, nullptr);
_gfxDrv->clearFlags(GfxDriver::kHiResMode);
+ toggleActiveHiresView(true);
}
void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) {
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 114a90e8207..1670716cea1 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -233,6 +233,18 @@ private:
*/
GfxScreenUpscaledMode _upscaledHires;
+ /**
+ * This flag is part of a hack to properly remove hires graphics from the screen
+ * when mixed speech/text mode is enabled. Sometimes the portrait is drawn
+ * out of bounds of the window/view port rect, so GfxPaint16::bitsShow() will
+ * not be able to refresh that part of the screen.
+ * The mixed speech/text is a mode that wasn't part of the original game, so
+ * there wasn't any need for Sierra to fix it. The mode has been added to ScummVM
+ * via complex script patches. Another solution would be to update these patches
+ * with better y-coordinates (inside the port rect)...
+ */
+ bool _activeHiresView;
+
/**
* This buffer is used to draw a single hires font glyph.
*/
@@ -463,6 +475,9 @@ public:
break;
}
}
+
+ bool hasActiveHiresView() const { return _activeHiresView; }
+ void toggleActiveHiresView(bool toggle) { _activeHiresView = toggle; }
};
} // End of namespace Sci
Commit: de32f308b43322d736462e92c88618c76d9bdf0d
https://github.com/scummvm/scummvm/commit/de32f308b43322d736462e92c88618c76d9bdf0d
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: remove GFX_SCREEN_MASK_DISPLAY
(and code sections guarded by it)
This was only used for the KQ6 Win hi-res code.
Changed paths:
engines/sci/console.cpp
engines/sci/graphics/screen.cpp
engines/sci/graphics/screen.h
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 9b14c572c2d..89d43ffc2a6 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -2280,8 +2280,6 @@ bool Console::cmdSavedBits(int argc, const char **argv) {
debugPrintf(" priority");
if (mask & GFX_SCREEN_MASK_CONTROL)
debugPrintf(" control");
- if (mask & GFX_SCREEN_MASK_DISPLAY)
- debugPrintf(" display");
debugPrintf("\n");
}
}
@@ -2361,8 +2359,6 @@ bool Console::cmdShowSavedBits(int argc, const char **argv) {
debugPrintf(" priority");
if (mask & GFX_SCREEN_MASK_CONTROL)
debugPrintf(" control");
- if (mask & GFX_SCREEN_MASK_DISPLAY)
- debugPrintf(" display");
debugPrintf("\n");
if (!_engine->_gfxPaint16 || !_engine->_gfxScreen)
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 6d1587b2044..7b5a4fd080c 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -618,13 +618,6 @@ int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
if (mask & GFX_SCREEN_MASK_CONTROL) {
byteCount += pixels; // _controlScreen
}
- if (mask & GFX_SCREEN_MASK_DISPLAY) {
- //if (!_upscaledHires)
- // error("bitsGetDataSize() called w/o being in upscaled hires mode");
- byteCount += pixels; // _displayScreen (coordinates actually are given to us for hires displayScreen)
- if (_paletteMapScreen)
- byteCount += pixels; // _paletteMapScreen
- }
return byteCount;
}
@@ -644,13 +637,6 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) {
if (mask & GFX_SCREEN_MASK_CONTROL) {
bitsSaveScreen(rect, _controlScreen, _width, memoryPtr);
}
- if (mask & GFX_SCREEN_MASK_DISPLAY) {
- //if (!_upscaledHires)
- // error("bitsSave() called w/o being in upscaled hires mode");
- bitsSaveScreen(rect, _displayScreen, _displayWidth, memoryPtr);
- if (_paletteMapScreen)
- bitsSaveScreen(rect, _paletteMapScreen, _displayWidth, memoryPtr);
- }
}
void GfxScreen::bitsSaveScreen(Common::Rect rect, const byte *screen, uint16 screenWidth, byte *&memoryPtr) {
@@ -708,19 +694,6 @@ void GfxScreen::bitsRestore(const byte *memoryPtr) {
if (mask & GFX_SCREEN_MASK_CONTROL) {
bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width);
}
- if (mask & GFX_SCREEN_MASK_DISPLAY) {
- //if (!_upscaledHires)
- // error("bitsRestore() called w/o being in upscaled hires mode");
- bitsRestoreScreen(rect, memoryPtr, _displayScreen, _displayWidth);
- if (_paletteMapScreen)
- bitsRestoreScreen(rect, memoryPtr, _paletteMapScreen, _displayWidth);
-
- // WORKAROUND - we are not sure what sierra is doing. If we don't do this here, portraits won't get fully removed
- // from screen. Some lowres showBits() call is used for that and it's not covering the whole area
- // We would need to find out inside the kq6 windows interpreter, but this here works already and seems not to have
- // any side-effects. The whole hires is hacked into the interpreter, so maybe this is even right.
- //copyDisplayRectToScreen(rect);
- }
}
void GfxScreen::bitsRestoreScreen(Common::Rect rect, const byte *&memoryPtr, byte *screen, uint16 screenWidth) {
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 1670716cea1..733de9ef83a 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -50,7 +50,6 @@ enum GfxScreenMasks {
GFX_SCREEN_MASK_VISUAL = 1,
GFX_SCREEN_MASK_PRIORITY = 2,
GFX_SCREEN_MASK_CONTROL = 4,
- GFX_SCREEN_MASK_DISPLAY = 8, // not official sierra sci, only used internally
GFX_SCREEN_MASK_ALL = GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY|GFX_SCREEN_MASK_CONTROL
};
Commit: 6be167aeafe6d6ab85fc5c2d9d01f58b3a8012d9
https://github.com/scummvm/scummvm/commit/6be167aeafe6d6ab85fc5c2d9d01f58b3a8012d9
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: cleanup
Changed paths:
engines/sci/engine/kgraphics.cpp
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index a79950e52e1..052d496cf8a 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -330,8 +330,10 @@ reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
- s->r_acc = g_sci->_gfxScreen->gfxDriver()->supportsHiResGraphics() ? g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask, true) : NULL_REG;
- return s->r_acc;
+ if (g_sci->_gfxScreen->gfxDriver()->supportsHiResGraphics())
+ return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask, true);
+ else
+ return NULL_REG;
}
reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
Commit: 523add9022e53cb85cac161696f389378443419a
https://github.com/scummvm/scummvm/commit/523add9022e53cb85cac161696f389378443419a
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: revert unneeded video code changes
Changed paths:
engines/sci/engine/kvideo.cpp
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index ba07723e258..f1d67e367b8 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -48,15 +48,7 @@
namespace Sci {
-struct MovieConfig {
- MovieConfig() : x(0), y(0), w(0), h(0), mode(0), pauseToken(), playFrom(0), playTo(0) {}
- Common::String filename;
- reg_t pauseToken;
- int16 x, y, w, h;
- uint16 mode, playFrom, playTo;
-};
-
-void playVideo(Video::VideoDecoder &videoDecoder, MovieConfig *conf) {
+void playVideo(Video::VideoDecoder &videoDecoder) {
videoDecoder.start();
Common::SpanOwner<SciSpan<byte> > scaleBuffer;
@@ -78,21 +70,6 @@ void playVideo(Video::VideoDecoder &videoDecoder, MovieConfig *conf) {
uint16 x = (screenWidth - width) / 2;
uint16 y = (screenHeight - height) / 2;
- if (conf) {
- // This is only for documentary purposes, since the original KQ6 Win interpreter does that.
- // But the videos will be centered on the screen anyway, without actually using these.
- x = conf->x;
- y = conf->y;
- if (conf->w > 0 && conf->h > 0) {
- width = conf->w;
- height = conf->h;
- }
- if (conf->playFrom)
- videoDecoder.seek(Audio::Timestamp(conf->playFrom, 1000));
- if (conf->playTo)
- videoDecoder.setEndTime(Audio::Timestamp(conf->playTo, 1000));
- }
-
bool skipVideo = false;
if (videoDecoder.hasDirtyPalette()) {
@@ -135,13 +112,6 @@ void playVideo(Video::VideoDecoder &videoDecoder, MovieConfig *conf) {
g_system->delayMillis(10);
}
}
-
-void playVideo(Video::VideoDecoder &videoDecoder) { // For the debugger
- playVideo(videoDecoder, nullptr);
-}
-
-MovieConfig *_movieConf = nullptr;
-
reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
reg_t retval = s->r_acc;
@@ -200,59 +170,34 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
} else {
// Windows AVI: Only used by KQ6 CD for the Sierra logo and intro cartoon.
// The first parameter is a subop. Some of the subops set the accumulator.
- // The interpreter implements subops 0-6. KQ6 only calls 0, 1, 2, 3, 6.
-
+ // The interpreter implements subops 0-6. KQ6 only calls 0, 1, 2, 6.
+ // Subop 0: Open movie file
+ // Subop 1: Setup movie playback rectangle
+ // Subop 2: Play movie
+ // Subop 6: Close movie file
+ // We just play it on opcode 0, since the config parameters that are passed
+ // to opcodes 1 and 2 aren't properly used anyway (the video will be centered,
+ // regardless of any x, y, width and height settings).
+ // Using any other opcode than 0 would also require unblocking the engine
+ // after the movie playback like this (with <pauseToken> being the second
+ // argument passed to opcode 2):
+ // invokeSelector(s, <pauseToken>, g_sci->getKernel()->findSelector("cue"), argc, argv);
switch (argv[0].toUint16()) {
case 0: {
- _movieConf = new MovieConfig();
- _movieConf->filename = s->_segMan->getString(argv[1]);
- retval = NULL_REG;
- break;
- }
- case 1:
- if (_movieConf) {
- int16 *dest[4] = { &_movieConf->x, &_movieConf->y, &_movieConf->w, &_movieConf->h };
- for (int i = 1; i < argc; ++i)
- *dest[i - 1] = argv[i].toSint16();
- }
- retval = NULL_REG;
- break;
- case 2:
- if (_movieConf) {
- switch (argc) {
- case 5:
- _movieConf->playTo = argv[4].toUint16();
- // fallthrough
- case 4:
- _movieConf->playFrom = argv[3].toUint16();
- // fallthrough
- case 3:
- _movieConf->pauseToken = argv[2];
- // fallthrough
- case 2:
- _movieConf->mode = argv[1].toUint16();
- break;
- default:
- break;
- }
- }
-
+ Common::String filename = s->_segMan->getString(argv[1]);
// For KQ6, this changes the vertical 200/440 upscaling to 200/400, since this is the expected behavior. Also,
// the calculation of the scaled x/y coordinates works slightly differently compared to the normal gfx rendering.
g_sci->_gfxScreen->gfxDriver()->setFlags(GfxDriver::kMovieMode);
-
videoDecoder.reset(new Video::AVIDecoder());
- if (!videoDecoder->loadFile(_movieConf->filename.c_str())) {
- warning("Failed to open movie file %s", _movieConf->filename.c_str());
+ if (!videoDecoder->loadFile(filename.c_str())) {
+ warning("Failed to open movie file %s", filename.c_str());
videoDecoder.reset();
}
-
syncLastFrame = false;
- retval = NULL_REG;
-
+ retval = TRUE_REG;
break;
+ }
default:
- // This will trigger on case 6 which is the close/deinit function. We don't need that.
debug(kDebugLevelVideo, "Unhandled kShowMovie subop %d", argv[0].toUint16());
}
}
@@ -261,7 +206,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
if (videoDecoder->getPixelFormat().bytesPerPixel > 1)
syncLastFrame = false;
- playVideo(*videoDecoder, _movieConf);
+ playVideo(*videoDecoder);
// HACK: Switch back to 8bpp if we played a true color video.
// We also won't be copying the screen to the SCI screen...
@@ -273,17 +218,6 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
}
g_sci->_gfxScreen->gfxDriver()->clearFlags(GfxDriver::kMovieMode);
-
- if (_movieConf) {
- // Unfreeze the engine. KQ6 Windows does not run the video modally, since it happens via OS functions.
- // So the engine is put into a waiting state until signalled to continue. The orignal interpreter does
- // that from the window proc after receiving the mci finishing message. We can/should do it here, since we
- // do play the video modally.
- if (!_movieConf->pauseToken.isNull())
- invokeSelector(s, _movieConf->pauseToken, g_sci->getKernel()->findSelector("cue"), argc, argv);
- delete _movieConf;
- _movieConf = nullptr;
- }
}
if (reshowCursor)
Commit: c0492ece444558b383812aec6bf3154fffbf11b4
https://github.com/scummvm/scummvm/commit/c0492ece444558b383812aec6bf3154fffbf11b4
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: work around original script/interpreter bug
The original interpreter expects the same arguments for
kGraph 15 as for kGraph 7. The scripts never pass the last
arguments, though. So the engine would use undefined
values here (which apparently had no visual consequence).
Changed paths:
engines/sci/engine/kernel_tables.h
engines/sci/engine/kgraphics.cpp
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 63286383315..32d131bf3b2 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -263,7 +263,7 @@ static const SciKernelMapSubEntry kGraph_subops[] = {
{ SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
{ SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
{ SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
- { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii(i)", NULL }, // kq6 hires
SCI_SUBOPENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 052d496cf8a..7432fd311be 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -329,7 +329,13 @@ reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
- uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
+ // There seems to be a mismatch between the function call the original interpreter expects and the call
+ // that the scripts actually make. The scripts never actually push argv[4] for kGraph sub op 15, but
+ // the interpreter will still pass on the (thus undefined) argument to kernelGraphSaveBox(). Apparently,
+ // the argument is not needed. From a technical point of view this would make sense, since the hires
+ // graphics never overwrite the engine bitmap buffers, so it should not be necessary to recover any pixel
+ // data. We pretend to check argc here, but we know it will always be 4...
+ uint16 screenMask = argc > 4 ? (argv[4].toUint16() & GFX_SCREEN_MASK_ALL) : 0;
if (g_sci->_gfxScreen->gfxDriver()->supportsHiResGraphics())
return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask, true);
else
Commit: bf48bc41b3f3154b9c0c490ed8ec58bc286098b9
https://github.com/scummvm/scummvm/commit/bf48bc41b3f3154b9c0c490ed8ec58bc286098b9
Author: athrxx (athrxx at scummvm.org)
Date: 2024-10-09T08:35:11+03:00
Commit Message:
SCI: (KQ6 Gfx driver) - fix monochrome mouse cursors in non-rgb rendering mode
Changed paths:
engines/sci/graphics/gfxdrivers.cpp
diff --git a/engines/sci/graphics/gfxdrivers.cpp b/engines/sci/graphics/gfxdrivers.cpp
index 61efe535bd3..91b2febf6fb 100644
--- a/engines/sci/graphics/gfxdrivers.cpp
+++ b/engines/sci/graphics/gfxdrivers.cpp
@@ -1600,6 +1600,11 @@ void KQ6WinGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hots
UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
return;
}
+ adjustCursorBuffer(w << 1, h << 1);
+
+ if (_pixelSize == 1)
+ copyCurrentPalette(_currentPalette, 0, _numColors);
+
byte col1 = findColorInPalette(0x00000000, _currentPalette, _numColors);
byte col2 = findColorInPalette(0x00FFFFFF, _currentPalette, _numColors);
renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor);
More information about the Scummvm-git-logs
mailing list